@hyodotdev/openiap 0.1.0-alpha.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,12 +16,12 @@ npx @hyodotdev/openiap init ./my-product --role experience
16
16
  npx @hyodotdev/openiap doctor ./my-app --json
17
17
  ```
18
18
 
19
- | Role | Connect |
20
- | --- | --- |
21
- | `app` | Purchases to customer access |
22
- | `experience` | Paywalls and experiments to the app's purchase flow |
23
- | `commerce` | Verification and access through the Commerce Protocol |
24
- | `data` | Normalized events to analytics and automation |
19
+ | Role | Connect |
20
+ | ------------ | ----------------------------------------------------- |
21
+ | `app` | Purchases to customer access |
22
+ | `experience` | Paywalls and experiments to the app's purchase flow |
23
+ | `commerce` | Verification and access through the Commerce Protocol |
24
+ | `data` | Normalized events to analytics and automation |
25
25
 
26
26
  The brief points your assistant to the matching implementation guide. Fill in
27
27
  the customer outcome, choose any missing product decisions, and review the
@@ -62,20 +62,22 @@ Most of these produce no error message that says what is actually wrong.
62
62
  | `android-horizon-app-id-missing` | warning | Horizon is selected but no manifest declares an app id. |
63
63
  | `iapkit-secret-key-in-client` | error | A secret key is on a name that reaches the app bundle. |
64
64
  | `iapkit-secret-key-in-env` | warning | A secret key is in an env file on a name nothing here proves is inlined. |
65
+ | `iapkit-secret-key-in-config` | warning | Executable app configuration contains a secret, but its presence in the app bundle is unproven. |
65
66
  | `iapkit-env-missing-expo-prefix` | warning | Expo inlines only `EXPO_PUBLIC_` names, so the bare name reads as undefined. |
66
67
  | `iapkit-env-unexpected-expo-prefix` | warning | An `EXPO_PUBLIC_` name is set where nothing inlines that prefix. |
67
68
  | `iapkit-base-url-has-path` | both | The base URL is not a bare origin; every SDK rejects a path, userinfo, a query or a fragment. |
68
69
  | `iapkit-base-url-invalid` | both | The base URL is not a URL. |
69
70
  | `iapkit-base-url-scheme` | both | The base URL is not http or https. |
70
71
  | `ios-scene-delegate-missing` | both | The Info.plist names a scene delegate the target lacks; the app opens to a black screen. Error when the plist names the app's own module or no class at all, warning when a linked framework could supply it. |
71
- | `project-file-unreadable` | error | A path could not be read, so nothing in it was checked. |
72
+ | `project-file-unreadable` | error | A path could not be read, so nothing in it was checked. |
72
73
  | `project-manifest-unreadable` | error | package.json exists but will not parse, so framework detection read nothing. |
73
74
  | `project-not-a-directory` | error | The path given is not a readable directory. |
74
75
 
75
76
  ## What it does not find
76
77
 
77
- Dynamic app configuration is never executed. Reading an unprefixed environment
78
- variable there does not prove it reaches the app; secret values stay warnings.
78
+ Dynamic app configuration is never executed. Secret literals in it stay warnings
79
+ unless the file is a declared bundled asset. Reading an unprefixed environment
80
+ variable during configuration also does not prove its value reaches the app.
79
81
 
80
82
  A checkout cannot answer for a device or a store account. The command prints
81
83
  these as unchecked rather than guessing:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyodotdev/openiap",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0",
4
4
  "description": "Start an OpenIAP integration with AI and check local purchase configuration",
5
5
  "keywords": [
6
6
  "openiap",
package/src/checks.mjs CHANGED
@@ -233,7 +233,7 @@ function reachesBundle(root, framework, name, file) {
233
233
  return pubspecAssets(root).includes(file);
234
234
  }
235
235
 
236
- /** A secret key in a client file ships to every device that installs the app. */
236
+ /** Distinguish bundled secrets from values used only during configuration. */
237
237
  export function secretKeyChecks(root, framework) {
238
238
  const findings = [];
239
239
  for (const file of clientFiles(root)) {
@@ -300,10 +300,23 @@ export function secretKeyChecks(root, framework) {
300
300
  continue;
301
301
  }
302
302
 
303
- // A key on a comment line is not shipped; anywhere else in a bundled file
304
- // it is, whether or not it sits inside a string.
305
303
  const found = codeLines(text).find((one) => SECRET_KEY.test(one.line));
306
304
  if (!found) continue;
305
+ const dynamicConfig =
306
+ BUNDLED_FILES.includes(file) && !file.endsWith(".json");
307
+ if (dynamicConfig && !pubspecAssets(root).includes(file)) {
308
+ findings.push(
309
+ finding(
310
+ "iapkit-secret-key-in-config",
311
+ "warning",
312
+ file,
313
+ "An IAPKit secret key appears in executable app configuration. Nothing here proves it reaches the bundle.",
314
+ "Keep build-time secrets out of exported app configuration.",
315
+ { line: found.number },
316
+ ),
317
+ );
318
+ continue;
319
+ }
307
320
  findings.push(
308
321
  finding(
309
322
  "iapkit-secret-key-in-client",
package/src/findings.mjs CHANGED
@@ -10,6 +10,7 @@ export const FINDING_IDS = Object.freeze([
10
10
  "android-horizon-app-id-missing",
11
11
  "iapkit-secret-key-in-client",
12
12
  "iapkit-secret-key-in-env",
13
+ "iapkit-secret-key-in-config",
13
14
  "iapkit-env-missing-expo-prefix",
14
15
  "iapkit-env-unexpected-expo-prefix",
15
16
  "iapkit-base-url-invalid",
package/src/project.mjs CHANGED
@@ -338,10 +338,8 @@ const ENV_TEMPLATE = /\.(example|sample|template)$/;
338
338
  /** What Flutter's `dotenv` is pointed at: `env`, `env.prod`, `.env.ci`. */
339
339
  const FLUTTER_ENV = /(^|\.)env(\..+)?$/;
340
340
 
341
- /** Files whose whole contents are compiled into the app bundle. */
341
+ /** App configuration files inspected without evaluating executable exports. */
342
342
  export const BUNDLED_FILES = [
343
- // Expo resolves these in this order, so a key in the first one is the one
344
- // that ships even when a later file also exists.
345
343
  "app.config.ts",
346
344
  "app.config.mts",
347
345
  "app.config.cts",
@@ -378,7 +376,8 @@ export function pubspecAssets(root) {
378
376
  .map((entry) => path.join(asset, entry))
379
377
  .filter((file) => !isDirectory(root, file))
380
378
  : [asset],
381
- );
379
+ )
380
+ .map((asset) => path.normalize(asset));
382
381
  }
383
382
 
384
383
  /**