@loucompanion/forge-bridge 0.1.3-dev.fbe15023cdfe → 0.1.4-dev.1ff37038366e

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.
Files changed (162) hide show
  1. package/README.md +88 -2
  2. package/dist/bridge/bridge-chat.types.d.ts +111 -0
  3. package/dist/bridge/bridge-chat.types.js +401 -0
  4. package/dist/bridge/bridge.base.d.ts +4 -1
  5. package/dist/bridge/bridge.service.d.ts +72 -3
  6. package/dist/bridge/bridge.service.js +1563 -152
  7. package/dist/bridge/bridge.types.d.ts +46 -1
  8. package/dist/bridge/dispatch-result-outbox.d.ts +25 -0
  9. package/dist/bridge/dispatch-result-outbox.js +62 -0
  10. package/dist/bridge/dispatch-result-worker.d.ts +2 -0
  11. package/dist/bridge/dispatch-result-worker.js +122 -0
  12. package/dist/bridge/internals.d.ts +2 -0
  13. package/dist/bridge/internals.js +1 -0
  14. package/dist/bridge/readiness-result-outbox.d.ts +21 -0
  15. package/dist/bridge/readiness-result-outbox.js +54 -0
  16. package/dist/bridge/readiness-result-worker.d.ts +2 -0
  17. package/dist/bridge/readiness-result-worker.js +121 -0
  18. package/dist/bridge/runner-host-capabilities.d.ts +15 -0
  19. package/dist/bridge/runner-host-capabilities.js +93 -0
  20. package/dist/cleanup/cleanup.service.d.ts +1 -0
  21. package/dist/cleanup/cleanup.service.js +70 -9
  22. package/dist/cli/cli.help.js +2 -2
  23. package/dist/cli/cli.service.d.ts +14 -6
  24. package/dist/cli/cli.service.js +75 -16
  25. package/dist/cli/local-config.d.ts +1 -0
  26. package/dist/cli/local-config.js +4 -0
  27. package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
  28. package/dist/runtime/aspnet-runtime-prebuild.js +341 -0
  29. package/dist/runtime/attempt-database-broker.d.ts +89 -0
  30. package/dist/runtime/attempt-database-broker.js +390 -0
  31. package/dist/runtime/attempt-node-runtime-launcher.d.ts +2 -1
  32. package/dist/runtime/attempt-node-runtime-launcher.js +57 -18
  33. package/dist/runtime/attempt-path-operation.d.ts +21 -0
  34. package/dist/runtime/attempt-path-operation.js +94 -0
  35. package/dist/runtime/attempt-private-directory.d.ts +6 -0
  36. package/dist/runtime/attempt-private-directory.js +54 -0
  37. package/dist/runtime/attempt-process-supervisor.d.ts +11 -0
  38. package/dist/runtime/attempt-process-supervisor.js +67 -20
  39. package/dist/runtime/canonical-source-file.d.ts +3 -0
  40. package/dist/runtime/canonical-source-file.js +54 -0
  41. package/dist/runtime/docker-engine-probe.d.ts +90 -0
  42. package/dist/runtime/docker-engine-probe.js +333 -0
  43. package/dist/runtime/generated-output-reset.d.ts +10 -0
  44. package/dist/runtime/generated-output-reset.js +62 -8
  45. package/dist/runtime/host-database-capability-boundary.d.ts +16 -0
  46. package/dist/runtime/host-database-capability-boundary.js +62 -0
  47. package/dist/runtime/host-diagnostics.d.ts +11 -0
  48. package/dist/runtime/host-diagnostics.js +191 -0
  49. package/dist/runtime/host-execution-root.d.ts +4 -0
  50. package/dist/runtime/host-execution-root.js +39 -0
  51. package/dist/runtime/host-listener-inspector.d.ts +8 -1
  52. package/dist/runtime/host-listener-inspector.js +121 -60
  53. package/dist/runtime/host-runtime-contract.d.ts +12 -1
  54. package/dist/runtime/host-runtime-contract.js +168 -55
  55. package/dist/runtime/host-runtime-lease.d.ts +92 -8
  56. package/dist/runtime/host-runtime-lease.js +1141 -114
  57. package/dist/runtime/host-runtime-promotion.d.ts +12 -3
  58. package/dist/runtime/host-runtime-promotion.js +59 -12
  59. package/dist/runtime/memory-capacity.d.ts +70 -0
  60. package/dist/runtime/memory-capacity.js +123 -0
  61. package/dist/runtime/native-test-postgres-fallback.d.ts +25 -0
  62. package/dist/runtime/native-test-postgres-fallback.js +91 -0
  63. package/dist/runtime/node-host-runtime-adapter.d.ts +3 -0
  64. package/dist/runtime/node-host-runtime-adapter.js +28 -11
  65. package/dist/runtime/pinned-directory-process.d.ts +9 -0
  66. package/dist/runtime/pinned-directory-process.js +61 -0
  67. package/dist/runtime/pinned-source-directory.d.ts +17 -0
  68. package/dist/runtime/pinned-source-directory.js +94 -0
  69. package/dist/runtime/playwright-host-runtime-adapter.d.ts +1 -0
  70. package/dist/runtime/playwright-host-runtime-adapter.js +53 -5
  71. package/dist/runtime/prepared-runtime-identity.d.ts +6 -0
  72. package/dist/runtime/prepared-runtime-identity.js +218 -0
  73. package/dist/runtime/prepared-runtime-readiness.d.ts +30 -0
  74. package/dist/runtime/prepared-runtime-readiness.js +285 -0
  75. package/dist/runtime/runtime-isolation-lease.js +4 -1
  76. package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
  77. package/dist/runtime/workspace-resource-estimator.js +174 -0
  78. package/dist/sandbox/preflight.d.ts +31 -1
  79. package/dist/sandbox/preflight.js +237 -51
  80. package/dist/sandbox/repository-lock.d.ts +2 -1
  81. package/dist/sandbox/repository-lock.js +17 -8
  82. package/dist/sandbox/runtime.d.ts +6 -1
  83. package/dist/sandbox/runtime.js +132 -103
  84. package/dist/sandbox/sandbox.types.d.ts +7 -0
  85. package/dist/sandbox/workspace.service.js +2 -0
  86. package/dist/session/managed-attempt-tools.d.ts +17 -1
  87. package/dist/session/managed-attempt-tools.js +485 -31
  88. package/dist/session/managed-build-coordinator.d.ts +5 -0
  89. package/dist/session/managed-build-coordinator.js +105 -0
  90. package/dist/session/managed-build-receipt.d.ts +21 -0
  91. package/dist/session/managed-build-receipt.js +337 -0
  92. package/dist/session/managed-verification-timeouts.d.ts +8 -0
  93. package/dist/session/managed-verification-timeouts.js +17 -0
  94. package/dist/session/phase-completion.artifacts.d.ts +17 -0
  95. package/dist/session/phase-completion.artifacts.js +224 -22
  96. package/dist/session/phase-completion.d.ts +11 -1
  97. package/dist/session/phase-completion.helpers.d.ts +5 -0
  98. package/dist/session/phase-completion.helpers.js +30 -7
  99. package/dist/session/phase-completion.js +14 -3
  100. package/dist/session/phase-completion.prompts.js +17 -18
  101. package/dist/session/phase-completion.runtime.d.ts +3 -1
  102. package/dist/session/phase-completion.runtime.js +126 -17
  103. package/dist/session/phase-completion.schemas.js +49 -2
  104. package/dist/session/phase-completion.validation.d.ts +1 -0
  105. package/dist/session/phase-completion.validation.js +111 -21
  106. package/dist/session/pre-publication-validation.d.ts +75 -0
  107. package/dist/session/pre-publication-validation.js +661 -0
  108. package/dist/session/provider-cli/bridge-cli.conformance-fixtures.d.ts +72 -0
  109. package/dist/session/provider-cli/bridge-cli.conformance-fixtures.js +462 -0
  110. package/dist/session/provider-cli/ci-repair-readiness.d.ts +8 -0
  111. package/dist/session/provider-cli/ci-repair-readiness.js +23 -3
  112. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +4 -2
  113. package/dist/session/provider-cli/provider-cli.artifacts.js +47 -13
  114. package/dist/session/provider-cli/provider-cli.deterministic.js +34 -2
  115. package/dist/session/provider-cli/provider-cli.process.d.ts +51 -0
  116. package/dist/session/provider-cli/provider-cli.process.js +422 -22
  117. package/dist/session/provider-cli/provider-cli.prompts.d.ts +5 -0
  118. package/dist/session/provider-cli/provider-cli.prompts.js +136 -16
  119. package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
  120. package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
  121. package/dist/session/provider-cli/provider-cli.runtime.d.ts +8 -1
  122. package/dist/session/provider-cli/provider-cli.runtime.js +43 -3
  123. package/dist/session/provider-cli/provider-cli.service.d.ts +30 -5
  124. package/dist/session/provider-cli/provider-cli.service.js +771 -68
  125. package/dist/session/provider-cli/provider-cli.types.d.ts +2 -0
  126. package/dist/session/provider-cli/provider-output-state.d.ts +10 -0
  127. package/dist/session/provider-cli/provider-output-state.js +361 -17
  128. package/dist/session/provider-cli/provider-usage.d.ts +7 -0
  129. package/dist/session/provider-cli/provider-usage.js +28 -5
  130. package/dist/session/session-redaction.js +434 -9
  131. package/dist/session/session.service.js +145 -23
  132. package/dist/session/session.types.d.ts +147 -2
  133. package/dist/session/session.types.js +587 -7
  134. package/dist/session/unreferenced-exports.d.ts +12 -0
  135. package/dist/session/unreferenced-exports.js +59 -0
  136. package/dist/shared/format/format-bytes.d.ts +1 -0
  137. package/dist/shared/format/format-bytes.js +9 -0
  138. package/dist/shared/git/git.js +2 -1
  139. package/dist/shared/path/path.d.ts +1 -0
  140. package/dist/shared/path/path.js +18 -0
  141. package/dist/shared/process/dotnet-toolchain.d.ts +17 -0
  142. package/dist/shared/process/dotnet-toolchain.js +307 -0
  143. package/dist/shared/process/host-process-supervisor.d.ts +2 -0
  144. package/dist/shared/process/host-process-supervisor.js +57 -9
  145. package/dist/shared/process/process.d.ts +2 -0
  146. package/dist/shared/process/process.js +12 -7
  147. package/dist/shared/provider-capabilities.d.ts +3 -0
  148. package/dist/shared/provider-capabilities.js +85 -0
  149. package/dist/shared/redaction/redaction.d.ts +1 -0
  150. package/dist/shared/redaction/redaction.js +39 -3
  151. package/dist/version.d.ts +1 -1
  152. package/dist/version.js +1 -1
  153. package/dist/worktree/dependency-warmer.d.ts +41 -9
  154. package/dist/worktree/dependency-warmer.js +497 -22
  155. package/dist/worktree/index.d.ts +1 -1
  156. package/dist/worktree/story-qa-reference-hydrator.d.ts +4 -0
  157. package/dist/worktree/story-qa-reference-hydrator.js +253 -0
  158. package/dist/worktree/worktree.service.d.ts +7 -0
  159. package/dist/worktree/worktree.service.js +253 -21
  160. package/dist/worktree/worktree.types.d.ts +7 -1
  161. package/package.json +7 -5
  162. package/runtime/windows-job-supervisor.ps1 +380 -14
@@ -1,23 +1,448 @@
1
- import { redactSecrets } from "../shared/redaction/redaction.js";
1
+ import { redactText, shouldRedactJsonProperty } from "../shared/redaction/redaction.js";
2
+ import { TextDecoder } from "node:util";
2
3
  const integrityProtectedEventDataTypes = new Set(["sandbox_started"]);
4
+ const structuralSecretMessage = "runner result structural metadata contains protected secret material and cannot be redacted without changing protocol identity.";
5
+ const sidecarSecretMessage = "runner result sidecar contains protected secret material and cannot be redacted without invalidating integrity metadata.";
6
+ const artifactJsonMessage = "runner result artifact JSON is invalid or exceeds safe inspection limits; transport was rejected.";
7
+ const artifactJsonKeySecretMessage = "runner result artifact JSON property name contains protected secret material and cannot be redacted without changing protocol identity.";
8
+ const artifactJsonPrimitiveSecretMessage = "runner result artifact JSON primitive contains protected secret material and cannot be redacted without changing protocol identity.";
9
+ const maximumIntegrityJsonBytes = 8 * 1024 * 1024;
10
+ const maximumIntegrityJsonDepth = 128;
11
+ const maximumIntegrityJsonTokens = 262_144;
3
12
  export function redactRunnerDispatchEvent(event, exactSecrets = []) {
4
- if (!event.data || !integrityProtectedEventDataTypes.has(event.eventType)) {
5
- return redactSecrets(event, exactSecrets);
13
+ assertStructuralFieldsDoNotContainSecrets([
14
+ event.role,
15
+ event.kind,
16
+ event.eventType,
17
+ event.wait?.kind,
18
+ event.wait?.scope,
19
+ event.wait?.wakeCondition,
20
+ event.wait?.contractOrigin,
21
+ ...(event.wait?.resourceKeys ?? []),
22
+ ], exactSecrets);
23
+ if (event.data !== undefined
24
+ && event.data !== null
25
+ && integrityProtectedEventDataTypes.has(event.eventType)) {
26
+ // Integrity-protected data cannot be rewritten because the runtime lease repeats the
27
+ // dispatch fence. Check the complete serialized payload so current and future resource
28
+ // identity fields cannot carry pending secret material across this boundary.
29
+ assertSerializedJsonDoesNotContainSecrets(event.data, exactSecrets, structuralSecretMessage);
6
30
  }
7
31
  // sandbox_started data is runner-authored protocol metadata. Its runtime
8
32
  // lease repeats the dispatch fence so the API can validate ownership. The
9
33
  // transcript redactor treats any serialized `*Token` field as a secret and
10
34
  // must not rewrite that identity before transport or result persistence.
11
- const { data, ...transcript } = event;
12
35
  return {
13
- ...redactSecrets(transcript, exactSecrets),
14
- data,
36
+ ...event,
37
+ content: redactContent(event.content, exactSecrets),
38
+ ...(event.wait === undefined
39
+ ? {}
40
+ : { wait: event.wait === null ? null : redactWait(event.wait, exactSecrets) }),
41
+ ...(event.data === undefined || event.data === null
42
+ ? {}
43
+ : {
44
+ data: integrityProtectedEventDataTypes.has(event.eventType)
45
+ ? event.data
46
+ : redactContent(event.data, exactSecrets),
47
+ }),
48
+ };
49
+ }
50
+ function redactWait(wait, exactSecrets) {
51
+ return {
52
+ ...wait,
53
+ ...(wait.blockerTitle === undefined || wait.blockerTitle === null
54
+ ? {}
55
+ : { blockerTitle: redactContent(wait.blockerTitle, exactSecrets).slice(0, 500) }),
15
56
  };
16
57
  }
17
58
  export function redactRunOnceResultPacket(result, exactSecrets = []) {
18
- const { events, ...payload } = result;
59
+ assertStructuralFieldsDoNotContainSecrets([result.resultCommitSha, result.publishedCommitSha], exactSecrets);
60
+ return {
61
+ ...result,
62
+ summary: redactContent(result.summary, exactSecrets),
63
+ ...(result.failure !== undefined
64
+ ? { failure: result.failure === null ? null : redactFailure(result.failure, exactSecrets) }
65
+ : {}),
66
+ ...(result.spec !== undefined
67
+ ? { spec: result.spec === null ? null : redactSpec(result.spec, exactSecrets) }
68
+ : {}),
69
+ ...(result.artifact !== undefined
70
+ ? { artifact: result.artifact === null ? null : redactArtifact(result.artifact, exactSecrets) }
71
+ : {}),
72
+ ...(result.artifacts !== undefined
73
+ ? {
74
+ artifacts: result.artifacts === null
75
+ ? null
76
+ : result.artifacts.map(item => redactArtifact(item, exactSecrets)),
77
+ }
78
+ : {}),
79
+ events: result.events.map(event => redactRunnerDispatchEvent(event, exactSecrets)),
80
+ };
81
+ }
82
+ function redactFailure(failure, exactSecrets) {
83
+ assertStructuralFieldsDoNotContainSecrets([failure.code, ...failure.causalChain.map(cause => cause.code)], exactSecrets);
19
84
  return {
20
- ...redactSecrets(payload, exactSecrets),
21
- events: events.map(event => redactRunnerDispatchEvent(event, exactSecrets)),
85
+ ...failure,
86
+ message: redactContent(failure.message, exactSecrets),
87
+ causalChain: failure.causalChain.map(cause => ({
88
+ ...cause,
89
+ message: redactContent(cause.message, exactSecrets),
90
+ })),
22
91
  };
23
92
  }
93
+ function redactSpec(spec, exactSecrets) {
94
+ assertStructuralFieldsDoNotContainSecrets([spec.repoPath, spec.path, spec.producedBy, spec.status], exactSecrets);
95
+ return { ...spec, body: redactContent(spec.body, exactSecrets) };
96
+ }
97
+ function redactArtifact(artifact, exactSecrets) {
98
+ assertStructuralFieldsDoNotContainSecrets([artifact.repoPath, artifact.path, artifact.producedBy, artifact.status, artifact.phase], exactSecrets);
99
+ assertSidecarsDoNotContainSecrets(artifact.sidecars, exactSecrets);
100
+ return {
101
+ ...artifact,
102
+ verdictJson: redactArtifactJson(artifact.verdictJson, exactSecrets),
103
+ ...(artifact.planJson === undefined || artifact.planJson === null
104
+ ? {}
105
+ : { planJson: redactArtifactJson(artifact.planJson, exactSecrets) }),
106
+ ...(artifact.evidenceJson === undefined || artifact.evidenceJson === null
107
+ ? {}
108
+ : { evidenceJson: redactArtifactJson(artifact.evidenceJson, exactSecrets) }),
109
+ ...(artifact.findingsJson === undefined || artifact.findingsJson === null
110
+ ? {}
111
+ : { findingsJson: redactArtifactJson(artifact.findingsJson, exactSecrets) }),
112
+ ...(artifact.implementationPlanJson === undefined || artifact.implementationPlanJson === null
113
+ ? {}
114
+ : { implementationPlanJson: redactArtifactJson(artifact.implementationPlanJson, exactSecrets) }),
115
+ ...(artifact.qaPlanJson === undefined || artifact.qaPlanJson === null
116
+ ? {}
117
+ : { qaPlanJson: redactArtifactJson(artifact.qaPlanJson, exactSecrets) }),
118
+ report: redactContent(artifact.report, exactSecrets),
119
+ // These bytes already passed the QA path/hash/size checks. The generic
120
+ // redactor strips contentBase64 fields and must not corrupt the payload.
121
+ ...(artifact.sidecars !== undefined ? { sidecars: artifact.sidecars } : {}),
122
+ };
123
+ }
124
+ function redactArtifactJson(value, exactSecrets) {
125
+ // Validate the original before redaction. Replacing an invalid value must never
126
+ // turn a malformed evidence bundle into an apparently valid QA artifact.
127
+ inspectJsonStringTokens(value, [], artifactJsonMessage);
128
+ const chunks = [];
129
+ let unchangedStart = 0;
130
+ let index = 0;
131
+ const replace = (start, end, replacement) => {
132
+ if (value.slice(start, end) === replacement)
133
+ return;
134
+ chunks.push(value.slice(unchangedStart, start), replacement);
135
+ unchangedStart = end;
136
+ };
137
+ while (index < value.length) {
138
+ if (value.charCodeAt(index) !== 0x22) {
139
+ const code = value.charCodeAt(index);
140
+ if (isJsonWhitespace(code) || isJsonPunctuation(code)) {
141
+ index += 1;
142
+ }
143
+ else {
144
+ const end = jsonValueEnd(value, index);
145
+ // Numeric credentials and other exact primitive matches are still secrets.
146
+ // Fail closed instead of silently changing evidence types to strings.
147
+ assertJsonStringDoesNotContainSecrets(value.slice(index, end), exactSecrets, artifactJsonPrimitiveSecretMessage);
148
+ index = end;
149
+ }
150
+ continue;
151
+ }
152
+ const token = decodeJsonStringToken(value, index, artifactJsonMessage);
153
+ const afterToken = skipJsonWhitespace(value, token.nextIndex);
154
+ if (value.charCodeAt(afterToken) === 0x3a) {
155
+ assertJsonStringDoesNotContainSecrets(token.decoded, exactSecrets, artifactJsonKeySecretMessage);
156
+ if (redactText(token.decoded, exactSecrets) !== token.decoded)
157
+ throw new Error(artifactJsonKeySecretMessage);
158
+ if (shouldRedactJsonProperty(token.decoded)) {
159
+ const valueStart = skipJsonWhitespace(value, afterToken + 1);
160
+ const valueEnd = jsonValueEnd(value, valueStart);
161
+ if (value[valueStart] !== '"'
162
+ || decodeJsonStringToken(value, valueStart, artifactJsonMessage).decoded !== "[REDACTED]") {
163
+ replace(valueStart, valueEnd, '"[REDACTED]"');
164
+ }
165
+ index = valueEnd;
166
+ continue;
167
+ }
168
+ }
169
+ else {
170
+ const redacted = redactText(token.decoded, exactSecrets);
171
+ if (redacted !== token.decoded)
172
+ replace(index, token.nextIndex, JSON.stringify(redacted));
173
+ }
174
+ index = token.nextIndex;
175
+ }
176
+ if (chunks.length === 0)
177
+ return value;
178
+ chunks.push(value.slice(unchangedStart));
179
+ const redacted = chunks.join("");
180
+ // Retain the same bounded contract even when a short exact secret expands into
181
+ // a longer redaction marker. No raw JSON regex or parse/stringify round-trip:
182
+ // untouched escapes, whitespace, duplicate properties and numeric text survive.
183
+ inspectJsonStringTokens(redacted, [], artifactJsonMessage);
184
+ return redacted;
185
+ }
186
+ function skipJsonWhitespace(value, start) {
187
+ let index = start;
188
+ while (index < value.length && isJsonWhitespace(value.charCodeAt(index)))
189
+ index += 1;
190
+ return index;
191
+ }
192
+ // Called only after bounded JSON validation. Locate a credential property's whole
193
+ // value (including objects/arrays) without decoding or rewriting adjacent data.
194
+ function jsonValueEnd(value, start) {
195
+ if (value.charCodeAt(start) === 0x22) {
196
+ return decodeJsonStringToken(value, start, artifactJsonMessage).nextIndex;
197
+ }
198
+ if (value[start] === "{" || value[start] === "[") {
199
+ let depth = 0;
200
+ for (let index = start; index < value.length; index += 1) {
201
+ if (value[index] === '"') {
202
+ index = decodeJsonStringToken(value, index, artifactJsonMessage).nextIndex - 1;
203
+ }
204
+ else if (value[index] === "{" || value[index] === "[") {
205
+ depth += 1;
206
+ }
207
+ else if (value[index] === "}" || value[index] === "]") {
208
+ depth -= 1;
209
+ if (depth === 0)
210
+ return index + 1;
211
+ }
212
+ }
213
+ throw new Error(artifactJsonMessage);
214
+ }
215
+ let index = start;
216
+ while (index < value.length
217
+ && !isJsonWhitespace(value.charCodeAt(index))
218
+ && value[index] !== "," && value[index] !== "}" && value[index] !== "]")
219
+ index += 1;
220
+ return index;
221
+ }
222
+ function redactContent(value, exactSecrets) {
223
+ return typeof value === "string" ? redactText(value, exactSecrets) : value;
224
+ }
225
+ function assertStructuralFieldsDoNotContainSecrets(fields, exactSecrets) {
226
+ for (const field of fields) {
227
+ if (field === undefined || field === null)
228
+ continue;
229
+ for (const secret of exactSecrets) {
230
+ if (secret.length > 0 && field.includes(secret)) {
231
+ throw new Error(structuralSecretMessage);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ function assertSidecarsDoNotContainSecrets(sidecars, exactSecrets) {
237
+ if (!sidecars || exactSecrets.length === 0)
238
+ return;
239
+ for (const sidecar of sidecars) {
240
+ const decoded = Buffer.from(sidecar.contentBase64, "base64");
241
+ for (const secret of exactSecrets) {
242
+ if (secret.length === 0)
243
+ continue;
244
+ if (sidecar.path.includes(secret)
245
+ || sidecar.contentBase64.includes(secret)
246
+ || sidecar.contentHash.includes(secret)
247
+ || decoded.indexOf(Buffer.from(secret, "utf8")) >= 0) {
248
+ throw new Error(sidecarSecretMessage);
249
+ }
250
+ }
251
+ assertSidecarJsonDoesNotContainSecrets(decoded, sidecar.path, exactSecrets, sidecarSecretMessage);
252
+ }
253
+ }
254
+ function assertSerializedJsonDoesNotContainSecrets(value, exactSecrets, errorMessage) {
255
+ if (exactSecrets.length === 0)
256
+ return;
257
+ assertJsonStringDoesNotContainSecrets(value, exactSecrets, errorMessage);
258
+ inspectJsonStringTokens(value, exactSecrets, errorMessage);
259
+ }
260
+ function assertSidecarJsonDoesNotContainSecrets(value, path, exactSecrets, errorMessage) {
261
+ let decoded;
262
+ try {
263
+ decoded = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(value);
264
+ }
265
+ catch {
266
+ if (path.toLowerCase().endsWith(".json"))
267
+ throw new Error(errorMessage);
268
+ return;
269
+ }
270
+ if (!looksLikeJson(decoded, path))
271
+ return;
272
+ const hasUtf8Bom = value.length >= 3
273
+ && value[0] === 0xef
274
+ && value[1] === 0xbb
275
+ && value[2] === 0xbf;
276
+ if (hasUtf8Bom)
277
+ throw new Error(errorMessage);
278
+ inspectJsonStringTokens(decoded, exactSecrets, errorMessage);
279
+ }
280
+ function inspectJsonStringTokens(value, exactSecrets, errorMessage) {
281
+ if (Buffer.byteLength(value, "utf8") > maximumIntegrityJsonBytes) {
282
+ throw new Error(errorMessage);
283
+ }
284
+ const containers = [];
285
+ let index = 0;
286
+ let tokenCount = 0;
287
+ const countToken = () => {
288
+ tokenCount += 1;
289
+ if (tokenCount > maximumIntegrityJsonTokens)
290
+ throw new Error(errorMessage);
291
+ };
292
+ while (index < value.length) {
293
+ const code = value.charCodeAt(index);
294
+ if (code === 0x22) {
295
+ countToken();
296
+ const token = decodeJsonStringToken(value, index, errorMessage);
297
+ assertJsonStringDoesNotContainSecrets(token.decoded, exactSecrets, errorMessage);
298
+ index = token.nextIndex;
299
+ continue;
300
+ }
301
+ if (code === 0x7b || code === 0x5b) {
302
+ countToken();
303
+ containers.push(code);
304
+ if (containers.length > maximumIntegrityJsonDepth)
305
+ throw new Error(errorMessage);
306
+ index += 1;
307
+ continue;
308
+ }
309
+ if (code === 0x7d || code === 0x5d) {
310
+ countToken();
311
+ const expected = code === 0x7d ? 0x7b : 0x5b;
312
+ if (containers.pop() !== expected)
313
+ throw new Error(errorMessage);
314
+ index += 1;
315
+ continue;
316
+ }
317
+ if (code === 0x2c || code === 0x3a) {
318
+ index += 1;
319
+ continue;
320
+ }
321
+ if (isJsonWhitespace(code)) {
322
+ index += 1;
323
+ continue;
324
+ }
325
+ countToken();
326
+ do {
327
+ index += 1;
328
+ } while (index < value.length
329
+ && !isJsonWhitespace(value.charCodeAt(index))
330
+ && !isJsonPunctuation(value.charCodeAt(index)));
331
+ }
332
+ if (containers.length !== 0)
333
+ throw new Error(errorMessage);
334
+ try {
335
+ JSON.parse(value);
336
+ }
337
+ catch {
338
+ throw new Error(errorMessage);
339
+ }
340
+ }
341
+ function decodeJsonStringToken(value, openingQuoteIndex, errorMessage) {
342
+ const chunks = [];
343
+ let segmentStart = openingQuoteIndex + 1;
344
+ let index = segmentStart;
345
+ while (index < value.length) {
346
+ const code = value.charCodeAt(index);
347
+ if (code === 0x22) {
348
+ chunks.push(value.slice(segmentStart, index));
349
+ const decoded = chunks.join("");
350
+ if (!isWellFormedUnicode(decoded))
351
+ throw new Error(errorMessage);
352
+ return { decoded, nextIndex: index + 1 };
353
+ }
354
+ if (code < 0x20)
355
+ throw new Error(errorMessage);
356
+ if (code !== 0x5c) {
357
+ index += 1;
358
+ continue;
359
+ }
360
+ chunks.push(value.slice(segmentStart, index));
361
+ index += 1;
362
+ if (index >= value.length)
363
+ throw new Error(errorMessage);
364
+ const escaped = value[index];
365
+ switch (escaped) {
366
+ case "\"":
367
+ chunks.push("\"");
368
+ index += 1;
369
+ break;
370
+ case "\\":
371
+ chunks.push("\\");
372
+ index += 1;
373
+ break;
374
+ case "/":
375
+ chunks.push("/");
376
+ index += 1;
377
+ break;
378
+ case "b":
379
+ chunks.push("\b");
380
+ index += 1;
381
+ break;
382
+ case "f":
383
+ chunks.push("\f");
384
+ index += 1;
385
+ break;
386
+ case "n":
387
+ chunks.push("\n");
388
+ index += 1;
389
+ break;
390
+ case "r":
391
+ chunks.push("\r");
392
+ index += 1;
393
+ break;
394
+ case "t":
395
+ chunks.push("\t");
396
+ index += 1;
397
+ break;
398
+ case "u": {
399
+ const hex = value.slice(index + 1, index + 5);
400
+ if (!/^[0-9a-fA-F]{4}$/u.test(hex))
401
+ throw new Error(errorMessage);
402
+ chunks.push(String.fromCharCode(Number.parseInt(hex, 16)));
403
+ index += 5;
404
+ break;
405
+ }
406
+ default: throw new Error(errorMessage);
407
+ }
408
+ segmentStart = index;
409
+ }
410
+ throw new Error(errorMessage);
411
+ }
412
+ function isWellFormedUnicode(value) {
413
+ for (let index = 0; index < value.length; index += 1) {
414
+ const code = value.charCodeAt(index);
415
+ if (code >= 0xd800 && code <= 0xdbff) {
416
+ if (index + 1 >= value.length)
417
+ return false;
418
+ const next = value.charCodeAt(index + 1);
419
+ if (next < 0xdc00 || next > 0xdfff)
420
+ return false;
421
+ index += 1;
422
+ }
423
+ else if (code >= 0xdc00 && code <= 0xdfff) {
424
+ return false;
425
+ }
426
+ }
427
+ return true;
428
+ }
429
+ function looksLikeJson(value, path) {
430
+ if (path.toLowerCase().endsWith(".json"))
431
+ return true;
432
+ const candidate = value.trimStart().replace(/^\uFEFF/u, "").trimStart();
433
+ return candidate.startsWith("{") || candidate.startsWith("[") || candidate.startsWith("\"");
434
+ }
435
+ function isJsonWhitespace(code) {
436
+ return code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d;
437
+ }
438
+ function isJsonPunctuation(code) {
439
+ return code === 0x7b || code === 0x7d || code === 0x5b || code === 0x5d
440
+ || code === 0x2c || code === 0x3a || code === 0x22;
441
+ }
442
+ function assertJsonStringDoesNotContainSecrets(value, exactSecrets, errorMessage) {
443
+ for (const secret of exactSecrets) {
444
+ if (secret.length > 0 && value.includes(secret)) {
445
+ throw new Error(errorMessage);
446
+ }
447
+ }
448
+ }