scan, privacy answers and credentials from a second game's first release (lessons 35-37): review-prompt needs rating context, not a bare "rate"; account signals skip import lines and module paths, config-only accounts say "from config"; accounts.inApp without deletion code stays a medium 5.1.1(v) finding; privacy-manifest --propose builds NSPrivacyCollectedDataTypes from appPrivacy.types and check warns on an empty list; appPrivacy.types is authoritative in the App Privacy sheet (unlisted = not collected), ad / support / AR types default to not collected without tracking or ad SDKs; Play advertising ID from SDKs, the AD_ID permission, app-ads.txt or the flag, never from a Device ID alone; scan.ignore[].evidence and warnings for entries that match nothing; empty credentials template, placeholder values count as missing (check, doctor)

This commit is contained in:
CRTkafa 2026-10-13 05:39:09 +08:00
parent 86cd79dd45
commit 1366e7e231
34 changed files with 881 additions and 71 deletions

View file

@ -160,7 +160,10 @@ export function appPrivacyMismatches(types: Partial<Record<AppleDataTypeId, Appl
if (a?.collected === false) out.push({ level: 'error', msg: `${m.path} declares ${k} (${typeName(id)}) as collected, but appPrivacy.types.${id}.collected is false` });
else if (!a || a.collected === undefined) out.push({ level: 'warn', msg: `${m.path} declares ${k} (${typeName(id)}) as collected, but appPrivacy.types has no answer for ${id}` });
}
for (const [id, a] of entries) if (a.collected === true && !inManifest.has(id))
const missing = entries.filter(([id, a]) => a.collected === true && !inManifest.has(id)).map(([id]) => id);
if (missing.length && !m.collectedTypes.length)
out.push({ level: 'warn', msg: `${m.path} has an empty NSPrivacyCollectedDataTypes, but appPrivacy.types marks ${missing.join(', ')} as collected: \`storebound privacy-manifest --ios <ios dir> --propose\` builds the list from appPrivacy.types (types, linked, tracking, purposes)` });
else for (const id of missing)
out.push({ level: 'warn', msg: `appPrivacy.types.${id} is collected, but ${m.path} has no ${APPLE_PRIVACY_TYPES[id][2]} in NSPrivacyCollectedDataTypes (fine if only an SDK collects it and declares it in its own manifest)` });
return out;
}