@empire-builder-kit/nx 1.0.0-rc.44 → 1.0.0-rc.46
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/package.json +3 -3
- package/src/blueprints/external-sandbox.d.ts +1 -1
- package/src/cli/main.d.ts +11 -5
- package/src/cli/main.js +88 -29
- package/src/container-images.d.ts +1 -1
- package/src/container-images.js +1 -1
- package/src/developer-agent/generator-planning.js +10 -3
- package/src/developer-agent/operation-registry.js +1 -1
- package/src/executors/deployment-plan/deployment-plan.js +5 -1
- package/src/executors/deployment-plan/schema.json +1 -1
- package/src/executors/fleet/fleet.js +4 -0
- package/src/executors/fleet/schema.json +1 -1
- package/src/executors/github-bootstrap/github-bootstrap.d.ts +3 -0
- package/src/executors/github-bootstrap/github-bootstrap.js +28 -0
- package/src/executors/github-bootstrap/schema.json +14 -0
- package/src/executors/local-deploy/federated-credential-transport.d.ts +4 -3
- package/src/executors/local-deploy/federated-credential-transport.js +15 -5
- package/src/foundation/cost-estimate.d.ts +8 -3
- package/src/foundation/cost-estimate.js +42 -19
- package/src/foundation/index.d.ts +1 -0
- package/src/foundation/index.js +1 -0
- package/src/foundation/local-private-smoke-access.js +6 -0
- package/src/foundation/logging.d.ts +3 -1
- package/src/foundation/logging.js +8 -1
- package/src/foundation/observability.d.ts +42 -0
- package/src/foundation/observability.js +181 -0
- package/src/foundation/preflight.js +1 -1
- package/src/generators/function/function.js +5 -2
- package/src/generators/observability-infrastructure.d.ts +2 -0
- package/src/generators/observability-infrastructure.js +31 -0
- package/src/generators/observability-policy.d.ts +4 -0
- package/src/generators/observability-policy.js +23 -0
- package/src/generators/preset/dependency-overrides.d.ts +17 -6
- package/src/generators/preset/dependency-overrides.js +17 -6
- package/src/generators/preset/tool-versions.d.ts +2 -2
- package/src/generators/preset/tool-versions.js +2 -2
- package/src/generators/preset/workspace-files-adopter-skills.js +28 -17
- package/src/generators/preset/workspace-files-agent-development.d.ts +9 -0
- package/src/generators/preset/workspace-files-agent-development.js +42 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +17 -4
- package/src/generators/preset/workspace-files-thin-docs.js +10 -22
- package/src/generators/preset/workspace-files.d.ts +1 -0
- package/src/generators/preset/workspace-files.js +120 -35
- package/src/generators/slice/built-in-plan-alarm-identity.d.ts +173 -0
- package/src/generators/slice/built-in-plan-alarm-identity.js +202 -0
- package/src/generators/slice/built-in-plan-api-contract-files.js +136 -0
- package/src/generators/slice/built-in-plan-data-files.js +10 -3
- package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1 -1
- package/src/generators/slice/built-in-plan-event-contract-literal.d.ts +6 -0
- package/src/generators/slice/built-in-plan-event-contract-literal.js +52 -0
- package/src/generators/slice/built-in-plan-infrastructure-files.js +304 -144
- package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +2 -2
- package/src/generators/slice/built-in-plan-metadata-files.js +37 -2
- package/src/generators/slice/built-in-plan-operations-files.js +106 -6
- package/src/generators/slice/built-in-plan-project-files.js +1 -0
- package/src/generators/slice/built-in-plan-release-files.js +2 -44
- package/src/generators/slice/built-in-plan-reliability-files.js +222 -236
- package/src/generators/slice/built-in-plan-shared.d.ts +29 -3
- package/src/generators/slice/built-in-plan-shared.js +29 -2
- package/src/generators/slice/built-in-plan-verification-files.js +0 -1
- package/src/generators/slice/built-in-plan-web-files.js +22 -6
- package/src/generators/slice/built-in-plan.js +2 -2
- package/src/generators/slice/slice.js +2 -0
- package/src/generators/upgrade/upgrade.d.ts +4 -3
- package/src/generators/upgrade/upgrade.js +139 -29
- package/src/generators/workload/frozen-container.d.ts +1 -1
- package/src/generators/workload/reliability-registration.d.ts +1 -1
- package/src/generators/workload/reliability-registration.js +9 -9
- package/src/generators/workload/workload.d.ts +1 -1
- package/src/generators/workload/workload.js +32 -10
- package/src/git-policy/github-bootstrap.d.ts +18 -0
- package/src/git-policy/github-bootstrap.js +99 -6
- package/src/local-dev/dev-command-core.d.ts +24 -3
- package/src/local-dev/dev-command-core.js +124 -12
- package/src/local-dev/errors.d.ts +19 -1
- package/src/local-dev/errors.js +18 -1
- package/src/local-dev/fleet-client.d.ts +64 -6
- package/src/local-dev/fleet-client.js +189 -15
- package/src/local-dev/fleet-process.d.ts +47 -2
- package/src/local-dev/fleet-process.js +248 -31
- package/src/local-dev/fleet-runtime.d.ts +37 -0
- package/src/local-dev/fleet-runtime.js +127 -69
- package/src/local-dev/fleet-session.d.ts +1 -0
- package/src/local-dev/fleet-session.js +83 -7
- package/src/local-dev/fleet-state.d.ts +5 -0
- package/src/local-dev/fleet-state.js +11 -2
- package/src/local-dev/fleet-supervisor-daemon.js +1 -5
- package/src/local-dev/fleet-supervisor.d.ts +7 -0
- package/src/local-dev/fleet-supervisor.js +83 -12
- package/src/local-dev/slice-runner.d.ts +19 -1
- package/src/local-dev/slice-runner.js +290 -91
- package/src/repository-policy/deployment-plan.d.ts +1 -1
- package/src/repository-policy/deployment-plan.js +16 -1
- package/src/secrets/generated-aws-control-plane.d.ts +11 -1
- package/src/secrets/generated-aws-control-plane.js +92 -29
- package/src/secrets/operator.d.ts +3 -2
- package/src/secrets/operator.js +27 -5
- package/src/supply-chain/blueprint-verification-authority.js +24 -3
- package/src/supply-chain/generated-files.js +9 -0
- package/src/supply-chain/sbom.js +74 -12
|
@@ -1,16 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AwsGeneratedSecretInitializationError = void 0;
|
|
4
|
+
exports.safeGeneratedSecretObservationDiagnostic = safeGeneratedSecretObservationDiagnostic;
|
|
4
5
|
exports.createAwsGeneratedSecretInitializationControlPlane = createAwsGeneratedSecretInitializationControlPlane;
|
|
5
6
|
const node_crypto_1 = require("node:crypto");
|
|
6
7
|
const client_secrets_manager_1 = require("@aws-sdk/client-secrets-manager");
|
|
7
8
|
const model_js_1 = require("./model.js");
|
|
9
|
+
const observationPhases = ['inspect', 'pre-create', 'post-create'];
|
|
10
|
+
const observationPredicates = [
|
|
11
|
+
'response-record',
|
|
12
|
+
'arn-type',
|
|
13
|
+
'arn-name',
|
|
14
|
+
'name',
|
|
15
|
+
'description',
|
|
16
|
+
'deleted-date',
|
|
17
|
+
'rotation-enabled',
|
|
18
|
+
'tags-shape',
|
|
19
|
+
'tags-count',
|
|
20
|
+
'tag-entry',
|
|
21
|
+
'tag-duplicate',
|
|
22
|
+
'tag-value',
|
|
23
|
+
'version-state',
|
|
24
|
+
'not-found',
|
|
25
|
+
];
|
|
26
|
+
/** Copy only static labels; never invoke getters or retain provider detail. */
|
|
27
|
+
function safeGeneratedSecretObservationDiagnostic(value) {
|
|
28
|
+
try {
|
|
29
|
+
if (!isRecord(value))
|
|
30
|
+
return undefined;
|
|
31
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
32
|
+
if (Reflect.ownKeys(descriptors).length !== 2 ||
|
|
33
|
+
!Object.prototype.hasOwnProperty.call(descriptors.phase ?? {}, 'value') ||
|
|
34
|
+
!Object.prototype.hasOwnProperty.call(descriptors.predicate ?? {}, 'value')) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const phase = descriptors.phase?.value;
|
|
38
|
+
const predicate = descriptors.predicate?.value;
|
|
39
|
+
if (!observationPhases.some((entry) => entry === phase) ||
|
|
40
|
+
!observationPredicates.some((entry) => entry === predicate)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return Object.freeze({
|
|
44
|
+
phase: phase,
|
|
45
|
+
predicate: predicate,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
8
52
|
class AwsGeneratedSecretInitializationError extends Error {
|
|
9
53
|
code;
|
|
10
|
-
|
|
54
|
+
observationDiagnostic;
|
|
55
|
+
constructor(code, observationDiagnostic) {
|
|
11
56
|
super(`Generated secret initialization failed (${code}).`);
|
|
12
57
|
this.name = 'AwsGeneratedSecretInitializationError';
|
|
13
58
|
this.code = code;
|
|
59
|
+
this.observationDiagnostic = safeGeneratedSecretObservationDiagnostic(observationDiagnostic);
|
|
14
60
|
}
|
|
15
61
|
}
|
|
16
62
|
exports.AwsGeneratedSecretInitializationError = AwsGeneratedSecretInitializationError;
|
|
@@ -71,21 +117,26 @@ function tagsToAws(tags) {
|
|
|
71
117
|
.sort()
|
|
72
118
|
.map((Key) => ({ Key, Value: tags[Key] }));
|
|
73
119
|
}
|
|
74
|
-
function
|
|
75
|
-
if (!Array.isArray(value)
|
|
76
|
-
return
|
|
77
|
-
|
|
120
|
+
function tagsDiagnostic(value, expected) {
|
|
121
|
+
if (!Array.isArray(value))
|
|
122
|
+
return 'tags-shape';
|
|
123
|
+
if (value.length !== Object.keys(expected).length)
|
|
124
|
+
return 'tags-count';
|
|
78
125
|
const observed = {};
|
|
79
126
|
for (const entry of value) {
|
|
80
127
|
if (!isRecord(entry) ||
|
|
81
128
|
typeof entry['Key'] !== 'string' ||
|
|
82
|
-
typeof entry['Value'] !== 'string'
|
|
83
|
-
|
|
84
|
-
|
|
129
|
+
typeof entry['Value'] !== 'string') {
|
|
130
|
+
return 'tag-entry';
|
|
131
|
+
}
|
|
132
|
+
if (Object.prototype.hasOwnProperty.call(observed, entry['Key'])) {
|
|
133
|
+
return 'tag-duplicate';
|
|
85
134
|
}
|
|
86
135
|
observed[entry['Key']] = entry['Value'];
|
|
87
136
|
}
|
|
88
|
-
return Object.entries(expected).every(([key, expectedValue]) => observed[key] === expectedValue)
|
|
137
|
+
return Object.entries(expected).every(([key, expectedValue]) => observed[key] === expectedValue)
|
|
138
|
+
? undefined
|
|
139
|
+
: 'tag-value';
|
|
89
140
|
}
|
|
90
141
|
function arnMatchesName(arn, name) {
|
|
91
142
|
if (!(0, model_js_1.isExactSecretArn)(arn))
|
|
@@ -108,20 +159,32 @@ function inspectVersionState(output, versionId) {
|
|
|
108
159
|
? 'current'
|
|
109
160
|
: 'conflicting';
|
|
110
161
|
}
|
|
111
|
-
function requireDescribeOutput(value, input) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
162
|
+
function requireDescribeOutput(value, input, phase) {
|
|
163
|
+
let predicate;
|
|
164
|
+
if (!isRecord(value))
|
|
165
|
+
predicate = 'response-record';
|
|
166
|
+
else if (typeof value['ARN'] !== 'string')
|
|
167
|
+
predicate = 'arn-type';
|
|
168
|
+
else if (!arnMatchesName(value['ARN'], input.name))
|
|
169
|
+
predicate = 'arn-name';
|
|
170
|
+
else if (value['Name'] !== input.name)
|
|
171
|
+
predicate = 'name';
|
|
172
|
+
else if (value['Description'] !== input.description)
|
|
173
|
+
predicate = 'description';
|
|
174
|
+
else if (value['DeletedDate'] !== undefined)
|
|
175
|
+
predicate = 'deleted-date';
|
|
176
|
+
else if (value['RotationEnabled'] === true)
|
|
177
|
+
predicate = 'rotation-enabled';
|
|
178
|
+
else
|
|
179
|
+
predicate = tagsDiagnostic(value['Tags'], input.tags);
|
|
180
|
+
if (predicate !== undefined) {
|
|
181
|
+
throw new AwsGeneratedSecretInitializationError('generated-secret-observation-invalid', { phase, predicate });
|
|
121
182
|
}
|
|
183
|
+
// The branches above establish the same shape as the former compound check.
|
|
184
|
+
const output = value;
|
|
122
185
|
return {
|
|
123
|
-
arn:
|
|
124
|
-
state: inspectVersionState(
|
|
186
|
+
arn: output.ARN,
|
|
187
|
+
state: inspectVersionState(output, expectedVersionId(input.name, input.generation)),
|
|
125
188
|
};
|
|
126
189
|
}
|
|
127
190
|
function requireCreateOutput(value, input, versionId) {
|
|
@@ -161,7 +224,7 @@ function createAwsGeneratedSecretInitializationControlPlane(options = {}) {
|
|
|
161
224
|
const client = options.client ?? defaultClient(options.clientConfiguration ?? {});
|
|
162
225
|
const generateBytes = options.randomBytes ?? node_crypto_1.randomBytes;
|
|
163
226
|
const wait = options.wait ?? defaultWait;
|
|
164
|
-
const inspectExact = async (input) => {
|
|
227
|
+
const inspectExact = async (input, phase) => {
|
|
165
228
|
requireInput(input);
|
|
166
229
|
let rawOutput;
|
|
167
230
|
try {
|
|
@@ -172,13 +235,13 @@ function createAwsGeneratedSecretInitializationControlPlane(options = {}) {
|
|
|
172
235
|
return { state: 'missing' };
|
|
173
236
|
throw new AwsGeneratedSecretInitializationError('generated-secret-read-failed');
|
|
174
237
|
}
|
|
175
|
-
return requireDescribeOutput(rawOutput, input);
|
|
238
|
+
return requireDescribeOutput(rawOutput, input, phase);
|
|
176
239
|
};
|
|
177
240
|
const inspectAfterCreate = async (input) => {
|
|
178
241
|
let finalInspection = { state: 'missing' };
|
|
179
242
|
for (const delay of inspectionDelaysMilliseconds) {
|
|
180
243
|
await wait(delay);
|
|
181
|
-
finalInspection = await inspectExact(input);
|
|
244
|
+
finalInspection = await inspectExact(input, 'post-create');
|
|
182
245
|
if (finalInspection.state !== 'missing')
|
|
183
246
|
return finalInspection;
|
|
184
247
|
}
|
|
@@ -186,15 +249,15 @@ function createAwsGeneratedSecretInitializationControlPlane(options = {}) {
|
|
|
186
249
|
};
|
|
187
250
|
return Object.freeze({
|
|
188
251
|
async inspect(input) {
|
|
189
|
-
return (await inspectExact(input)).state;
|
|
252
|
+
return (await inspectExact(input, 'inspect')).state;
|
|
190
253
|
},
|
|
191
254
|
async initializeGenerated(input) {
|
|
192
255
|
requireInput(input);
|
|
193
|
-
const initial = await inspectExact(input);
|
|
256
|
+
const initial = await inspectExact(input, 'pre-create');
|
|
194
257
|
if (initial.state === 'current')
|
|
195
258
|
return 'unchanged';
|
|
196
259
|
if (initial.state !== 'missing') {
|
|
197
|
-
throw new AwsGeneratedSecretInitializationError('generated-secret-state-conflicting');
|
|
260
|
+
throw new AwsGeneratedSecretInitializationError('generated-secret-state-conflicting', { phase: 'pre-create', predicate: 'version-state' });
|
|
198
261
|
}
|
|
199
262
|
let idBytes;
|
|
200
263
|
let keyBytes;
|
|
@@ -248,11 +311,11 @@ function createAwsGeneratedSecretInitializationControlPlane(options = {}) {
|
|
|
248
311
|
return responseAccepted ? 'applied' : 'unchanged';
|
|
249
312
|
}
|
|
250
313
|
if (finalInspection.state === 'conflicting') {
|
|
251
|
-
throw new AwsGeneratedSecretInitializationError('generated-secret-state-conflicting');
|
|
314
|
+
throw new AwsGeneratedSecretInitializationError('generated-secret-state-conflicting', { phase: 'post-create', predicate: 'version-state' });
|
|
252
315
|
}
|
|
253
316
|
throw new AwsGeneratedSecretInitializationError(responseAccepted
|
|
254
317
|
? 'generated-secret-observation-invalid'
|
|
255
|
-
: 'generated-secret-write-failed');
|
|
318
|
+
: 'generated-secret-write-failed', { phase: 'post-create', predicate: 'not-found' });
|
|
256
319
|
},
|
|
257
320
|
});
|
|
258
321
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AwsGeneratedSecretInitializationError } from './generated-aws-control-plane.js';
|
|
1
|
+
import { AwsGeneratedSecretInitializationError, type GeneratedSecretObservationDiagnostic } from './generated-aws-control-plane.js';
|
|
2
2
|
import { type SecretMetadataFinding, type SecretMetadataMutationIntent, type SecretMetadataPreflightResult, type SecretMetadataReconciliationResult } from './metadata.js';
|
|
3
3
|
import { type ObservedSecretMetadata, type SecretManagementScope, type SecretMetadataDeclaration } from './model.js';
|
|
4
4
|
export interface ListedSecretContainer {
|
|
@@ -84,8 +84,9 @@ export type SecretProtectedLifecycleAction = 'deploy' | 'recover' | 'remove' | '
|
|
|
84
84
|
export declare class SecretOperatorControlPlaneError extends Error {
|
|
85
85
|
readonly operation: 'create-empty' | 'describe' | 'inventory' | 'initialize-generated' | 'list' | 'recover' | 'retire' | 'tag';
|
|
86
86
|
readonly diagnosticCode?: AwsGeneratedSecretInitializationError['code'];
|
|
87
|
+
readonly observationDiagnostic?: GeneratedSecretObservationDiagnostic;
|
|
87
88
|
readonly code: 'secret-control-plane-apply-failed' | 'secret-control-plane-inventory-failed' | 'secret-control-plane-inventory-invalid' | 'secret-control-plane-protected-action-failed';
|
|
88
|
-
constructor(code: SecretOperatorControlPlaneError['code'], operation: 'create-empty' | 'describe' | 'inventory' | 'initialize-generated' | 'list' | 'recover' | 'retire' | 'tag', diagnosticCode?: AwsGeneratedSecretInitializationError['code']);
|
|
89
|
+
constructor(code: SecretOperatorControlPlaneError['code'], operation: 'create-empty' | 'describe' | 'inventory' | 'initialize-generated' | 'list' | 'recover' | 'retire' | 'tag', diagnosticCode?: AwsGeneratedSecretInitializationError['code'], observationDiagnostic?: GeneratedSecretObservationDiagnostic);
|
|
89
90
|
}
|
|
90
91
|
export interface SecretMetadataOperatorResult {
|
|
91
92
|
after?: SecretMetadataReconciliationResult;
|
package/src/secrets/operator.js
CHANGED
|
@@ -13,14 +13,20 @@ const model_js_1 = require("./model.js");
|
|
|
13
13
|
class SecretOperatorControlPlaneError extends Error {
|
|
14
14
|
operation;
|
|
15
15
|
diagnosticCode;
|
|
16
|
+
observationDiagnostic;
|
|
16
17
|
code;
|
|
17
|
-
constructor(code, operation, diagnosticCode) {
|
|
18
|
+
constructor(code, operation, diagnosticCode, observationDiagnostic) {
|
|
19
|
+
const safeObservation = (0, generated_aws_control_plane_js_1.safeGeneratedSecretObservationDiagnostic)(observationDiagnostic);
|
|
18
20
|
super(`Secret metadata ${operation} failed (${code}).` +
|
|
19
|
-
(diagnosticCode ? ` Reason: ${diagnosticCode}.` : '')
|
|
21
|
+
(diagnosticCode ? ` Reason: ${diagnosticCode}.` : '') +
|
|
22
|
+
(safeObservation
|
|
23
|
+
? ` Observation: ${safeObservation.phase}/${safeObservation.predicate}.`
|
|
24
|
+
: ''));
|
|
20
25
|
this.operation = operation;
|
|
21
26
|
this.diagnosticCode = diagnosticCode;
|
|
22
27
|
this.name = 'SecretOperatorControlPlaneError';
|
|
23
28
|
this.code = code;
|
|
29
|
+
this.observationDiagnostic = safeObservation;
|
|
24
30
|
}
|
|
25
31
|
}
|
|
26
32
|
exports.SecretOperatorControlPlaneError = SecretOperatorControlPlaneError;
|
|
@@ -35,14 +41,30 @@ function safeControlPlaneError(error, code, operation) {
|
|
|
35
41
|
// Preserve only our finite, value-blind diagnostic vocabulary. Never attach
|
|
36
42
|
// the raw SDK error, message, stack, request or cause: they may hold values.
|
|
37
43
|
if (error instanceof generated_aws_control_plane_js_1.AwsGeneratedSecretInitializationError) {
|
|
38
|
-
|
|
44
|
+
let diagnosticCode;
|
|
45
|
+
try {
|
|
46
|
+
diagnosticCode = Object.getOwnPropertyDescriptor(error, 'code')?.value;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// A descriptor trap must not replace the safe error with its own.
|
|
50
|
+
return new SecretOperatorControlPlaneError(code, operation);
|
|
51
|
+
}
|
|
39
52
|
switch (diagnosticCode) {
|
|
40
53
|
case 'generated-secret-input-invalid':
|
|
41
54
|
case 'generated-secret-observation-invalid':
|
|
42
55
|
case 'generated-secret-read-failed':
|
|
43
56
|
case 'generated-secret-state-conflicting':
|
|
44
|
-
case 'generated-secret-write-failed':
|
|
45
|
-
|
|
57
|
+
case 'generated-secret-write-failed': {
|
|
58
|
+
let observationDiagnostic;
|
|
59
|
+
try {
|
|
60
|
+
observationDiagnostic = (0, generated_aws_control_plane_js_1.safeGeneratedSecretObservationDiagnostic)(Object.getOwnPropertyDescriptor(error, 'observationDiagnostic')
|
|
61
|
+
?.value);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// An optional descriptor trap is untrusted detail, not a new error.
|
|
65
|
+
}
|
|
66
|
+
return new SecretOperatorControlPlaneError(code, operation, diagnosticCode, observationDiagnostic);
|
|
67
|
+
}
|
|
46
68
|
}
|
|
47
69
|
}
|
|
48
70
|
return new SecretOperatorControlPlaneError(code, operation);
|
|
@@ -439,6 +439,25 @@ function isBlueprintVerificationExecutionAuthorityPath(path) {
|
|
|
439
439
|
/^(?:vite|vitest)\.config\.[a-z0-9]+$/u.test(name) ||
|
|
440
440
|
/^packages\/[^/]+\/\.ebk\/ownership\.json$/u.test(normalized));
|
|
441
441
|
}
|
|
442
|
+
function isNativeArtifactDependencyBoundary(workspaceRoot, path, candidate) {
|
|
443
|
+
const match = /^packages\/([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\/\.sst\/artifacts\/[^/]+\/node_modules$/u.exec(path);
|
|
444
|
+
const slice = match?.[1];
|
|
445
|
+
if (!slice)
|
|
446
|
+
return false;
|
|
447
|
+
const dependencyRoot = (0, node_path_1.join)(workspaceRoot, 'packages', slice, 'node_modules');
|
|
448
|
+
try {
|
|
449
|
+
const metadata = (0, node_fs_1.lstatSync)(dependencyRoot);
|
|
450
|
+
return (metadata.isDirectory() &&
|
|
451
|
+
!metadata.isSymbolicLink() &&
|
|
452
|
+
(0, node_fs_1.realpathSync)(dependencyRoot) === dependencyRoot &&
|
|
453
|
+
(0, node_path_1.toNamespacedPath)((0, node_path_1.resolve)((0, node_path_1.dirname)(candidate), (0, node_fs_1.readlinkSync)(candidate))) ===
|
|
454
|
+
(0, node_path_1.toNamespacedPath)(dependencyRoot) &&
|
|
455
|
+
(0, node_fs_1.realpathSync)(candidate) === dependencyRoot);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
442
461
|
/**
|
|
443
462
|
* Inventories every execution-loader configuration outside bounded generated,
|
|
444
463
|
* dependency, cache, and evidence directories. Forbidden ambient config is
|
|
@@ -467,15 +486,17 @@ function assertNoMutableBlueprintVerificationConfiguration(workspace) {
|
|
|
467
486
|
const path = directoryPath
|
|
468
487
|
? `${directoryPath}/${entry.name}`
|
|
469
488
|
: entry.name;
|
|
470
|
-
const
|
|
471
|
-
|
|
489
|
+
const candidate = (0, node_path_1.join)(directory, entry.name);
|
|
490
|
+
const skippedDirectory = (entry.isDirectory() &&
|
|
491
|
+
SKIPPED_CONFIGURATION_DIRECTORIES.has(entry.name.toLowerCase())) ||
|
|
492
|
+
(entry.isSymbolicLink() &&
|
|
493
|
+
isNativeArtifactDependencyBoundary(workspaceRoot, path, candidate));
|
|
472
494
|
if (entry.isSymbolicLink() && !skippedDirectory) {
|
|
473
495
|
throw new Error(`Blueprint verification configuration inventory refuses symbolic-link authority at ${path}.`);
|
|
474
496
|
}
|
|
475
497
|
if (forbiddenConfigurationName(entry.name)) {
|
|
476
498
|
throw new Error(`Blueprint verification refuses mutable environment or package-manager configuration at ${path}.`);
|
|
477
499
|
}
|
|
478
|
-
const candidate = (0, node_path_1.join)(directory, entry.name);
|
|
479
500
|
if (protectedConfigurationName(entry.name)) {
|
|
480
501
|
const metadata = (0, node_fs_1.lstatSync)(candidate);
|
|
481
502
|
const child = (0, node_path_1.relative)(workspaceRoot, candidate);
|
|
@@ -275,6 +275,15 @@ to generate the effective managed \`pnpm-workspace.yaml\`, then run
|
|
|
275
275
|
Review the lockfile and rerun affected checks. Never edit the managed YAML or
|
|
276
276
|
ownership hashes. Pins do not waive the existing supply-chain policies.
|
|
277
277
|
|
|
278
|
+
The Standard Slice owns an exact local \`@opennextjs/aws\` development dependency.
|
|
279
|
+
Its build wrapper refuses a missing or different local builder; it never invokes
|
|
280
|
+
npx or dlx. Fresh override defaults include the reviewed
|
|
281
|
+
\`@node-minify/core@9.0.2>glob@10.3.3: 10.5.0\` correction. Existing seeded
|
|
282
|
+
policy always wins: explicitly reconcile that entry in the editable JSON before
|
|
283
|
+
the supported workspace report/apply, preserving other decisions. The override
|
|
284
|
+
governs the managed builder tree; OpenNext's nested npm image install remains a
|
|
285
|
+
separate boundary checked by the generated Sharp metadata/native-target guard.
|
|
286
|
+
|
|
278
287
|
The license lists are organization-owned policy, not legal conclusions.
|
|
279
288
|
Unknown and changed licenses require review. Exceptions are exact-component,
|
|
280
289
|
approved, expiring records with compensating controls and recheck triggers.
|
package/src/supply-chain/sbom.js
CHANGED
|
@@ -160,7 +160,8 @@ function createCycloneDxComponentsFromInventory(input) {
|
|
|
160
160
|
}));
|
|
161
161
|
}
|
|
162
162
|
function canonicalTimestamp(value, path) {
|
|
163
|
-
if (
|
|
163
|
+
if (typeof value !== 'string' ||
|
|
164
|
+
!Number.isFinite(Date.parse(value)) ||
|
|
164
165
|
new Date(value).toISOString() !== value) {
|
|
165
166
|
throw new errors_js_1.SupplyChainError('supply-chain-input-invalid', path, 'must be a canonical ISO-8601 timestamp');
|
|
166
167
|
}
|
|
@@ -775,8 +776,11 @@ function bindingInput(value) {
|
|
|
775
776
|
}
|
|
776
777
|
function createSupplyChainReleaseBinding(input) {
|
|
777
778
|
canonicalTimestamp(input.createdAt, 'createdAt');
|
|
778
|
-
if (
|
|
779
|
-
|
|
779
|
+
if (typeof input.releaseId !== 'string' ||
|
|
780
|
+
common_js_1.SUPPLY_CHAIN_REFERENCE.exec(input.releaseId)?.[0] !== input.releaseId ||
|
|
781
|
+
typeof input.releaseManifestDigest !== 'string' ||
|
|
782
|
+
common_js_1.SUPPLY_CHAIN_SHA256.exec(input.releaseManifestDigest)?.[0] !==
|
|
783
|
+
input.releaseManifestDigest ||
|
|
780
784
|
input.artifacts.length === 0 ||
|
|
781
785
|
(0, common_js_1.hasSensitiveSupplyChainShape)(input)) {
|
|
782
786
|
throw new errors_js_1.SupplyChainError('supply-chain-input-invalid', 'releaseBinding', 'must bind at least one artifact to an exact release manifest');
|
|
@@ -785,11 +789,21 @@ function createSupplyChainReleaseBinding(input) {
|
|
|
785
789
|
const artifacts = [...input.artifacts]
|
|
786
790
|
.sort((left, right) => (0, deterministic_order_js_1.compareUtf16)(left.bindingId, right.bindingId))
|
|
787
791
|
.map((artifact, index) => {
|
|
788
|
-
if (
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
792
|
+
if (typeof artifact.bindingId !== 'string' ||
|
|
793
|
+
common_js_1.SUPPLY_CHAIN_REFERENCE.exec(artifact.bindingId)?.[0] !==
|
|
794
|
+
artifact.bindingId ||
|
|
795
|
+
typeof artifact.candidateDigest !== 'string' ||
|
|
796
|
+
common_js_1.SUPPLY_CHAIN_SHA256.exec(artifact.candidateDigest)?.[0] !==
|
|
797
|
+
artifact.candidateDigest ||
|
|
798
|
+
typeof artifact.contentDigest !== 'string' ||
|
|
799
|
+
common_js_1.SUPPLY_CHAIN_SHA256.exec(artifact.contentDigest)?.[0] !==
|
|
800
|
+
artifact.contentDigest ||
|
|
801
|
+
typeof artifact.sbomDigest !== 'string' ||
|
|
802
|
+
common_js_1.SUPPLY_CHAIN_SHA256.exec(artifact.sbomDigest)?.[0] !==
|
|
803
|
+
artifact.sbomDigest ||
|
|
804
|
+
typeof artifact.sbomSerialNumber !== 'string' ||
|
|
805
|
+
/^urn:uuid:[a-f0-9-]{36}$/.exec(artifact.sbomSerialNumber)?.[0] !==
|
|
806
|
+
artifact.sbomSerialNumber ||
|
|
793
807
|
seen.has(artifact.bindingId)) {
|
|
794
808
|
throw new errors_js_1.SupplyChainError('supply-chain-input-invalid', `releaseBinding.artifacts[${index}]`, 'must uniquely bind artifact and SBOM digests');
|
|
795
809
|
}
|
|
@@ -810,14 +824,62 @@ function createSupplyChainReleaseBinding(input) {
|
|
|
810
824
|
};
|
|
811
825
|
}
|
|
812
826
|
function validateSupplyChainReleaseBinding(value, expected) {
|
|
827
|
+
const exactKeys = (record, keys) => [Object.prototype, null].includes(Object.getPrototypeOf(record)) &&
|
|
828
|
+
Object.keys(record).sort(deterministic_order_js_1.compareUtf16).join('\0') ===
|
|
829
|
+
[...keys].sort(deterministic_order_js_1.compareUtf16).join('\0');
|
|
813
830
|
if (!(0, validation_js_1.isRecord)(value) ||
|
|
831
|
+
!exactKeys(value, [
|
|
832
|
+
'artifacts',
|
|
833
|
+
'createdAt',
|
|
834
|
+
'kind',
|
|
835
|
+
'materialDigest',
|
|
836
|
+
'releaseId',
|
|
837
|
+
'releaseManifestDigest',
|
|
838
|
+
'schemaVersion',
|
|
839
|
+
]) ||
|
|
814
840
|
value.kind !== exports.SUPPLY_CHAIN_RELEASE_BINDING_KIND ||
|
|
815
841
|
value.schemaVersion !== exports.SUPPLY_CHAIN_SBOM_SCHEMA_VERSION ||
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
842
|
+
typeof value.materialDigest !== 'string' ||
|
|
843
|
+
common_js_1.SUPPLY_CHAIN_SHA256.exec(value.materialDigest)?.[0] !==
|
|
844
|
+
value.materialDigest ||
|
|
845
|
+
typeof value.createdAt !== 'string' ||
|
|
846
|
+
typeof value.releaseId !== 'string' ||
|
|
847
|
+
typeof value.releaseManifestDigest !== 'string' ||
|
|
848
|
+
!Array.isArray(value.artifacts) ||
|
|
849
|
+
!value.artifacts.length) {
|
|
850
|
+
throw new errors_js_1.SupplyChainError('supply-chain-input-invalid', 'releaseBinding', 'must be an exact EBK supply-chain release binding v1');
|
|
851
|
+
}
|
|
852
|
+
const artifacts = [];
|
|
853
|
+
for (const artifact of value.artifacts) {
|
|
854
|
+
if (!(0, validation_js_1.isRecord)(artifact) ||
|
|
855
|
+
!exactKeys(artifact, [
|
|
856
|
+
'bindingId',
|
|
857
|
+
'candidateDigest',
|
|
858
|
+
'contentDigest',
|
|
859
|
+
'sbomDigest',
|
|
860
|
+
'sbomSerialNumber',
|
|
861
|
+
]) ||
|
|
862
|
+
typeof artifact.bindingId !== 'string' ||
|
|
863
|
+
typeof artifact.candidateDigest !== 'string' ||
|
|
864
|
+
typeof artifact.contentDigest !== 'string' ||
|
|
865
|
+
typeof artifact.sbomDigest !== 'string' ||
|
|
866
|
+
typeof artifact.sbomSerialNumber !== 'string') {
|
|
867
|
+
throw new errors_js_1.SupplyChainError('supply-chain-input-invalid', 'releaseBinding.artifacts', 'must contain exact string artifact fields');
|
|
868
|
+
}
|
|
869
|
+
artifacts.push({
|
|
870
|
+
bindingId: artifact.bindingId,
|
|
871
|
+
candidateDigest: artifact.candidateDigest,
|
|
872
|
+
contentDigest: artifact.contentDigest,
|
|
873
|
+
sbomDigest: artifact.sbomDigest,
|
|
874
|
+
sbomSerialNumber: artifact.sbomSerialNumber,
|
|
875
|
+
});
|
|
819
876
|
}
|
|
820
|
-
const recreated = createSupplyChainReleaseBinding(
|
|
877
|
+
const recreated = createSupplyChainReleaseBinding({
|
|
878
|
+
artifacts,
|
|
879
|
+
createdAt: value.createdAt,
|
|
880
|
+
releaseId: value.releaseId,
|
|
881
|
+
releaseManifestDigest: value.releaseManifestDigest,
|
|
882
|
+
});
|
|
821
883
|
if (recreated.materialDigest !== value.materialDigest) {
|
|
822
884
|
throw new errors_js_1.SupplyChainError('supply-chain-artifact-mismatch', 'releaseBinding.materialDigest', 'does not match the canonical binding bytes');
|
|
823
885
|
}
|