@brydio/manifest 0.1.0-alpha.5 → 0.1.0-alpha.50

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/src/secrets.js CHANGED
@@ -35,21 +35,21 @@ const isRealValue = (value) => {
35
35
  return false;
36
36
  return true;
37
37
  };
38
- function walk(node, at, found) {
38
+ function walk(node, at, found, publicValue = () => false) {
39
39
  if (Array.isArray(node)) {
40
- node.forEach((one, index) => walk(one, `${at}[${index}]`, found));
40
+ node.forEach((one, index) => walk(one, `${at}[${index}]`, found, publicValue));
41
41
  return;
42
42
  }
43
43
  if (!node || typeof node !== 'object')
44
44
  return;
45
45
  for (const [key, value] of Object.entries(node)) {
46
46
  const path = at ? `${at}.${key}` : key;
47
- if (VALUE_KEYS.has(key) && isRealValue(value)) {
47
+ if (VALUE_KEYS.has(key) && isRealValue(value) && !publicValue(path)) {
48
48
  // Never quoted back: a report that repeated the secret would copy it somewhere else.
49
49
  found.push({ code: 'secret_in_bundle', message: SECRET_MESSAGE, path });
50
50
  continue;
51
51
  }
52
- walk(value, path, found);
52
+ walk(value, path, found, publicValue);
53
53
  }
54
54
  }
55
55
  /**
@@ -59,9 +59,20 @@ function walk(node, at, found) {
59
59
  */
60
60
  export function secretsInJson(document, at) {
61
61
  const found = [];
62
- walk(document, at, found);
62
+ walk(document, at, found, path => DECLARATIONS.some(declared => declared.test(path)));
63
63
  return found;
64
64
  }
65
+ /**
66
+ * Names in an app.json that read like credentials but are declarations: a
67
+ * placement's identity, a collection's field definition (an open-schema
68
+ * companion must declare `key`), and a tool's input field. Their values are
69
+ * types and names, never secrets.
70
+ */
71
+ const DECLARATIONS = [
72
+ /(?:^|\.)placements\[\d+\]\.key$/,
73
+ /(?:^|\.)data\.[^.[\]]+\.schema\.[^.[\]]+$/,
74
+ /(?:^|\.)tools\.custom\[\d+\]\.input\.[^.[\]]+$/,
75
+ ];
65
76
  /** Every secret declared in a bundle's files, as the server finds them. */
66
77
  export function findSecrets(files, root = '') {
67
78
  const found = [];
@@ -79,3 +90,25 @@ export function findSecrets(files, root = '') {
79
90
  }
80
91
  return found;
81
92
  }
93
+ /**
94
+ * The server's sentence for `developer_key_in_bundle` (ADR-A22), word for
95
+ * word: `DEVELOPER_KEY_IN_BUNDLE` in Brydio's `app-publish.service.ts`.
96
+ */
97
+ export const DEVELOPER_KEY_MESSAGE = 'That package contains a Brydio developer API key. A key belongs on your own server, never in an app: revoke it in Settings › Developer, and call Brydio’s model from a handler with the model grant instead.';
98
+ /** A developer API key's exact shape, anywhere in a text. */
99
+ const DEVELOPER_KEY = /bry_live_[0-9a-f]{12}_[A-Za-z0-9_-]{43}/;
100
+ /** Whether a text holds a Brydio developer API key. */
101
+ export const holdsDeveloperKey = (text) => DEVELOPER_KEY.test(text);
102
+ /**
103
+ * The first file holding a developer API key, by path, as Brydio's publish
104
+ * route finds it (`developerKeyIn`). Unlike the declaration scan, every file
105
+ * is read, scripts included: the key's shape is exact enough that a match is
106
+ * a key, not a guess, and no part of an app may hold one (ADR-A24).
107
+ */
108
+ export function developerKeyIn(files) {
109
+ for (const [path, bytes] of files) {
110
+ if (holdsDeveloperKey(new TextDecoder('latin1').decode(bytes)))
111
+ return path;
112
+ }
113
+ return null;
114
+ }
package/src/validate.d.ts CHANGED
@@ -22,6 +22,12 @@ export interface ManifestValidation {
22
22
  manifest?: AppManifestWithData;
23
23
  problems: ManifestProblem[];
24
24
  }
25
- export declare function validateManifest(source: unknown): ManifestValidation;
25
+ export declare function validateManifest(source: unknown,
26
+ /** `retired`: a manifest being written now, which may not use a retired kind such as `project-tab`. */
27
+ options?: {
28
+ retired?: boolean;
29
+ }): ManifestValidation;
26
30
  /** Parses the text of `.brydio/app.json` and validates it. */
27
- export declare function validateManifestText(text: string): ManifestValidation;
31
+ export declare function validateManifestText(text: string, options?: {
32
+ retired?: boolean;
33
+ }): ManifestValidation;
package/src/validate.js CHANGED
@@ -1,6 +1,8 @@
1
- import { appManifestSchema } from "./schema.js";
2
- export function validateManifest(source) {
3
- const parsed = appManifestSchema.safeParse(source);
1
+ import { appManifestSchema, newAppManifestSchema } from "./schema.js";
2
+ export function validateManifest(source,
3
+ /** `retired`: a manifest being written now, which may not use a retired kind such as `project-tab`. */
4
+ options = {}) {
5
+ const parsed = (options.retired ? newAppManifestSchema : appManifestSchema).safeParse(source);
4
6
  if (parsed.success)
5
7
  return { ok: true, manifest: parsed.data, problems: [] };
6
8
  return {
@@ -14,7 +16,7 @@ export function validateManifest(source) {
14
16
  };
15
17
  }
16
18
  /** Parses the text of `.brydio/app.json` and validates it. */
17
- export function validateManifestText(text) {
19
+ export function validateManifestText(text, options = {}) {
18
20
  let json;
19
21
  try {
20
22
  json = JSON.parse(text);
@@ -25,5 +27,5 @@ export function validateManifestText(text) {
25
27
  problems: [{ code: 'manifest_not_json', message: `The manifest is not valid JSON: ${error instanceof Error ? error.message : String(error)}` }],
26
28
  };
27
29
  }
28
- return validateManifest(json);
30
+ return validateManifest(json, options);
29
31
  }