pumuki 6.3.231 → 6.3.233
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/core/gate/evaluateGate.test.ts +30 -0
- package/core/gate/evaluateRules.test.ts +40 -9
- package/core/gate/evaluateRules.ts +1 -2
- package/integrations/gate/stagePolicies.ts +20 -11
- package/integrations/lifecycle/audit.ts +1 -4
- package/integrations/policy/policyProfiles.ts +22 -11
- package/package.json +1 -1
- package/scripts/framework-menu-system-notifications-payloads-audit.ts +4 -4
|
@@ -75,3 +75,33 @@ test('evaluateGate devuelve BLOCK cuando existe al menos un finding bloqueante',
|
|
|
75
75
|
assert.equal(result.warnings.length, 1);
|
|
76
76
|
assert.equal(result.warnings[0]?.ruleId, 'rule.warn');
|
|
77
77
|
});
|
|
78
|
+
|
|
79
|
+
test('evaluateGate bloquea cualquier severidad cuando la policy zero-violations usa INFO', () => {
|
|
80
|
+
const findings: Finding[] = [
|
|
81
|
+
{
|
|
82
|
+
ruleId: 'rule.info',
|
|
83
|
+
severity: 'INFO',
|
|
84
|
+
code: 'RULE_INFO',
|
|
85
|
+
message: 'Info finding',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
ruleId: 'rule.warn',
|
|
89
|
+
severity: 'WARN',
|
|
90
|
+
code: 'RULE_WARN',
|
|
91
|
+
message: 'Warn finding',
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const result = evaluateGate(findings, {
|
|
96
|
+
stage: 'PRE_PUSH',
|
|
97
|
+
blockOnOrAbove: 'INFO',
|
|
98
|
+
warnOnOrAbove: 'INFO',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
assert.equal(result.outcome, 'BLOCK');
|
|
102
|
+
assert.deepEqual(
|
|
103
|
+
result.blocking.map((finding) => finding.ruleId),
|
|
104
|
+
['rule.info', 'rule.warn']
|
|
105
|
+
);
|
|
106
|
+
assert.deepEqual(result.warnings, []);
|
|
107
|
+
});
|
|
@@ -32,15 +32,46 @@ test('evaluateRules genera finding cuando la condicion coincide y usa code expli
|
|
|
32
32
|
const findings = evaluateRules(rules, facts);
|
|
33
33
|
|
|
34
34
|
assert.equal(findings.length, 1);
|
|
35
|
-
assert.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
assert.equal(findings[0]?.ruleId, 'rule.explicit.code');
|
|
36
|
+
assert.equal(findings[0]?.severity, 'WARN');
|
|
37
|
+
assert.equal(findings[0]?.code, 'BACKEND_FILE_MODIFIED');
|
|
38
|
+
assert.equal(findings[0]?.message, 'Backend file modified.');
|
|
39
|
+
assert.equal(findings[0]?.filePath, 'apps/backend/src/main.ts');
|
|
40
|
+
assert.equal(findings[0]?.matchedBy, 'FileChange');
|
|
41
|
+
assert.equal(findings[0]?.source, 'git');
|
|
42
|
+
assert.equal(findings[0]?.blocking, true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('evaluateRules marca cualquier severity como bloqueante bajo contrato zero-violations', () => {
|
|
46
|
+
const rules: RuleSet = [
|
|
47
|
+
{
|
|
48
|
+
id: 'rule.info.blocking',
|
|
49
|
+
description: 'Info still blocks in enterprise zero-violations mode.',
|
|
50
|
+
severity: 'INFO',
|
|
51
|
+
when: {
|
|
52
|
+
kind: 'FileChange',
|
|
53
|
+
where: { pathPrefix: 'apps/frontend/', changeType: 'modified' },
|
|
54
|
+
},
|
|
55
|
+
then: {
|
|
56
|
+
kind: 'Finding',
|
|
57
|
+
message: 'Any finding blocks.',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
const facts = [
|
|
62
|
+
{
|
|
63
|
+
kind: 'FileChange',
|
|
64
|
+
path: 'apps/frontend/src/App.tsx',
|
|
65
|
+
changeType: 'modified',
|
|
66
|
+
source: 'git',
|
|
67
|
+
},
|
|
68
|
+
] as const;
|
|
69
|
+
|
|
70
|
+
const findings = evaluateRules(rules, facts);
|
|
71
|
+
|
|
72
|
+
assert.equal(findings.length, 1);
|
|
73
|
+
assert.equal(findings[0]?.severity, 'INFO');
|
|
74
|
+
assert.equal(findings[0]?.blocking, true);
|
|
44
75
|
});
|
|
45
76
|
|
|
46
77
|
test('evaluateRules usa id de la regla como code cuando no se define en consecuencia', () => {
|
|
@@ -23,8 +23,7 @@ type FindingTarget = {
|
|
|
23
23
|
expected_fix?: string;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const isBlockingSeverity = (
|
|
27
|
-
severity === 'CRITICAL' || severity === 'ERROR';
|
|
26
|
+
const isBlockingSeverity = (_severity: RuleDefinition['severity']): boolean => true;
|
|
28
27
|
|
|
29
28
|
export type EvaluateRulesCoverageResult = {
|
|
30
29
|
findings: ReadonlyArray<Finding>;
|
|
@@ -124,26 +124,35 @@ const toGatePolicyRecordFromEnterpriseThresholds = (
|
|
|
124
124
|
};
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
+
const ZERO_VIOLATION_BLOCK_ON_OR_ABOVE: GatePolicy['blockOnOrAbove'] = 'INFO';
|
|
128
|
+
const ZERO_VIOLATION_WARN_ON_OR_ABOVE: GatePolicy['warnOnOrAbove'] = 'INFO';
|
|
129
|
+
|
|
130
|
+
const enforceZeroViolationPolicy = (policy: GatePolicy): GatePolicy => ({
|
|
131
|
+
...policy,
|
|
132
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
133
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
134
|
+
});
|
|
135
|
+
|
|
127
136
|
const defaultPolicyByStage: Record<SkillsStage, GatePolicy> = {
|
|
128
137
|
PRE_WRITE: {
|
|
129
138
|
stage: 'PRE_WRITE',
|
|
130
|
-
blockOnOrAbove:
|
|
131
|
-
warnOnOrAbove:
|
|
139
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
140
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
132
141
|
},
|
|
133
142
|
PRE_COMMIT: {
|
|
134
143
|
stage: 'PRE_COMMIT',
|
|
135
|
-
blockOnOrAbove:
|
|
136
|
-
warnOnOrAbove:
|
|
144
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
145
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
137
146
|
},
|
|
138
147
|
PRE_PUSH: {
|
|
139
148
|
stage: 'PRE_PUSH',
|
|
140
|
-
blockOnOrAbove:
|
|
141
|
-
warnOnOrAbove:
|
|
149
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
150
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
142
151
|
},
|
|
143
152
|
CI: {
|
|
144
153
|
stage: 'CI',
|
|
145
|
-
blockOnOrAbove:
|
|
146
|
-
warnOnOrAbove:
|
|
154
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
155
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
147
156
|
},
|
|
148
157
|
};
|
|
149
158
|
|
|
@@ -624,7 +633,7 @@ export const resolvePolicyForStage = (
|
|
|
624
633
|
const profilePolicy = profileName
|
|
625
634
|
? hardModePolicyProfileByStage[profileName][stage]
|
|
626
635
|
: null;
|
|
627
|
-
const hardModePolicy = profilePolicy ?? hardModePolicyByStage[stage];
|
|
636
|
+
const hardModePolicy = enforceZeroViolationPolicy(profilePolicy ?? hardModePolicyByStage[stage]);
|
|
628
637
|
const bundle = profileName
|
|
629
638
|
? `gate-policy.hard-mode.${profileName}.${stage}`
|
|
630
639
|
: `gate-policy.hard-mode.${stage}`;
|
|
@@ -693,8 +702,8 @@ export const resolvePolicyForStage = (
|
|
|
693
702
|
|
|
694
703
|
const resolvedPolicy: GatePolicy = {
|
|
695
704
|
stage: defaults.stage,
|
|
696
|
-
blockOnOrAbove:
|
|
697
|
-
warnOnOrAbove:
|
|
705
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
706
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
698
707
|
};
|
|
699
708
|
|
|
700
709
|
const bundle = `gate-policy.skills.policy.${stage}`;
|
|
@@ -256,10 +256,7 @@ const toResultScope = (params: {
|
|
|
256
256
|
};
|
|
257
257
|
|
|
258
258
|
const isFindingBlocking = (finding: SnapshotFinding): boolean => {
|
|
259
|
-
|
|
260
|
-
return finding.blocking;
|
|
261
|
-
}
|
|
262
|
-
return finding.severity === 'CRITICAL' || finding.severity === 'ERROR';
|
|
259
|
+
return Boolean(finding.ruleId);
|
|
263
260
|
};
|
|
264
261
|
|
|
265
262
|
const toLifecycleAuditFinding = (finding: SnapshotFinding): LifecycleAuditFinding => ({
|
|
@@ -85,26 +85,37 @@ const toGatePolicyRecordFromEnterpriseThresholds = (
|
|
|
85
85
|
};
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
const ZERO_VIOLATION_BLOCK_ON_OR_ABOVE: GatePolicy['blockOnOrAbove'] = 'INFO';
|
|
89
|
+
const ZERO_VIOLATION_WARN_ON_OR_ABOVE: GatePolicy['warnOnOrAbove'] = 'INFO';
|
|
90
|
+
|
|
91
|
+
const enforceZeroViolationPolicy = (policy: GatePolicy): GatePolicy => {
|
|
92
|
+
return {
|
|
93
|
+
...policy,
|
|
94
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
95
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
88
99
|
const defaultPolicyByStage: Record<SkillsStage, GatePolicy> = {
|
|
89
100
|
PRE_WRITE: {
|
|
90
101
|
stage: 'PRE_WRITE',
|
|
91
|
-
blockOnOrAbove:
|
|
92
|
-
warnOnOrAbove:
|
|
102
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
103
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
93
104
|
},
|
|
94
105
|
PRE_COMMIT: {
|
|
95
106
|
stage: 'PRE_COMMIT',
|
|
96
|
-
blockOnOrAbove:
|
|
97
|
-
warnOnOrAbove:
|
|
107
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
108
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
98
109
|
},
|
|
99
110
|
PRE_PUSH: {
|
|
100
111
|
stage: 'PRE_PUSH',
|
|
101
|
-
blockOnOrAbove:
|
|
102
|
-
warnOnOrAbove:
|
|
112
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
113
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
103
114
|
},
|
|
104
115
|
CI: {
|
|
105
116
|
stage: 'CI',
|
|
106
|
-
blockOnOrAbove:
|
|
107
|
-
warnOnOrAbove:
|
|
117
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
118
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
108
119
|
},
|
|
109
120
|
};
|
|
110
121
|
|
|
@@ -374,7 +385,7 @@ export const resolveExplicitPolicyProfileForStage = (
|
|
|
374
385
|
const profilePolicy = profileName
|
|
375
386
|
? hardModePolicyProfileByStage[profileName][stage]
|
|
376
387
|
: null;
|
|
377
|
-
const hardModePolicy = profilePolicy ?? hardModePolicyByStage[stage];
|
|
388
|
+
const hardModePolicy = enforceZeroViolationPolicy(profilePolicy ?? hardModePolicyByStage[stage]);
|
|
378
389
|
return {
|
|
379
390
|
policy: hardModePolicy,
|
|
380
391
|
source: 'hard-mode',
|
|
@@ -394,8 +405,8 @@ export const resolveExplicitPolicyProfileForStage = (
|
|
|
394
405
|
return {
|
|
395
406
|
policy: {
|
|
396
407
|
stage: defaults.stage,
|
|
397
|
-
blockOnOrAbove:
|
|
398
|
-
warnOnOrAbove:
|
|
408
|
+
blockOnOrAbove: ZERO_VIOLATION_BLOCK_ON_OR_ABOVE,
|
|
409
|
+
warnOnOrAbove: ZERO_VIOLATION_WARN_ON_OR_ABOVE,
|
|
399
410
|
},
|
|
400
411
|
source: 'skills.policy',
|
|
401
412
|
layer: 'policy-pack',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.233",
|
|
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": {
|
|
@@ -8,20 +8,20 @@ export const buildAuditSummaryPayload = (
|
|
|
8
8
|
): SystemNotificationPayload => {
|
|
9
9
|
if (event.criticalViolations > 0) {
|
|
10
10
|
return {
|
|
11
|
-
title: 'AST Audit
|
|
11
|
+
title: 'AST Audit Blocked',
|
|
12
12
|
message: `🔴 ${event.criticalViolations} CRITICAL, ${event.highViolations} HIGH violations`,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
if (event.highViolations > 0) {
|
|
16
16
|
return {
|
|
17
|
-
title: 'AST Audit
|
|
17
|
+
title: 'AST Audit Blocked',
|
|
18
18
|
message: `🟡 ${event.highViolations} HIGH violations found`,
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
if (event.totalViolations > 0) {
|
|
22
22
|
return {
|
|
23
|
-
title: 'AST Audit
|
|
24
|
-
message:
|
|
23
|
+
title: 'AST Audit Blocked',
|
|
24
|
+
message: `🔴 ${event.totalViolations} violations block the gate`,
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
return {
|