archgraph-argo 0.10.43 → 0.10.44
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/LICENSE +201 -201
- package/argo/package.json +8 -8
- package/argo/schema/ImplementationToCodingHandoff.schema.json +251 -251
- package/argo/schema/ImplementationToIntentTraceProposal.schema.json +180 -180
- package/argo/schema/IntentToImplementationHandoff.schema.json +74 -74
- package/argo/schema/SystemArchitecture.schema.json +378 -378
- package/argo/schema/archimate3.2.md +7153 -7153
- package/argo/scripts/archimate32-rules.js +12301 -12301
- package/argo/scripts/generateArchitectureDiffPlantuml.js +466 -466
- package/argo/scripts/graph-rag/ARCHITECTURE.md +192 -192
- package/argo/scripts/graph-rag/canonicalProjectionAuthority.js +45 -45
- package/argo/scripts/graph-rag/embeddingQualificationGate.js +59 -59
- package/argo/scripts/graph-rag/externalProductionConfig.js +74 -74
- package/argo/scripts/graph-rag/liveEmbeddingProviderClient.js +49 -49
- package/argo/scripts/graph-rag/neo4jNativeRetrieval.js +37 -37
- package/argo/scripts/graph-rag/semantic-persistence/ARCHITECTURE.md +51 -51
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticCheckpointStore.js +99 -99
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticProjectionStore.js +171 -171
- package/argo/scripts/graph-rag/semanticOperatorError.js +38 -38
- package/argo/scripts/graph-rag/semanticOperatorJourney.js +459 -459
- package/argo/scripts/graph-rag/semanticReadinessAttestationStore.js +398 -398
- package/argo/scripts/graph-rag/systemMetadataCommandAdapter.js +269 -269
- package/argo/scripts/graph-semantics.js +220 -220
- package/argo/scripts/repositoryArgoEnvironment.js +101 -101
- package/argo/scripts/runArchitectureTests.js +583 -583
- package/argo/scripts/semanticOperatorJourneyCli.js +91 -91
- package/argo/scripts/syncSystemArchitectureToNeo4j.js +66 -66
- package/argo/scripts/test-executors/_template.js +58 -58
- package/argo/scripts/test-executors/default.js +199 -199
- package/argo/scripts/validateStageHandoff.js +458 -458
- package/argo/scripts/validateSystemArchitecture.js +253 -253
- package/argo/scripts/validateTraceProposal.js +181 -181
- package/bin/argo-deploy.js +12 -12
- package/install-argo.ps1 +27 -8
- package/package.json +1 -1
|
@@ -1,269 +1,269 @@
|
|
|
1
|
-
const { spawnSync } = require('node:child_process');
|
|
2
|
-
const path = require('node:path');
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
getArgoEnvPath,
|
|
6
|
-
} = require('../argo-paths.js');
|
|
7
|
-
|
|
8
|
-
const CAPABILITY_NAMES = Object.freeze([
|
|
9
|
-
'isSecretFileIgnored',
|
|
10
|
-
'isSecretFileTracked',
|
|
11
|
-
'isSecretFileInsideGitRepository',
|
|
12
|
-
'readCurrentIdentity',
|
|
13
|
-
'readSecretFileAcl',
|
|
14
|
-
]);
|
|
15
|
-
|
|
16
|
-
function createSystemMetadataCommandAdapter(options = {}) {
|
|
17
|
-
requireExactKeys(options, ['repositoryRoot']);
|
|
18
|
-
const repositoryRoot = requireRepositoryRoot(options.repositoryRoot);
|
|
19
|
-
return createAdapter({
|
|
20
|
-
repositoryRoot,
|
|
21
|
-
executeMetadataCommand: spawnSync,
|
|
22
|
-
mutateInvocation: undefined,
|
|
23
|
-
forbiddenValues: [],
|
|
24
|
-
}).adapter;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function createReadinessAttestationMetadataAdapter(options = {}) {
|
|
28
|
-
requireExactKeys(options, ['repositoryRoot']);
|
|
29
|
-
const repositoryRoot = requireRepositoryRoot(options.repositoryRoot);
|
|
30
|
-
const file = path.join(
|
|
31
|
-
options.repositoryRoot,
|
|
32
|
-
'.argo',
|
|
33
|
-
'temp',
|
|
34
|
-
'semantic-readiness-attestation.json',
|
|
35
|
-
);
|
|
36
|
-
const directory = path.dirname(file);
|
|
37
|
-
const environment = sanitizedEnvironment();
|
|
38
|
-
|
|
39
|
-
function spawnExact(executable, args) {
|
|
40
|
-
return spawnSync(executable, args, {
|
|
41
|
-
cwd: repositoryRoot,
|
|
42
|
-
encoding: 'utf8',
|
|
43
|
-
env: { ...environment },
|
|
44
|
-
shell: false,
|
|
45
|
-
windowsHide: true,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return Object.freeze({
|
|
50
|
-
readCurrentIdentity: () => spawnExact('whoami', []),
|
|
51
|
-
readReadinessAttestationDirectoryAcl: () => spawnExact('icacls', [directory]),
|
|
52
|
-
readReadinessAttestationAcl: () => spawnExact('icacls', [file]),
|
|
53
|
-
readReadinessAttestationOwner: () => spawnExact('powershell.exe', [
|
|
54
|
-
'-NoProfile',
|
|
55
|
-
'-NonInteractive',
|
|
56
|
-
'-Command',
|
|
57
|
-
exactOwnerScript(file),
|
|
58
|
-
]),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function exactOwnerScript(file) {
|
|
63
|
-
const literalPath = String(file).replace(/'/g, "''");
|
|
64
|
-
return `(Get-Acl -LiteralPath '${literalPath}').Owner`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function sanitizedEnvironment() {
|
|
68
|
-
return Object.freeze(Object.fromEntries(
|
|
69
|
-
['PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR']
|
|
70
|
-
.filter(key => typeof process.env[key] === 'string')
|
|
71
|
-
.map(key => [key, process.env[key]]),
|
|
72
|
-
));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function withSystemMetadataCommandTestComposition(options = {}, callback) {
|
|
76
|
-
requireExactKeys(options, [
|
|
77
|
-
'executeMetadataCommand',
|
|
78
|
-
'forbiddenValues',
|
|
79
|
-
'mutateInvocation',
|
|
80
|
-
'repositoryRoot',
|
|
81
|
-
], true);
|
|
82
|
-
if (typeof options.executeMetadataCommand !== 'function' || typeof callback !== 'function') {
|
|
83
|
-
throw prohibited();
|
|
84
|
-
}
|
|
85
|
-
const composition = createAdapter({
|
|
86
|
-
repositoryRoot: requireRepositoryRoot(options.repositoryRoot),
|
|
87
|
-
executeMetadataCommand: options.executeMetadataCommand,
|
|
88
|
-
mutateInvocation: options.mutateInvocation,
|
|
89
|
-
forbiddenValues: Array.isArray(options.forbiddenValues) ? [...options.forbiddenValues] : [],
|
|
90
|
-
});
|
|
91
|
-
let result;
|
|
92
|
-
let then;
|
|
93
|
-
let asyncRevocationOwner = false;
|
|
94
|
-
try {
|
|
95
|
-
result = callback(composition.adapter);
|
|
96
|
-
if (
|
|
97
|
-
result !== null
|
|
98
|
-
&& (typeof result === 'object' || typeof result === 'function')
|
|
99
|
-
) {
|
|
100
|
-
then = result.then;
|
|
101
|
-
}
|
|
102
|
-
if (typeof then === 'function') {
|
|
103
|
-
asyncRevocationOwner = true;
|
|
104
|
-
return assimilateThenable(result, then, composition.revoke);
|
|
105
|
-
}
|
|
106
|
-
return undefined;
|
|
107
|
-
} finally {
|
|
108
|
-
if (!asyncRevocationOwner) composition.revoke();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function assimilateThenable(value, then, revoke) {
|
|
113
|
-
try {
|
|
114
|
-
await new Promise((resolve, reject) => {
|
|
115
|
-
then.call(value, resolve, reject);
|
|
116
|
-
});
|
|
117
|
-
return undefined;
|
|
118
|
-
} finally {
|
|
119
|
-
revoke();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function createAdapter({
|
|
124
|
-
repositoryRoot,
|
|
125
|
-
executeMetadataCommand,
|
|
126
|
-
mutateInvocation,
|
|
127
|
-
forbiddenValues,
|
|
128
|
-
}) {
|
|
129
|
-
const canonicalPath = getArgoEnvPath();
|
|
130
|
-
const gitDirectory = path.dirname(canonicalPath);
|
|
131
|
-
const sanitizedEnvironment = Object.freeze(Object.fromEntries(
|
|
132
|
-
['PATH', 'PATHEXT', 'SystemRoot', 'WINDIR']
|
|
133
|
-
.filter(key => typeof process.env[key] === 'string')
|
|
134
|
-
.map(key => [key, process.env[key]]),
|
|
135
|
-
));
|
|
136
|
-
let revoked = false;
|
|
137
|
-
|
|
138
|
-
function invoke(capabilityName, receivedArguments) {
|
|
139
|
-
if (revoked) {
|
|
140
|
-
const error = new Error('TEST_SYSTEM_METADATA_ADAPTER_REVOKED');
|
|
141
|
-
error.category = 'TEST_SYSTEM_METADATA_ADAPTER_REVOKED';
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
if (receivedArguments.length !== 0) throw prohibited();
|
|
145
|
-
const expected = buildInvocation(
|
|
146
|
-
capabilityName,
|
|
147
|
-
gitDirectory,
|
|
148
|
-
canonicalPath,
|
|
149
|
-
sanitizedEnvironment,
|
|
150
|
-
);
|
|
151
|
-
const candidate = typeof mutateInvocation === 'function'
|
|
152
|
-
? mutateInvocation(cloneInvocation(expected))
|
|
153
|
-
: expected;
|
|
154
|
-
validateInvocation(candidate, expected, forbiddenValues);
|
|
155
|
-
return executeMetadataCommand(
|
|
156
|
-
candidate.executable,
|
|
157
|
-
[...candidate.args],
|
|
158
|
-
cloneOptions(candidate.options),
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const adapter = Object.create(null);
|
|
163
|
-
for (const capabilityName of CAPABILITY_NAMES) {
|
|
164
|
-
const capability = capabilityName === 'isSecretFileIgnored'
|
|
165
|
-
? (...args) => invoke(capabilityName, args).status === 0
|
|
166
|
-
: capabilityName === 'isSecretFileTracked'
|
|
167
|
-
? (...args) => invoke(capabilityName, args).status === 0
|
|
168
|
-
: (...args) => {
|
|
169
|
-
const result = invoke(capabilityName, args);
|
|
170
|
-
return {
|
|
171
|
-
status: result.status,
|
|
172
|
-
stdout: typeof result.stdout === 'string' ? result.stdout : '',
|
|
173
|
-
};
|
|
174
|
-
};
|
|
175
|
-
Object.freeze(capability);
|
|
176
|
-
Object.defineProperty(adapter, capabilityName, {
|
|
177
|
-
value: capability,
|
|
178
|
-
enumerable: true,
|
|
179
|
-
writable: false,
|
|
180
|
-
configurable: false,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
Object.freeze(adapter);
|
|
184
|
-
return {
|
|
185
|
-
adapter,
|
|
186
|
-
revoke() {
|
|
187
|
-
revoked = true;
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function buildInvocation(capabilityName, gitDirectory, canonicalPath, environment) {
|
|
193
|
-
const templates = {
|
|
194
|
-
isSecretFileInsideGitRepository: ['git', ['rev-parse', '--is-inside-work-tree']],
|
|
195
|
-
isSecretFileIgnored: ['git', ['check-ignore', '--quiet', '--', '.env']],
|
|
196
|
-
isSecretFileTracked: ['git', ['ls-files', '--error-unmatch', '--', '.env']],
|
|
197
|
-
readCurrentIdentity: ['whoami', []],
|
|
198
|
-
readSecretFileAcl: ['icacls', [canonicalPath]],
|
|
199
|
-
};
|
|
200
|
-
const template = templates[capabilityName];
|
|
201
|
-
if (!template) throw prohibited();
|
|
202
|
-
return {
|
|
203
|
-
executable: template[0],
|
|
204
|
-
args: [...template[1]],
|
|
205
|
-
options: {
|
|
206
|
-
cwd: gitDirectory,
|
|
207
|
-
encoding: 'utf8',
|
|
208
|
-
env: { ...environment },
|
|
209
|
-
shell: false,
|
|
210
|
-
windowsHide: true,
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function validateInvocation(candidate, expected, forbiddenValues) {
|
|
216
|
-
if (!candidate || typeof candidate !== 'object') throw prohibited();
|
|
217
|
-
if (JSON.stringify(candidate) !== JSON.stringify(expected)) throw prohibited();
|
|
218
|
-
const serialized = JSON.stringify(candidate);
|
|
219
|
-
for (const forbiddenValue of forbiddenValues) {
|
|
220
|
-
if (typeof forbiddenValue === 'string' && forbiddenValue && serialized.includes(forbiddenValue)) {
|
|
221
|
-
throw prohibited();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function cloneInvocation(invocation) {
|
|
227
|
-
return {
|
|
228
|
-
executable: invocation.executable,
|
|
229
|
-
args: [...invocation.args],
|
|
230
|
-
options: cloneOptions(invocation.options),
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function cloneOptions(options) {
|
|
235
|
-
return {
|
|
236
|
-
cwd: options.cwd,
|
|
237
|
-
encoding: options.encoding,
|
|
238
|
-
env: { ...options.env },
|
|
239
|
-
shell: options.shell,
|
|
240
|
-
windowsHide: options.windowsHide,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function requireExactKeys(value, allowedKeys, optional = false) {
|
|
245
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) throw prohibited();
|
|
246
|
-
const actual = Object.keys(value).sort();
|
|
247
|
-
const allowed = [...allowedKeys].sort();
|
|
248
|
-
if (actual.some(key => !allowed.includes(key))) throw prohibited();
|
|
249
|
-
if (!optional && (actual.length !== allowed.length || actual.some((key, index) => key !== allowed[index]))) {
|
|
250
|
-
throw prohibited();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function requireRepositoryRoot(value) {
|
|
255
|
-
if (typeof value !== 'string' || value.trim() === '') throw prohibited();
|
|
256
|
-
return path.resolve(value);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function prohibited() {
|
|
260
|
-
const error = new Error('SYSTEM_METADATA_COMMAND_PROHIBITED');
|
|
261
|
-
error.category = 'SYSTEM_METADATA_COMMAND_PROHIBITED';
|
|
262
|
-
return error;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
module.exports = {
|
|
266
|
-
createReadinessAttestationMetadataAdapter,
|
|
267
|
-
createSystemMetadataCommandAdapter,
|
|
268
|
-
withSystemMetadataCommandTestComposition,
|
|
269
|
-
};
|
|
1
|
+
const { spawnSync } = require('node:child_process');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
getArgoEnvPath,
|
|
6
|
+
} = require('../argo-paths.js');
|
|
7
|
+
|
|
8
|
+
const CAPABILITY_NAMES = Object.freeze([
|
|
9
|
+
'isSecretFileIgnored',
|
|
10
|
+
'isSecretFileTracked',
|
|
11
|
+
'isSecretFileInsideGitRepository',
|
|
12
|
+
'readCurrentIdentity',
|
|
13
|
+
'readSecretFileAcl',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
function createSystemMetadataCommandAdapter(options = {}) {
|
|
17
|
+
requireExactKeys(options, ['repositoryRoot']);
|
|
18
|
+
const repositoryRoot = requireRepositoryRoot(options.repositoryRoot);
|
|
19
|
+
return createAdapter({
|
|
20
|
+
repositoryRoot,
|
|
21
|
+
executeMetadataCommand: spawnSync,
|
|
22
|
+
mutateInvocation: undefined,
|
|
23
|
+
forbiddenValues: [],
|
|
24
|
+
}).adapter;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createReadinessAttestationMetadataAdapter(options = {}) {
|
|
28
|
+
requireExactKeys(options, ['repositoryRoot']);
|
|
29
|
+
const repositoryRoot = requireRepositoryRoot(options.repositoryRoot);
|
|
30
|
+
const file = path.join(
|
|
31
|
+
options.repositoryRoot,
|
|
32
|
+
'.argo',
|
|
33
|
+
'temp',
|
|
34
|
+
'semantic-readiness-attestation.json',
|
|
35
|
+
);
|
|
36
|
+
const directory = path.dirname(file);
|
|
37
|
+
const environment = sanitizedEnvironment();
|
|
38
|
+
|
|
39
|
+
function spawnExact(executable, args) {
|
|
40
|
+
return spawnSync(executable, args, {
|
|
41
|
+
cwd: repositoryRoot,
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
env: { ...environment },
|
|
44
|
+
shell: false,
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
readCurrentIdentity: () => spawnExact('whoami', []),
|
|
51
|
+
readReadinessAttestationDirectoryAcl: () => spawnExact('icacls', [directory]),
|
|
52
|
+
readReadinessAttestationAcl: () => spawnExact('icacls', [file]),
|
|
53
|
+
readReadinessAttestationOwner: () => spawnExact('powershell.exe', [
|
|
54
|
+
'-NoProfile',
|
|
55
|
+
'-NonInteractive',
|
|
56
|
+
'-Command',
|
|
57
|
+
exactOwnerScript(file),
|
|
58
|
+
]),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function exactOwnerScript(file) {
|
|
63
|
+
const literalPath = String(file).replace(/'/g, "''");
|
|
64
|
+
return `(Get-Acl -LiteralPath '${literalPath}').Owner`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sanitizedEnvironment() {
|
|
68
|
+
return Object.freeze(Object.fromEntries(
|
|
69
|
+
['PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR']
|
|
70
|
+
.filter(key => typeof process.env[key] === 'string')
|
|
71
|
+
.map(key => [key, process.env[key]]),
|
|
72
|
+
));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function withSystemMetadataCommandTestComposition(options = {}, callback) {
|
|
76
|
+
requireExactKeys(options, [
|
|
77
|
+
'executeMetadataCommand',
|
|
78
|
+
'forbiddenValues',
|
|
79
|
+
'mutateInvocation',
|
|
80
|
+
'repositoryRoot',
|
|
81
|
+
], true);
|
|
82
|
+
if (typeof options.executeMetadataCommand !== 'function' || typeof callback !== 'function') {
|
|
83
|
+
throw prohibited();
|
|
84
|
+
}
|
|
85
|
+
const composition = createAdapter({
|
|
86
|
+
repositoryRoot: requireRepositoryRoot(options.repositoryRoot),
|
|
87
|
+
executeMetadataCommand: options.executeMetadataCommand,
|
|
88
|
+
mutateInvocation: options.mutateInvocation,
|
|
89
|
+
forbiddenValues: Array.isArray(options.forbiddenValues) ? [...options.forbiddenValues] : [],
|
|
90
|
+
});
|
|
91
|
+
let result;
|
|
92
|
+
let then;
|
|
93
|
+
let asyncRevocationOwner = false;
|
|
94
|
+
try {
|
|
95
|
+
result = callback(composition.adapter);
|
|
96
|
+
if (
|
|
97
|
+
result !== null
|
|
98
|
+
&& (typeof result === 'object' || typeof result === 'function')
|
|
99
|
+
) {
|
|
100
|
+
then = result.then;
|
|
101
|
+
}
|
|
102
|
+
if (typeof then === 'function') {
|
|
103
|
+
asyncRevocationOwner = true;
|
|
104
|
+
return assimilateThenable(result, then, composition.revoke);
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
} finally {
|
|
108
|
+
if (!asyncRevocationOwner) composition.revoke();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function assimilateThenable(value, then, revoke) {
|
|
113
|
+
try {
|
|
114
|
+
await new Promise((resolve, reject) => {
|
|
115
|
+
then.call(value, resolve, reject);
|
|
116
|
+
});
|
|
117
|
+
return undefined;
|
|
118
|
+
} finally {
|
|
119
|
+
revoke();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function createAdapter({
|
|
124
|
+
repositoryRoot,
|
|
125
|
+
executeMetadataCommand,
|
|
126
|
+
mutateInvocation,
|
|
127
|
+
forbiddenValues,
|
|
128
|
+
}) {
|
|
129
|
+
const canonicalPath = getArgoEnvPath();
|
|
130
|
+
const gitDirectory = path.dirname(canonicalPath);
|
|
131
|
+
const sanitizedEnvironment = Object.freeze(Object.fromEntries(
|
|
132
|
+
['PATH', 'PATHEXT', 'SystemRoot', 'WINDIR']
|
|
133
|
+
.filter(key => typeof process.env[key] === 'string')
|
|
134
|
+
.map(key => [key, process.env[key]]),
|
|
135
|
+
));
|
|
136
|
+
let revoked = false;
|
|
137
|
+
|
|
138
|
+
function invoke(capabilityName, receivedArguments) {
|
|
139
|
+
if (revoked) {
|
|
140
|
+
const error = new Error('TEST_SYSTEM_METADATA_ADAPTER_REVOKED');
|
|
141
|
+
error.category = 'TEST_SYSTEM_METADATA_ADAPTER_REVOKED';
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
if (receivedArguments.length !== 0) throw prohibited();
|
|
145
|
+
const expected = buildInvocation(
|
|
146
|
+
capabilityName,
|
|
147
|
+
gitDirectory,
|
|
148
|
+
canonicalPath,
|
|
149
|
+
sanitizedEnvironment,
|
|
150
|
+
);
|
|
151
|
+
const candidate = typeof mutateInvocation === 'function'
|
|
152
|
+
? mutateInvocation(cloneInvocation(expected))
|
|
153
|
+
: expected;
|
|
154
|
+
validateInvocation(candidate, expected, forbiddenValues);
|
|
155
|
+
return executeMetadataCommand(
|
|
156
|
+
candidate.executable,
|
|
157
|
+
[...candidate.args],
|
|
158
|
+
cloneOptions(candidate.options),
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const adapter = Object.create(null);
|
|
163
|
+
for (const capabilityName of CAPABILITY_NAMES) {
|
|
164
|
+
const capability = capabilityName === 'isSecretFileIgnored'
|
|
165
|
+
? (...args) => invoke(capabilityName, args).status === 0
|
|
166
|
+
: capabilityName === 'isSecretFileTracked'
|
|
167
|
+
? (...args) => invoke(capabilityName, args).status === 0
|
|
168
|
+
: (...args) => {
|
|
169
|
+
const result = invoke(capabilityName, args);
|
|
170
|
+
return {
|
|
171
|
+
status: result.status,
|
|
172
|
+
stdout: typeof result.stdout === 'string' ? result.stdout : '',
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
Object.freeze(capability);
|
|
176
|
+
Object.defineProperty(adapter, capabilityName, {
|
|
177
|
+
value: capability,
|
|
178
|
+
enumerable: true,
|
|
179
|
+
writable: false,
|
|
180
|
+
configurable: false,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
Object.freeze(adapter);
|
|
184
|
+
return {
|
|
185
|
+
adapter,
|
|
186
|
+
revoke() {
|
|
187
|
+
revoked = true;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function buildInvocation(capabilityName, gitDirectory, canonicalPath, environment) {
|
|
193
|
+
const templates = {
|
|
194
|
+
isSecretFileInsideGitRepository: ['git', ['rev-parse', '--is-inside-work-tree']],
|
|
195
|
+
isSecretFileIgnored: ['git', ['check-ignore', '--quiet', '--', '.env']],
|
|
196
|
+
isSecretFileTracked: ['git', ['ls-files', '--error-unmatch', '--', '.env']],
|
|
197
|
+
readCurrentIdentity: ['whoami', []],
|
|
198
|
+
readSecretFileAcl: ['icacls', [canonicalPath]],
|
|
199
|
+
};
|
|
200
|
+
const template = templates[capabilityName];
|
|
201
|
+
if (!template) throw prohibited();
|
|
202
|
+
return {
|
|
203
|
+
executable: template[0],
|
|
204
|
+
args: [...template[1]],
|
|
205
|
+
options: {
|
|
206
|
+
cwd: gitDirectory,
|
|
207
|
+
encoding: 'utf8',
|
|
208
|
+
env: { ...environment },
|
|
209
|
+
shell: false,
|
|
210
|
+
windowsHide: true,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function validateInvocation(candidate, expected, forbiddenValues) {
|
|
216
|
+
if (!candidate || typeof candidate !== 'object') throw prohibited();
|
|
217
|
+
if (JSON.stringify(candidate) !== JSON.stringify(expected)) throw prohibited();
|
|
218
|
+
const serialized = JSON.stringify(candidate);
|
|
219
|
+
for (const forbiddenValue of forbiddenValues) {
|
|
220
|
+
if (typeof forbiddenValue === 'string' && forbiddenValue && serialized.includes(forbiddenValue)) {
|
|
221
|
+
throw prohibited();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function cloneInvocation(invocation) {
|
|
227
|
+
return {
|
|
228
|
+
executable: invocation.executable,
|
|
229
|
+
args: [...invocation.args],
|
|
230
|
+
options: cloneOptions(invocation.options),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function cloneOptions(options) {
|
|
235
|
+
return {
|
|
236
|
+
cwd: options.cwd,
|
|
237
|
+
encoding: options.encoding,
|
|
238
|
+
env: { ...options.env },
|
|
239
|
+
shell: options.shell,
|
|
240
|
+
windowsHide: options.windowsHide,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function requireExactKeys(value, allowedKeys, optional = false) {
|
|
245
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw prohibited();
|
|
246
|
+
const actual = Object.keys(value).sort();
|
|
247
|
+
const allowed = [...allowedKeys].sort();
|
|
248
|
+
if (actual.some(key => !allowed.includes(key))) throw prohibited();
|
|
249
|
+
if (!optional && (actual.length !== allowed.length || actual.some((key, index) => key !== allowed[index]))) {
|
|
250
|
+
throw prohibited();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function requireRepositoryRoot(value) {
|
|
255
|
+
if (typeof value !== 'string' || value.trim() === '') throw prohibited();
|
|
256
|
+
return path.resolve(value);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function prohibited() {
|
|
260
|
+
const error = new Error('SYSTEM_METADATA_COMMAND_PROHIBITED');
|
|
261
|
+
error.category = 'SYSTEM_METADATA_COMMAND_PROHIBITED';
|
|
262
|
+
return error;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = {
|
|
266
|
+
createReadinessAttestationMetadataAdapter,
|
|
267
|
+
createSystemMetadataCommandAdapter,
|
|
268
|
+
withSystemMetadataCommandTestComposition,
|
|
269
|
+
};
|