play data-safety: PSL_SUPPORT_DATA_DELETION_BY_USER as Play's single choice (partialDeletion true | false | 'auto90'), drop the removed PSL_DATA_COLLECTION_USER_REQUEST_DELETE (deletionRequest deprecated, check notes it); ship Google's empty form (cli/data/play-data-safety-form.csv, exported 2026-10-12), take every Answer requirement from it and report pairs not in it (formPairIssues) in check, declarations and play data-safety. Pitfall 21

This commit is contained in:
CRTkafa 2026-10-12 09:33:56 +08:00
parent dfc28c15f8
commit b5b8f2da9d
35 changed files with 1824 additions and 859 deletions

View file

@ -124,8 +124,9 @@ export function proposeDeclarations(p: ProjectFacts, c: ShipConfig | null, confi
// top-level Data safety answers
const anyData = Object.keys(out).length > 0;
if (anyData) items.push({ area: 'dataSafety', what: 'collectsData', value: 'true', status: 'detected', evidence: diff.flatMap(d => d.evidence).slice(0, 1) });
for (const k of ['encryptedInTransit', 'deletionRequest'] as const)
items.push({ area: 'dataSafety', what: k, value: c?.dataSafety ? String(c.dataSafety[k]) : '?', status: 'ask', evidence: [], note: k === 'encryptedInTransit' ? 'all traffic over HTTPS / TLS? (ATS and the SDKs\' own endpoints)' : 'can users ask for their data to be deleted?' });
items.push({ area: 'dataSafety', what: 'encryptedInTransit', value: c?.dataSafety ? String(c.dataSafety.encryptedInTransit) : '?', status: 'ask', evidence: [], note: 'all traffic over HTTPS / TLS? (ATS and the SDKs\' own endpoints)' });
if (accounts) items.push({ area: 'dataSafety', what: 'partialDeletion', value: c?.dataSafety?.partialDeletion !== undefined ? String(c.dataSafety.partialDeletion) : '?', status: 'ask', evidence: [],
note: "can people request that some or all of their data is deleted without deleting the account? true | false | 'auto90' (deleted automatically within 90 days)" });
const acmFromCfg = accountCreationOf(signUpCfg);
const acmNone = (c?.dataSafety?.accountCreation ?? ['PSL_ACM_NONE']).includes('PSL_ACM_NONE');
let accountCreation: string[] | null = null;

View file

@ -199,7 +199,7 @@ export function scanRules(inp: ScanInput): ScanFinding[] {
if (play && !c?.accounts?.deletionUrl)
add({ rule: 'play-deletion-url', guideline: 'Play: Account deletion', store: 'play', severity: c?.accounts?.enabled || signUpCode.length ? 'blocker' : 'high',
title: 'Accounts can be created but there is no web page to request account deletion (Data safety asks for it)', evidence: creates.slice(0, 4),
fix: 'Publish a page where people request account and data deletion without the app, set accounts.deletionUrl (and dataSafety.deletionRequest: true); Play also wants the in-app path.', skill: 'account-deletion', ref: `${PLAY}13327111` });
fix: 'Publish a page where people request account and data deletion without the app, set accounts.deletionUrl (and answer dataSafety.partialDeletion); Play also wants the in-app path.', skill: 'account-deletion', ref: `${PLAY}13327111` });
}
if (apple && thirdParty.length && !siwa.length)
add({ rule: 'login-services', guideline: '4.8', store: 'apple', severity: 'high', title: 'Third-party login (Google / Facebook / ...) without Sign in with Apple or another equivalent login option', evidence: thirdParty.slice(0, 4),