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:
parent
86cd79dd45
commit
1366e7e231
34 changed files with 881 additions and 71 deletions
|
|
@ -9,6 +9,9 @@ fails without the fix.
|
|||
|
||||
| # | date | lesson | guard |
|
||||
|---|---|---|---|
|
||||
| 37 | 2026-10 | A `scan.ignore` entry whose `match` text was in the evidence (a code line) instead of the finding title accepted nothing and said nothing. `init --profile game` wrote example-looking values into `.storebound/credentials.json` (`YOUR_KEY_ID`, `/absolute/path/to/upload.p12`, `sk_your_revenuecat_v2_secret_key`), which commands read as real credentials. | `scan.ignore[].evidence` matches an evidence line ("file:line text"), `match` stays the title; `scan` (report notes) and `check` (warning) name every entry that accepts no finding (`ignoreMatches` / `applyIgnore` / `unusedIgnores` in `cli/lib/scan/rules.ts`). `templates/credentials.example.json` holds empty strings only; `isPlaceholder` (`cli/lib/credentials.ts`) makes "your-…", `<…>`, "xxx", "/absolute/path/to/…", "example" and the zero Apple Ads client id missing in every credential read; `check` warns, `doctor` shows set / missing / placeholder per key. `tests/game-report.test.ts`. |
|
||||
| 36 | 2026-10 | Privacy answers derived from the wrong source: `privacy-manifest --propose` wrote an EMPTY NSPrivacyCollectedDataTypes while `appPrivacy.types` listed 5 collected types; `console plan apple-app-privacy` added "Other Usage Data" from dataSafety (PSL_OTHER_APP_ACTIVITY) although `appPrivacy.types` was given; `console plan play-advertising-id` answered "uses advertising ID" Yes because dataSafety declared PSL_DEVICE_ID (a random install id, not the advertising ID). | `--propose` builds NSPrivacyCollectedDataTypes from `appPrivacy.types` (types, linked, tracking, purposes as Apple's manifest keys; NSPrivacyTracking still only with app-level domains, lesson 29) and `check` warns on an empty list against collected types. `appPrivacy.types` is authoritative in the App Privacy sheet (unlisted = not collected, certain); without it, tracking false and no ad / attribution SDK make Advertising Data, Customer Support, Environment Scanning, Hands and Head "not collected" unless a project signal suggests them (`cli/lib/project-signals.ts`). Advertising ID comes from ad / attribution SDKs, the AD_ID permission (source or merged manifest, or its `tools:node="remove"`), ads.appAdsTxt or `console.appContent.advertisingId`; none: No (certain), a conflict: ask (`advertisingIdUse`). `tests/game-report.test.ts`. |
|
||||
| 35 | 2026-10 | `scan` on a Capacitor game: rule `review-prompt` (3.2.2(x), reward for rating) fired on `const rate = F.genRate(lv, bonus.gen(r));` (a bare "rate" next to "bonus"); every account finding cited `import './buildings/register';` as sign-up evidence; and 5.1.1(v) cleared as soon as `accounts.inApp` named a screen the code did not have. | The incentive signal needs a rate-us / leave-a-review phrase next to a reward, a reward "for rating / reviewing", or a reward granted within a few lines of a system review prompt call (`rewardNearReviewApi`). Sign-in / sign-up / deletion signals skip import, require and export-from lines (`codeLine`), and auth endpoints must be an absolute path, URL or `${base}/...`, never a module path; config-only accounts say "from config (accounts.enabled)". A config claim of in-app deletion without code evidence stays a finding (medium, "claimed by config, not found in code"). `tests/game-report.test.ts` with `tests/fixtures/scan/capacitor-game`. |
|
||||
| 34 | 2026-10 | The macOS desktop release ran the universal app's x86_64 slice on the Apple silicon build Mac (`arch -x86_64 storebound-desktop --self-test`, and the x64 sidecar's `version`) to cover both CPU types. That starts Rosetta, and macOS showed the owner "App Update Required: this version of storebound includes a component that will not open in macOS 28". | `tools/desktop-release.ts` never runs an x86_64 slice: `sliceProblems` checks every Mach-O in the bundle with `lipo -archs` (each must carry arm64 and x86_64), the self-test and the sidecar `version` run natively (arm64) only; `tests/desktop-release.test.ts` (slice check, and no `arch -x86_64` / Rosetta launch in the release tools); docs/desktop-release.md. |
|
||||
| 33 | 2026-10 | Play Console: pressing "Submit N changes for review" in Publishing overview while a review was running opened a dialog "Do you want to restart your review?" with "Restart review". Pressing it restarts the running review with the new changes; the plan did not know the dialog. | `play-production-release` plan: the dialog is a known step and UI fact, "Restart review" is in neverPress (the owner's button), Cancel is allowed (closes it, nothing changes); play-first-release and release-management skills explain it. `tests/console-play-plans.test.ts`. |
|
||||
| 32 | 2026-10 | `play build` passed `-PversionCode` / `-PversionName` and `android.injected.version.*`, but a Capacitor `app/build.gradle` read versionCode from an env var, so the AAB stayed at versionCode 1 (the post-build check caught it). Checked with AGP 8.13: `-Pandroid.injected.version.code=42` on `versionCode 1` gives an AAB with versionCode 1, so the injected property does not override `defaultConfig` for `bundleRelease`. The Groovy line `versionCode (project.findProperty('versionCode') ?: 1) as Integer` fails ("Value is null": it parses as `versionCode(...) as Integer`); the cast belongs inside the parentheses. | `versionSourceIssues` (cli/lib/gradle.ts) reads `defaultConfig` of the app module's build.gradle(.kts): a literal, an env var or anything but the project property is a warning in `play build` (before Gradle, also in the dry run and `--json` as `versionWarnings`) and in `check`, with the exact line (Groovy `versionCode((project.findProperty('versionCode') ?: <current>) as Integer)`, `versionName project.findProperty('versionName') ?: '<current>'`; Kotlin `versionCode = (project.findProperty("versionCode") as String?)?.toInt() ?: <current>`, `versionName = project.findProperty("versionName") as String? ?: "<current>"`), both lines built with AGP 8.13; the post-build versionCode check is now a hard stop (the AAB is not copied). `tests/build-sign.test.ts`, `tests/web-assets.test.ts`. |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue