pumuki 6.3.75 → 6.3.77
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.
|
@@ -233,6 +233,8 @@ type PolicyAsCodeContract = {
|
|
|
233
233
|
version: '1.0';
|
|
234
234
|
source: 'default' | 'skills.policy' | 'hard-mode';
|
|
235
235
|
signatures: Partial<Record<SkillsStage, string>> & Record<'PRE_COMMIT' | 'PRE_PUSH' | 'CI', string>;
|
|
236
|
+
strict?: Partial<Record<SkillsStage, boolean>> &
|
|
237
|
+
Record<'PRE_COMMIT' | 'PRE_PUSH' | 'CI', boolean>;
|
|
236
238
|
expires_at?: string;
|
|
237
239
|
};
|
|
238
240
|
|
|
@@ -272,6 +274,30 @@ const policyStrictModeFromEnv = (): boolean => {
|
|
|
272
274
|
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
273
275
|
};
|
|
274
276
|
|
|
277
|
+
const isBoolean = (value: unknown): value is boolean => {
|
|
278
|
+
return typeof value === 'boolean';
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const resolveContractStrictForStage = (
|
|
282
|
+
strictByStage: PolicyAsCodeContract['strict'],
|
|
283
|
+
stage: SkillsStage
|
|
284
|
+
): boolean | null => {
|
|
285
|
+
if (!strictByStage) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
if (stage === 'PRE_WRITE') {
|
|
289
|
+
return strictByStage.PRE_WRITE ?? strictByStage.PRE_COMMIT ?? null;
|
|
290
|
+
}
|
|
291
|
+
return strictByStage[stage] ?? null;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const resolvePolicyStrict = (strictByContract: boolean | null): boolean => {
|
|
295
|
+
if (typeof strictByContract === 'boolean') {
|
|
296
|
+
return strictByContract;
|
|
297
|
+
}
|
|
298
|
+
return policyStrictModeFromEnv();
|
|
299
|
+
};
|
|
300
|
+
|
|
275
301
|
const isPolicyAsCodeContract = (value: unknown): value is PolicyAsCodeContract => {
|
|
276
302
|
if (!isObject(value)) {
|
|
277
303
|
return false;
|
|
@@ -289,6 +315,16 @@ const isPolicyAsCodeContract = (value: unknown): value is PolicyAsCodeContract =
|
|
|
289
315
|
if (!isObject(value.signatures)) {
|
|
290
316
|
return false;
|
|
291
317
|
}
|
|
318
|
+
if (
|
|
319
|
+
typeof value.strict !== 'undefined' &&
|
|
320
|
+
(!isObject(value.strict)
|
|
321
|
+
|| (typeof value.strict.PRE_WRITE !== 'undefined' && !isBoolean(value.strict.PRE_WRITE))
|
|
322
|
+
|| !isBoolean(value.strict.PRE_COMMIT)
|
|
323
|
+
|| !isBoolean(value.strict.PRE_PUSH)
|
|
324
|
+
|| !isBoolean(value.strict.CI))
|
|
325
|
+
) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
292
328
|
if (typeof value.expires_at !== 'undefined' && !isIsoDateString(value.expires_at)) {
|
|
293
329
|
return false;
|
|
294
330
|
}
|
|
@@ -332,7 +368,7 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
332
368
|
policySource: string;
|
|
333
369
|
validation: NonNullable<ResolvedStagePolicy['trace']['validation']>;
|
|
334
370
|
} => {
|
|
335
|
-
const
|
|
371
|
+
const envStrict = policyStrictModeFromEnv();
|
|
336
372
|
const computedVersion = `policy-as-code/${params.source}@${POLICY_AS_CODE_VERSION}`;
|
|
337
373
|
const computedSignature = createPolicyAsCodeSignature({
|
|
338
374
|
stage: params.stage,
|
|
@@ -344,7 +380,7 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
344
380
|
const contractPath = join(params.repoRoot, POLICY_AS_CODE_CONTRACT_PATH);
|
|
345
381
|
|
|
346
382
|
if (!existsSync(contractPath)) {
|
|
347
|
-
if (
|
|
383
|
+
if (envStrict) {
|
|
348
384
|
return {
|
|
349
385
|
version: computedVersion,
|
|
350
386
|
signature: computedSignature,
|
|
@@ -354,7 +390,7 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
354
390
|
code: 'POLICY_AS_CODE_UNSIGNED',
|
|
355
391
|
message:
|
|
356
392
|
'Policy-as-code contract is missing; runtime policy metadata is unsigned.',
|
|
357
|
-
strict,
|
|
393
|
+
strict: envStrict,
|
|
358
394
|
},
|
|
359
395
|
};
|
|
360
396
|
}
|
|
@@ -367,7 +403,7 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
367
403
|
status: 'valid',
|
|
368
404
|
code: 'POLICY_AS_CODE_VALID',
|
|
369
405
|
message: 'Policy-as-code metadata generated from active runtime policy.',
|
|
370
|
-
strict,
|
|
406
|
+
strict: envStrict,
|
|
371
407
|
},
|
|
372
408
|
};
|
|
373
409
|
}
|
|
@@ -383,11 +419,13 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
383
419
|
status: 'invalid',
|
|
384
420
|
code: 'POLICY_AS_CODE_CONTRACT_INVALID',
|
|
385
421
|
message: 'Policy-as-code contract is malformed.',
|
|
386
|
-
strict,
|
|
422
|
+
strict: envStrict,
|
|
387
423
|
},
|
|
388
424
|
};
|
|
389
425
|
}
|
|
390
426
|
|
|
427
|
+
const strict = resolvePolicyStrict(resolveContractStrictForStage(raw.strict, params.stage));
|
|
428
|
+
|
|
391
429
|
if (raw.source !== params.source) {
|
|
392
430
|
return {
|
|
393
431
|
version: `policy-as-code/${raw.source}@${raw.version}`,
|
|
@@ -465,7 +503,7 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
465
503
|
status: 'invalid',
|
|
466
504
|
code: 'POLICY_AS_CODE_CONTRACT_INVALID',
|
|
467
505
|
message: 'Policy-as-code contract cannot be parsed as JSON.',
|
|
468
|
-
strict,
|
|
506
|
+
strict: envStrict,
|
|
469
507
|
},
|
|
470
508
|
};
|
|
471
509
|
}
|
|
@@ -103,19 +103,9 @@ const toContractSource = (
|
|
|
103
103
|
return 'default';
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
const
|
|
106
|
+
const writePolicyAsCodeContract = (params: {
|
|
107
107
|
report: Omit<PolicyReconcileReport, 'applyRequested' | 'autofix'>;
|
|
108
108
|
}): PolicyReconcileReport['autofix'] => {
|
|
109
|
-
const actionableDrifts = params.report.drifts.filter((drift) => POLICY_AUTOFIX_DRIFT_CODES.has(drift.code));
|
|
110
|
-
if (actionableDrifts.length === 0) {
|
|
111
|
-
return {
|
|
112
|
-
attempted: false,
|
|
113
|
-
status: 'SKIPPED',
|
|
114
|
-
actions: [],
|
|
115
|
-
details: 'No policy-as-code drift eligible for autofix.',
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
109
|
const preWriteStage = params.report.stages.PRE_WRITE;
|
|
120
110
|
const signatures = {
|
|
121
111
|
PRE_WRITE: createPolicyAsCodeSignature({
|
|
@@ -134,7 +124,7 @@ const tryApplyPolicyAutofix = (params: {
|
|
|
134
124
|
attempted: true,
|
|
135
125
|
status: 'FAILED',
|
|
136
126
|
actions: [],
|
|
137
|
-
details: 'Cannot
|
|
127
|
+
details: 'Cannot write policy-as-code contract: missing computed signatures for one or more stages.',
|
|
138
128
|
};
|
|
139
129
|
}
|
|
140
130
|
|
|
@@ -142,6 +132,12 @@ const tryApplyPolicyAutofix = (params: {
|
|
|
142
132
|
const contract = {
|
|
143
133
|
version: '1.0',
|
|
144
134
|
source: toContractSource(params.report.stages.PRE_WRITE.source),
|
|
135
|
+
strict: {
|
|
136
|
+
PRE_WRITE: params.report.stages.PRE_WRITE.strict,
|
|
137
|
+
PRE_COMMIT: params.report.stages.PRE_COMMIT.strict,
|
|
138
|
+
PRE_PUSH: params.report.stages.PRE_PUSH.strict,
|
|
139
|
+
CI: params.report.stages.CI.strict,
|
|
140
|
+
},
|
|
145
141
|
signatures: {
|
|
146
142
|
PRE_WRITE: signatures.PRE_WRITE,
|
|
147
143
|
PRE_COMMIT: signatures.PRE_COMMIT,
|
|
@@ -158,7 +154,7 @@ const tryApplyPolicyAutofix = (params: {
|
|
|
158
154
|
attempted: true,
|
|
159
155
|
status: 'APPLIED',
|
|
160
156
|
actions: ['WRITE_POLICY_AS_CODE_CONTRACT'],
|
|
161
|
-
details: `Wrote ${POLICY_AS_CODE_CONTRACT_PATH} with deterministic stage signatures.`,
|
|
157
|
+
details: `Wrote ${POLICY_AS_CODE_CONTRACT_PATH} with deterministic stage signatures and strict flags.`,
|
|
162
158
|
};
|
|
163
159
|
} catch (error) {
|
|
164
160
|
return {
|
|
@@ -170,6 +166,22 @@ const tryApplyPolicyAutofix = (params: {
|
|
|
170
166
|
}
|
|
171
167
|
};
|
|
172
168
|
|
|
169
|
+
const tryApplyPolicyAutofix = (params: {
|
|
170
|
+
report: Omit<PolicyReconcileReport, 'applyRequested' | 'autofix'>;
|
|
171
|
+
}): PolicyReconcileReport['autofix'] => {
|
|
172
|
+
const actionableDrifts = params.report.drifts.filter((drift) => POLICY_AUTOFIX_DRIFT_CODES.has(drift.code));
|
|
173
|
+
if (actionableDrifts.length === 0) {
|
|
174
|
+
return {
|
|
175
|
+
attempted: false,
|
|
176
|
+
status: 'SKIPPED',
|
|
177
|
+
actions: [],
|
|
178
|
+
details: 'No policy-as-code drift eligible for autofix.',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return writePolicyAsCodeContract(params);
|
|
183
|
+
};
|
|
184
|
+
|
|
173
185
|
export const runPolicyReconcile = (params?: {
|
|
174
186
|
repoRoot?: string;
|
|
175
187
|
now?: () => Date;
|
|
@@ -412,6 +424,31 @@ export const runPolicyReconcile = (params?: {
|
|
|
412
424
|
};
|
|
413
425
|
}
|
|
414
426
|
|
|
427
|
+
if (strictRequested && baseReport.summary.status === 'PASS') {
|
|
428
|
+
const strictPersistence = writePolicyAsCodeContract({
|
|
429
|
+
report: baseReport,
|
|
430
|
+
});
|
|
431
|
+
if (strictPersistence.status !== 'APPLIED') {
|
|
432
|
+
return {
|
|
433
|
+
...baseReport,
|
|
434
|
+
applyRequested: true,
|
|
435
|
+
autofix: strictPersistence,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const reevaluated = runPolicyReconcile({
|
|
440
|
+
repoRoot,
|
|
441
|
+
now,
|
|
442
|
+
strict: strictRequested,
|
|
443
|
+
apply: false,
|
|
444
|
+
});
|
|
445
|
+
return {
|
|
446
|
+
...reevaluated,
|
|
447
|
+
applyRequested: true,
|
|
448
|
+
autofix: strictPersistence,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
415
452
|
const autofix = tryApplyPolicyAutofix({
|
|
416
453
|
report: baseReport,
|
|
417
454
|
});
|
|
@@ -39,6 +39,14 @@ type EnterpriseStatusPayload = {
|
|
|
39
39
|
evidence: ReturnType<typeof toStatusPayload>;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
type EnterpriseHealthPayload = {
|
|
43
|
+
status: 'ok';
|
|
44
|
+
repoRoot: string;
|
|
45
|
+
experimentalFeatures: {
|
|
46
|
+
mcp_enterprise: ReturnType<typeof resolveMcpEnterpriseExperimentalFeature>;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
42
50
|
const ENTERPRISE_RESOURCES = [
|
|
43
51
|
'evidence://status',
|
|
44
52
|
'gitflow://state',
|
|
@@ -650,6 +658,14 @@ const buildStatusPayload = (repoRoot: string): EnterpriseStatusPayload => ({
|
|
|
650
658
|
evidence: toStatusPayload(repoRoot),
|
|
651
659
|
});
|
|
652
660
|
|
|
661
|
+
const buildHealthPayload = (repoRoot: string): EnterpriseHealthPayload => ({
|
|
662
|
+
status: 'ok',
|
|
663
|
+
repoRoot,
|
|
664
|
+
experimentalFeatures: {
|
|
665
|
+
mcp_enterprise: readMcpEnterpriseExperimentalState(),
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
|
|
653
669
|
export const startEnterpriseMcpServer = (
|
|
654
670
|
options: EnterpriseServerOptions = {}
|
|
655
671
|
): EnterpriseServerHandle => {
|
|
@@ -680,7 +696,7 @@ export const startEnterpriseMcpServer = (
|
|
|
680
696
|
sendJson(res, 405, { error: 'Method not allowed' });
|
|
681
697
|
return;
|
|
682
698
|
}
|
|
683
|
-
sendJson(res, 200,
|
|
699
|
+
sendJson(res, 200, buildHealthPayload(repoRoot));
|
|
684
700
|
return;
|
|
685
701
|
}
|
|
686
702
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Socket, createServer } from 'node:net';
|
|
2
2
|
import { startEnterpriseMcpServer } from './enterpriseServer';
|
|
3
|
+
import { resolveMcpEnterpriseExperimentalFeature } from '../policy/experimentalFeatures';
|
|
3
4
|
|
|
4
5
|
type JsonRpcId = string | number | null;
|
|
5
6
|
|
|
@@ -98,11 +99,32 @@ const fetchJson = async (url: string, options?: RequestInit): Promise<unknown> =
|
|
|
98
99
|
}
|
|
99
100
|
};
|
|
100
101
|
|
|
102
|
+
type EnterpriseHealthPayload = {
|
|
103
|
+
status?: string;
|
|
104
|
+
repoRoot?: string;
|
|
105
|
+
experimentalFeatures?: {
|
|
106
|
+
mcp_enterprise?: {
|
|
107
|
+
mode?: string;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const canReuseEnterpriseHttp = (
|
|
113
|
+
health: EnterpriseHealthPayload,
|
|
114
|
+
repoRoot: string,
|
|
115
|
+
expectedMcpMode: string
|
|
116
|
+
): boolean =>
|
|
117
|
+
health.status === 'ok'
|
|
118
|
+
&& health.repoRoot === repoRoot
|
|
119
|
+
&& health.experimentalFeatures?.mcp_enterprise?.mode === expectedMcpMode;
|
|
120
|
+
|
|
101
121
|
const startOrReuseEnterpriseHttp = async (): Promise<{
|
|
102
122
|
host: string;
|
|
103
123
|
port: number;
|
|
104
124
|
startedByThisProcess: boolean;
|
|
105
125
|
}> => {
|
|
126
|
+
const repoRoot = process.cwd();
|
|
127
|
+
const expectedMcpMode = resolveMcpEnterpriseExperimentalFeature().mode;
|
|
106
128
|
const host = process.env.PUMUKI_ENTERPRISE_MCP_HOST ?? '127.0.0.1';
|
|
107
129
|
const parsedPort = Number.parseInt(process.env.PUMUKI_ENTERPRISE_MCP_PORT ?? '', 10);
|
|
108
130
|
const preferredPort = Number.isFinite(parsedPort) ? parsedPort : 7391;
|
|
@@ -110,16 +132,21 @@ const startOrReuseEnterpriseHttp = async (): Promise<{
|
|
|
110
132
|
|
|
111
133
|
const healthUrl = `http://${host}:${requestedPort}/health`;
|
|
112
134
|
try {
|
|
113
|
-
const health = (await fetchJson(healthUrl)) as
|
|
114
|
-
if (health
|
|
135
|
+
const health = (await fetchJson(healthUrl)) as EnterpriseHealthPayload;
|
|
136
|
+
if (canReuseEnterpriseHttp(health, repoRoot, expectedMcpMode)) {
|
|
115
137
|
return {
|
|
116
138
|
host,
|
|
117
139
|
port: requestedPort,
|
|
118
140
|
startedByThisProcess: false,
|
|
119
141
|
};
|
|
120
142
|
}
|
|
121
|
-
} catch {
|
|
122
|
-
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (process.env.PUMUKI_DEBUG_MCP === '1') {
|
|
145
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
146
|
+
process.stderr.write(
|
|
147
|
+
`[pumuki-mcp-enterprise-stdio] health probe reuse skipped: ${message}\n`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
123
150
|
}
|
|
124
151
|
|
|
125
152
|
const portInUse = await isPortInUse(host, requestedPort);
|
|
@@ -127,7 +154,7 @@ const startOrReuseEnterpriseHttp = async (): Promise<{
|
|
|
127
154
|
startEnterpriseMcpServer({
|
|
128
155
|
host,
|
|
129
156
|
port: resolvedPort,
|
|
130
|
-
repoRoot
|
|
157
|
+
repoRoot,
|
|
131
158
|
});
|
|
132
159
|
|
|
133
160
|
return {
|
|
@@ -30,7 +30,10 @@ export type PolicyAsCodeTraceMetadata = {
|
|
|
30
30
|
type PolicyAsCodeContract = {
|
|
31
31
|
version: '1.0';
|
|
32
32
|
source: PolicyProfileSource;
|
|
33
|
-
signatures: Record<SkillsStage, string
|
|
33
|
+
signatures: Partial<Record<SkillsStage, string>> &
|
|
34
|
+
Record<'PRE_COMMIT' | 'PRE_PUSH' | 'CI', string>;
|
|
35
|
+
strict?: Partial<Record<SkillsStage, boolean>> &
|
|
36
|
+
Record<'PRE_COMMIT' | 'PRE_PUSH' | 'CI', boolean>;
|
|
34
37
|
expires_at?: string;
|
|
35
38
|
};
|
|
36
39
|
|
|
@@ -57,6 +60,40 @@ const policyStrictModeFromEnv = (): boolean => {
|
|
|
57
60
|
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
58
61
|
};
|
|
59
62
|
|
|
63
|
+
const isBoolean = (value: unknown): value is boolean => {
|
|
64
|
+
return typeof value === 'boolean';
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const resolveContractSignatureForStage = (
|
|
68
|
+
signatures: PolicyAsCodeContract['signatures'],
|
|
69
|
+
stage: SkillsStage
|
|
70
|
+
): string | undefined => {
|
|
71
|
+
if (stage === 'PRE_WRITE') {
|
|
72
|
+
return signatures.PRE_WRITE ?? signatures.PRE_COMMIT;
|
|
73
|
+
}
|
|
74
|
+
return signatures[stage];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const resolveContractStrictForStage = (
|
|
78
|
+
strictByStage: PolicyAsCodeContract['strict'],
|
|
79
|
+
stage: SkillsStage
|
|
80
|
+
): boolean | null => {
|
|
81
|
+
if (!strictByStage) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (stage === 'PRE_WRITE') {
|
|
85
|
+
return strictByStage.PRE_WRITE ?? strictByStage.PRE_COMMIT ?? null;
|
|
86
|
+
}
|
|
87
|
+
return strictByStage[stage] ?? null;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const resolvePolicyStrict = (strictByContract: boolean | null): boolean => {
|
|
91
|
+
if (typeof strictByContract === 'boolean') {
|
|
92
|
+
return strictByContract;
|
|
93
|
+
}
|
|
94
|
+
return policyStrictModeFromEnv();
|
|
95
|
+
};
|
|
96
|
+
|
|
60
97
|
const isPolicyAsCodeContract = (value: unknown): value is PolicyAsCodeContract => {
|
|
61
98
|
if (!isObject(value)) {
|
|
62
99
|
return false;
|
|
@@ -74,10 +111,21 @@ const isPolicyAsCodeContract = (value: unknown): value is PolicyAsCodeContract =
|
|
|
74
111
|
if (!isObject(value.signatures)) {
|
|
75
112
|
return false;
|
|
76
113
|
}
|
|
114
|
+
if (
|
|
115
|
+
typeof value.strict !== 'undefined' &&
|
|
116
|
+
(!isObject(value.strict)
|
|
117
|
+
|| (typeof value.strict.PRE_WRITE !== 'undefined' && !isBoolean(value.strict.PRE_WRITE))
|
|
118
|
+
|| !isBoolean(value.strict.PRE_COMMIT)
|
|
119
|
+
|| !isBoolean(value.strict.PRE_PUSH)
|
|
120
|
+
|| !isBoolean(value.strict.CI))
|
|
121
|
+
) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
77
124
|
if (typeof value.expires_at !== 'undefined' && !isIsoDateString(value.expires_at)) {
|
|
78
125
|
return false;
|
|
79
126
|
}
|
|
80
127
|
return (
|
|
128
|
+
(typeof value.signatures.PRE_WRITE === 'undefined' || isSha256Hex(value.signatures.PRE_WRITE)) &&
|
|
81
129
|
isSha256Hex(value.signatures.PRE_COMMIT) &&
|
|
82
130
|
isSha256Hex(value.signatures.PRE_PUSH) &&
|
|
83
131
|
isSha256Hex(value.signatures.CI)
|
|
@@ -111,7 +159,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
111
159
|
hash: string;
|
|
112
160
|
repoRoot: string;
|
|
113
161
|
}): PolicyAsCodeTraceMetadata => {
|
|
114
|
-
const
|
|
162
|
+
const envStrict = policyStrictModeFromEnv();
|
|
115
163
|
const computedVersion = `policy-as-code/${params.source}@${POLICY_AS_CODE_VERSION}`;
|
|
116
164
|
const computedSignature = createPolicyAsCodeSignature({
|
|
117
165
|
stage: params.stage,
|
|
@@ -123,7 +171,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
123
171
|
const contractPath = join(params.repoRoot, POLICY_AS_CODE_CONTRACT_PATH);
|
|
124
172
|
|
|
125
173
|
if (!existsSync(contractPath)) {
|
|
126
|
-
if (
|
|
174
|
+
if (envStrict) {
|
|
127
175
|
return {
|
|
128
176
|
version: computedVersion,
|
|
129
177
|
signature: computedSignature,
|
|
@@ -133,7 +181,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
133
181
|
code: 'POLICY_AS_CODE_UNSIGNED',
|
|
134
182
|
message:
|
|
135
183
|
'Policy-as-code contract is missing; runtime policy metadata is unsigned.',
|
|
136
|
-
strict,
|
|
184
|
+
strict: envStrict,
|
|
137
185
|
},
|
|
138
186
|
};
|
|
139
187
|
}
|
|
@@ -146,7 +194,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
146
194
|
status: 'valid',
|
|
147
195
|
code: 'POLICY_AS_CODE_VALID',
|
|
148
196
|
message: 'Policy-as-code metadata generated from active runtime policy.',
|
|
149
|
-
strict,
|
|
197
|
+
strict: envStrict,
|
|
150
198
|
},
|
|
151
199
|
};
|
|
152
200
|
}
|
|
@@ -162,15 +210,17 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
162
210
|
status: 'invalid',
|
|
163
211
|
code: 'POLICY_AS_CODE_CONTRACT_INVALID',
|
|
164
212
|
message: 'Policy-as-code contract is malformed.',
|
|
165
|
-
strict,
|
|
213
|
+
strict: envStrict,
|
|
166
214
|
},
|
|
167
215
|
};
|
|
168
216
|
}
|
|
169
217
|
|
|
218
|
+
const strict = resolvePolicyStrict(resolveContractStrictForStage(raw.strict, params.stage));
|
|
219
|
+
|
|
170
220
|
if (raw.source !== params.source) {
|
|
171
221
|
return {
|
|
172
222
|
version: `policy-as-code/${raw.source}@${raw.version}`,
|
|
173
|
-
signature: raw.signatures
|
|
223
|
+
signature: resolveContractSignatureForStage(raw.signatures, params.stage) ?? computedSignature,
|
|
174
224
|
policySource: `file:${POLICY_AS_CODE_CONTRACT_PATH}`,
|
|
175
225
|
validation: {
|
|
176
226
|
status: 'unknown-source',
|
|
@@ -183,18 +233,18 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
183
233
|
}
|
|
184
234
|
|
|
185
235
|
const expectedSignature = createPolicyAsCodeSignature({
|
|
186
|
-
stage: params.stage,
|
|
236
|
+
stage: params.stage === 'PRE_WRITE' ? 'PRE_COMMIT' : params.stage,
|
|
187
237
|
source: params.source,
|
|
188
238
|
bundle: params.bundle,
|
|
189
239
|
hash: params.hash,
|
|
190
240
|
version: raw.version,
|
|
191
241
|
});
|
|
192
|
-
const stageSignature = raw.signatures
|
|
242
|
+
const stageSignature = resolveContractSignatureForStage(raw.signatures, params.stage);
|
|
193
243
|
|
|
194
244
|
if (stageSignature !== expectedSignature) {
|
|
195
245
|
return {
|
|
196
246
|
version: `policy-as-code/${raw.source}@${raw.version}`,
|
|
197
|
-
signature: stageSignature,
|
|
247
|
+
signature: stageSignature ?? computedSignature,
|
|
198
248
|
policySource: `file:${POLICY_AS_CODE_CONTRACT_PATH}`,
|
|
199
249
|
validation: {
|
|
200
250
|
status: 'invalid',
|
|
@@ -211,7 +261,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
211
261
|
if (Number.isFinite(expiresAtTimestamp) && Date.now() >= expiresAtTimestamp) {
|
|
212
262
|
return {
|
|
213
263
|
version: `policy-as-code/${raw.source}@${raw.version}`,
|
|
214
|
-
signature: stageSignature,
|
|
264
|
+
signature: stageSignature ?? computedSignature,
|
|
215
265
|
policySource: `file:${POLICY_AS_CODE_CONTRACT_PATH}`,
|
|
216
266
|
validation: {
|
|
217
267
|
status: 'expired',
|
|
@@ -225,7 +275,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
225
275
|
|
|
226
276
|
return {
|
|
227
277
|
version: `policy-as-code/${raw.source}@${raw.version}`,
|
|
228
|
-
signature: stageSignature,
|
|
278
|
+
signature: stageSignature ?? computedSignature,
|
|
229
279
|
policySource: `file:${POLICY_AS_CODE_CONTRACT_PATH}`,
|
|
230
280
|
validation: {
|
|
231
281
|
status: 'valid',
|
|
@@ -243,7 +293,7 @@ export const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
243
293
|
status: 'invalid',
|
|
244
294
|
code: 'POLICY_AS_CODE_CONTRACT_INVALID',
|
|
245
295
|
message: 'Policy-as-code contract cannot be parsed as JSON.',
|
|
246
|
-
strict,
|
|
296
|
+
strict: envStrict,
|
|
247
297
|
},
|
|
248
298
|
};
|
|
249
299
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.77",
|
|
4
4
|
"description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|