pumuki 6.3.231 → 6.3.232
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.
|
@@ -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>;
|
|
@@ -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.232",
|
|
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 {
|