@boyingliu01/xp-gate 0.18.5 → 0.19.0
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/adapter-common.sh +27 -0
- package/adapters/gate-3.sh +0 -0
- package/adapters/gate-4.sh +0 -0
- package/adapters/gate-7.sh +0 -0
- package/adapters/gate-8.sh +0 -0
- package/adapters/gate-9.sh +0 -0
- package/adapters/powershell.sh +85 -33
- package/adapters/typescript.sh +8 -8
- package/build-integrity/__tests__/import-resolver.test.ts +22 -0
- package/build-integrity/__tests__/orchestrator.test.ts +1 -2
- package/build-integrity/gate-10.ts +7 -0
- package/gate-12-file-hygiene.sh +230 -0
- package/hooks/adapter-common.sh +27 -0
- package/hooks/gate-10.sh +105 -0
- package/hooks/gate-12-file-hygiene.sh +0 -0
- package/hooks/lib/validate-code-walkthrough.cjs +125 -0
- package/hooks/pre-commit +130 -33
- package/hooks/pre-push +15 -91
- package/lib/__tests__/check-version.test.js +67 -46
- package/lib/__tests__/doctor.test.js +7 -6
- package/lib/__tests__/gate-runner.test.js +267 -0
- package/lib/__tests__/init.test.js +32 -0
- package/lib/__tests__/npm-package.test.js +13 -0
- package/lib/__tests__/phase-transition.test.js +604 -12
- package/lib/__tests__/sprint-discovery.test.ts +0 -1
- package/lib/__tests__/sprint-status-rework.test.js +100 -1
- package/lib/__tests__/update-hooks.test.js +143 -10
- package/lib/__tests__/upgrade-exec.test.js +73 -45
- package/lib/__tests__/upgrade-skill-sync.test.js +1 -1
- package/lib/__tests__/upgrade.test.js +12 -11
- package/lib/doctor.js +2 -1
- package/lib/gate-runner.js +68 -23
- package/lib/init.js +34 -5
- package/lib/install-skill.js +1 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/phase-transition.js +203 -23
- package/lib/shared-paths.js +2 -0
- package/lib/sprint-status.js +15 -4
- package/lib/test-alignment.ts +3 -13
- package/lib/update-hooks.js +129 -15
- package/mock-policy/AGENTS.md +5 -5
- package/mock-policy/__tests__/gate-m3.test.ts +0 -5
- package/mock-policy/__tests__/scope-scanner.test.ts +8 -2
- package/mutation/AGENTS.md +4 -4
- package/mutation/__tests__/mutmut-runner.test.ts +443 -2
- package/mutation/__tests__/update-baseline.test.ts +0 -2
- package/mutation/runners/mutmut-runner.ts +67 -17
- package/package.json +9 -3
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/claude-code/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +13 -12
- package/plugins/claude-code/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/claude-code/skills/delphi-review/SKILL.md +30 -29
- package/plugins/claude-code/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/claude-code/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/claude-code/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/claude-code/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/claude-code/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/dsh/README.md +25 -0
- package/plugins/dsh/cordis.patch.yml +7 -0
- package/plugins/dsh/package.json +70 -0
- package/plugins/dsh/scripts/prepack.cjs +82 -0
- package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +113 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-1.md +91 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-2.md +155 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-3.md +70 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-4.md +154 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-5.md +126 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-6.md +180 -0
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/dsh/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/dsh/skills/delphi-review/.delphi-config.json.example +70 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +67 -0
- package/plugins/dsh/skills/delphi-review/INSTALL.md +150 -0
- package/plugins/dsh/skills/delphi-review/SKILL.md +559 -0
- package/plugins/dsh/skills/delphi-review/hooks.md +19 -0
- package/plugins/dsh/skills/delphi-review/opencode.json.delphi.example +52 -0
- package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +470 -0
- package/plugins/dsh/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
- package/plugins/dsh/skills/delphi-review/references/requirements.md +402 -0
- package/plugins/dsh/skills/delphi-review/references/round-templates.md +134 -0
- package/plugins/dsh/skills/delphi-review/scripts/verify-consensus.sh +43 -0
- package/plugins/dsh/skills/delphi-review/tools.md +29 -0
- package/plugins/dsh/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/dsh/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/dsh/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/dsh/skills/grilling/SKILL.md +12 -0
- package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +156 -0
- package/plugins/dsh/skills/ralph-loop/SKILL.md +198 -0
- package/plugins/dsh/skills/ralph-loop/references/components.md +113 -0
- package/plugins/dsh/skills/ralph-loop/references/phase-2-build-ralph.md +89 -0
- package/plugins/dsh/skills/ralph-loop/references/workflow-details.md +99 -0
- package/plugins/dsh/skills/ralph-loop/templates/progress-log.md +36 -0
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +103 -0
- package/plugins/dsh/skills/sprint-flow/SKILL.md +411 -0
- package/plugins/dsh/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
- package/plugins/dsh/skills/sprint-flow/hooks.md +18 -0
- package/plugins/dsh/skills/sprint-flow/references/components/memory.md +87 -0
- package/plugins/dsh/skills/sprint-flow/references/components/middleware.md +75 -0
- package/plugins/dsh/skills/sprint-flow/references/components/skill-invocations.md +105 -0
- package/plugins/dsh/skills/sprint-flow/references/components/system-prompt.md +27 -0
- package/plugins/dsh/skills/sprint-flow/references/components/tool-descriptions.md +95 -0
- package/plugins/dsh/skills/sprint-flow/references/force-levels.md +204 -0
- package/plugins/dsh/skills/sprint-flow/references/orchestration-rules.md +236 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-1-prep.md +294 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +273 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-3-build.md +348 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-4-verify.md +225 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-5-ship.md +222 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-6-close.md +266 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-overview.md +486 -0
- package/plugins/dsh/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-learning-log.md +136 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-output-template.md +110 -0
- package/plugins/dsh/skills/sprint-flow/templates/emergent-issues-template.md +120 -0
- package/plugins/dsh/skills/sprint-flow/templates/pain-document-template.md +115 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-progress-template.md +140 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-summary-template.md +121 -0
- package/plugins/dsh/skills/sprint-flow/tools.md +29 -0
- package/plugins/dsh/skills/test-driven-development/SKILL.md +371 -0
- package/plugins/dsh/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +56 -0
- package/plugins/dsh/skills/test-specification-alignment/SKILL.md +205 -0
- package/plugins/dsh/skills/test-specification-alignment/references/alignment-verification-algorithm.md +493 -0
- package/plugins/dsh/skills/test-specification-alignment/references/phase2-constraint-enforcement.md +431 -0
- package/plugins/dsh/skills/test-specification-alignment/references/specification-format.md +348 -0
- package/plugins/dsh/skills/to-issues/SKILL.md +277 -0
- package/plugins/dsh/src/command.test.ts +102 -0
- package/plugins/dsh/src/command.ts +75 -0
- package/plugins/dsh/src/contract.test.ts +62 -0
- package/plugins/dsh/src/gate-runner.test.ts +100 -0
- package/plugins/dsh/src/gate-runner.ts +58 -0
- package/plugins/dsh/src/index.test.ts +97 -0
- package/plugins/dsh/src/index.ts +94 -0
- package/plugins/dsh/src/skills.test.ts +58 -0
- package/plugins/dsh/tsconfig.build.json +13 -0
- package/plugins/dsh/tsconfig.json +19 -0
- package/plugins/dsh/vitest.config.ts +8 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/scripts/prepack.cjs +1 -0
- package/plugins/opencode/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/opencode/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/opencode/skills/delphi-review/AGENTS.md +13 -12
- package/plugins/opencode/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/opencode/skills/delphi-review/SKILL.md +30 -29
- package/plugins/opencode/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/opencode/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/opencode/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/opencode/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/opencode/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/opencode/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/opencode/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +3 -11
- package/plugins/qoder/skills/delphi-review/AGENTS.md +15 -17
- package/plugins/qoder/skills/delphi-review/INSTALL.md +12 -12
- package/plugins/qoder/skills/delphi-review/SKILL.md +58 -25
- package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +1 -1
- package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/qoder/skills/sprint-flow/SKILL.md +2 -2
- package/plugins/qoder/skills/sprint-flow/references/force-levels.md +38 -35
- package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +48 -6
- package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +5 -6
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
- package/principles/AGENTS.md +4 -4
- package/scripts/__tests__/sync-package-content.test.js +30 -0
- package/scripts/delphi-external-review.cjs +194 -63
- package/scripts/sync-package-content.js +9 -2
- package/skills/clipboard-vision/SKILL.md +47 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/skills/delphi-review/.delphi-config.json.example +24 -27
- package/skills/delphi-review/AGENTS.md +13 -12
- package/skills/delphi-review/INSTALL.md +15 -17
- package/skills/delphi-review/SKILL.md +30 -29
- package/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/skills/delphi-review/references/requirements.md +66 -54
- package/skills/delphi-review/references/round-templates.md +7 -7
- package/skills/sprint-flow/AGENTS.md +5 -5
- package/skills/sprint-flow/SKILL.md +4 -4
- package/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/skills/sprint-flow/references/force-levels.md +37 -35
- package/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/skills/sprint-flow/references/phase-overview.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +4 -4
- package/sprint-gate.sh +291 -0
package/hooks/gate-10.sh
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# GATE 10: Semgrep SAST Security Scan
|
|
4
|
+
# Detects security vulnerabilities (SQL injection, XSS, etc.) in staged files
|
|
5
|
+
# Tool: semgrep -- https://semgrep.dev
|
|
6
|
+
# ============================================================================
|
|
7
|
+
|
|
8
|
+
2>&1 echo ""
|
|
9
|
+
2>&1 echo "→ Gate 10: Semgrep SAST Security Scan..."
|
|
10
|
+
GATE_10_START=$(gate_start_ms)
|
|
11
|
+
|
|
12
|
+
GATE_10_STATUS=""
|
|
13
|
+
|
|
14
|
+
# Semgrep availability check
|
|
15
|
+
SEMGREP_CMD=""
|
|
16
|
+
if command -v semgrep >/dev/null 2>&1; then
|
|
17
|
+
SEMGREP_CMD="semgrep"
|
|
18
|
+
elif [ -f "$HOME/.local/bin/semgrep" ]; then
|
|
19
|
+
SEMGREP_CMD="$HOME/.local/bin/semgrep"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [ -z "$SEMGREP_CMD" ]; then
|
|
23
|
+
echo " ⚠️ WARN - semgrep not installed — SAST scanning unavailable"
|
|
24
|
+
echo " Install: brew install semgrep (macOS) | pip install semgrep (Linux) | pip install semgrep (Windows)"
|
|
25
|
+
echo " Pre-cache rules: semgrep --config=p/security-audit"
|
|
26
|
+
echo " Gate 10: SAST Security (WARN, semgrep not installed)"
|
|
27
|
+
GATE_10_STATUS="WARN"
|
|
28
|
+
else
|
|
29
|
+
# Get staged files filtered to Semgrep-supported languages
|
|
30
|
+
SEMGREP_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E '\.(ts|tsx|js|jsx|py|go|java|c|cpp|cs|rb|php|scala|swift)$' || true)
|
|
31
|
+
|
|
32
|
+
if [ -z "$SEMGREP_FILES" ]; then
|
|
33
|
+
echo " ⏭️ SKIPPED - SAST (no supported language files changed)"
|
|
34
|
+
GATE_10_STATUS="SKIP"
|
|
35
|
+
else
|
|
36
|
+
# Run semgrep with JSON output
|
|
37
|
+
# --config=p/security-audit: explicit security ruleset
|
|
38
|
+
# --json: machine-readable output
|
|
39
|
+
# --disable-version-check: skip network call
|
|
40
|
+
SEMGREP_OUTPUT=$("$SEMGREP_CMD" scan --config=p/security-audit --json --disable-version-check "$SEMGREP_FILES" 2>&1)
|
|
41
|
+
SEMGREP_EXIT=$?
|
|
42
|
+
|
|
43
|
+
if [ "$SEMGREP_EXIT" -eq 0 ]; then
|
|
44
|
+
echo " ✅ PASSED - No security vulnerabilities found."
|
|
45
|
+
GATE_10_STATUS="PASS"
|
|
46
|
+
elif [ "$SEMGREP_EXIT" -eq 1 ]; then
|
|
47
|
+
# Findings detected - parse JSON to categorize
|
|
48
|
+
CRITICAL_HIGH=$(echo "$SEMGREP_OUTPUT" | node -e "
|
|
49
|
+
let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{
|
|
50
|
+
try{const data=JSON.parse(d);const results=data.results||[];
|
|
51
|
+
const count=results.filter(r=>{const s=(r.extra&&r.extra.severity||'').toUpperCase();return s==='CRITICAL'||s==='HIGH';}).length;
|
|
52
|
+
console.log(count);}catch(e){console.log('0');}
|
|
53
|
+
});" 2>/dev/null || echo "0")
|
|
54
|
+
|
|
55
|
+
MEDIUM_LOW=$(echo "$SEMGREP_OUTPUT" | node -e "
|
|
56
|
+
let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{
|
|
57
|
+
try{const data=JSON.parse(d);const results=data.results||[];
|
|
58
|
+
const count=results.filter(r=>{const s=(r.extra&&r.extra.severity||'').toUpperCase();return s==='MEDIUM'||s==='LOW';}).length;
|
|
59
|
+
console.log(count);}catch(e){console.log('0');}
|
|
60
|
+
});" 2>/dev/null || echo "0")
|
|
61
|
+
|
|
62
|
+
# Extract top finding details
|
|
63
|
+
FINDING_DETAILS=$(echo "$SEMGREP_OUTPUT" | node -e "
|
|
64
|
+
let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{
|
|
65
|
+
try{const data=JSON.parse(d);const results=data.results||[];
|
|
66
|
+
results.slice(0,5).forEach(r=>{const extra=r.extra||{};
|
|
67
|
+
const severity=(extra.severity||'UNKNOWN').toUpperCase();
|
|
68
|
+
const ruleId=r.check_id||'unknown';const path=r.path||'unknown';
|
|
69
|
+
const line=(r.start&&r.start.line)||'?';const msg=(extra.message||'').slice(0,80);
|
|
70
|
+
console.log(' ['+severity+'] '+ruleId);console.log(' '+path+':'+line+' → '+msg);console.log();});
|
|
71
|
+
}catch(e){console.log(' (Failed to parse semgrep output)');}
|
|
72
|
+
});" 2>/dev/null || echo " (Failed to parse semgrep output)")
|
|
73
|
+
|
|
74
|
+
if [ "$CRITICAL_HIGH" -gt 0 ]; then
|
|
75
|
+
echo ""
|
|
76
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
77
|
+
echo " GATE 10: Semgrep Security Gate"
|
|
78
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
79
|
+
echo " CRITICAL/HIGH: ${CRITICAL_HIGH} ❌ BLOCKED"
|
|
80
|
+
echo " MEDIUM/LOW: ${MEDIUM_LOW} ⚠️ warning"
|
|
81
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
82
|
+
echo " ❌ BLOCKED — Critical/High vulnerability found"
|
|
83
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
84
|
+
echo "$FINDING_DETAILS"
|
|
85
|
+
echo " Run 'semgrep scan --config=p/security-audit' to review all findings."
|
|
86
|
+
GATE_10_STATUS="FAIL"
|
|
87
|
+
exit 1
|
|
88
|
+
else
|
|
89
|
+
echo ""
|
|
90
|
+
echo " ✅ PASSED - No critical/high vulnerabilities"
|
|
91
|
+
if [ "$MEDIUM_LOW" -gt 0 ]; then
|
|
92
|
+
echo " ⚠️ ${MEDIUM_LOW} medium/low findings (warnings only)"
|
|
93
|
+
echo "$FINDING_DETAILS"
|
|
94
|
+
fi
|
|
95
|
+
GATE_10_STATUS="PASS"
|
|
96
|
+
fi
|
|
97
|
+
else
|
|
98
|
+
# semgrep runtime error (timeout, config error, etc.)
|
|
99
|
+
echo " ⚠️ semgrep exited with code ${SEMGREP_EXIT} — skipping gate"
|
|
100
|
+
echo " ⏭️ SKIPPED - SAST (semgrep runtime error)"
|
|
101
|
+
GATE_10_STATUS="SKIP"
|
|
102
|
+
fi
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
record_gate_audit "gate-10" "sast-security" "$GATE_10_STATUS" "0" "$GATE_10_START"
|
|
File without changes
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
|
|
6
|
+
const REQUIRED_ROLES = ['architecture', 'technical', 'feasibility'];
|
|
7
|
+
const VALIDITY_MS = 60 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
function fail(message) {
|
|
10
|
+
console.error(`Invalid code walkthrough evidence: ${message}`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isPlainObject(value) {
|
|
15
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
16
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17
|
+
return prototype === Object.prototype || prototype === null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseTimestamp(value, field) {
|
|
21
|
+
const match = typeof value === 'string'
|
|
22
|
+
? value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?Z$/)
|
|
23
|
+
: null;
|
|
24
|
+
if (!match) fail(`${field} must be a canonical UTC ISO-8601 timestamp.`);
|
|
25
|
+
|
|
26
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText, fractionText] = match;
|
|
27
|
+
const year = Number(yearText);
|
|
28
|
+
const month = Number(monthText);
|
|
29
|
+
const day = Number(dayText);
|
|
30
|
+
const hour = Number(hourText);
|
|
31
|
+
const minute = Number(minuteText);
|
|
32
|
+
const second = Number(secondText);
|
|
33
|
+
const milliseconds = Number((fractionText || '').padEnd(3, '0') || 0);
|
|
34
|
+
const date = new Date(0);
|
|
35
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
36
|
+
date.setUTCHours(hour, minute, second, milliseconds);
|
|
37
|
+
|
|
38
|
+
if (date.getUTCFullYear() !== year
|
|
39
|
+
|| date.getUTCMonth() !== month - 1
|
|
40
|
+
|| date.getUTCDate() !== day
|
|
41
|
+
|| date.getUTCHours() !== hour
|
|
42
|
+
|| date.getUTCMinutes() !== minute
|
|
43
|
+
|| date.getUTCSeconds() !== second
|
|
44
|
+
|| date.getUTCMilliseconds() !== milliseconds) {
|
|
45
|
+
fail(`${field} must contain a valid UTC calendar date and time.`);
|
|
46
|
+
}
|
|
47
|
+
return date.getTime();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateExpert(expert, index, roles, models) {
|
|
51
|
+
if (!isPlainObject(expert)) fail(`experts[${index}] must be a plain object.`);
|
|
52
|
+
if (!REQUIRED_ROLES.includes(expert.role)) fail(`experts[${index}].role is invalid.`);
|
|
53
|
+
if (roles.has(expert.role)) fail(`expert role ${expert.role} is duplicated.`);
|
|
54
|
+
roles.add(expert.role);
|
|
55
|
+
|
|
56
|
+
if (expert.verdict !== 'APPROVED') fail(`expert ${expert.role} must be APPROVED.`);
|
|
57
|
+
if (expert.result_type !== 'delphi_expert_result') {
|
|
58
|
+
fail(`expert ${expert.role} must be a successful Delphi expert result.`);
|
|
59
|
+
}
|
|
60
|
+
if (expert.error !== undefined || expert.fallback !== undefined) {
|
|
61
|
+
fail(`expert ${expert.role} contains an error or fallback marker.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof expert.requested_model !== 'string' || expert.requested_model.trim() === '') {
|
|
65
|
+
fail(`expert ${expert.role} requested_model must be non-empty.`);
|
|
66
|
+
}
|
|
67
|
+
const model = expert.requested_model.trim();
|
|
68
|
+
if (models.has(model)) fail(`requested_model ${model} is duplicated.`);
|
|
69
|
+
models.add(model);
|
|
70
|
+
|
|
71
|
+
if (expert.resolved_model !== null
|
|
72
|
+
&& (typeof expert.resolved_model !== 'string' || expert.resolved_model.trim() === '')) {
|
|
73
|
+
fail(`expert ${expert.role} resolved_model must be non-empty or null.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function validateEvidence(evidence, expectedCommit, expectedBranch, now) {
|
|
78
|
+
if (!isPlainObject(evidence)) fail('top level must be a plain object.');
|
|
79
|
+
if (evidence.commit !== expectedCommit) fail('commit does not match HEAD.');
|
|
80
|
+
if (evidence.branch !== expectedBranch) fail('branch does not match the current branch.');
|
|
81
|
+
if (evidence.verdict !== 'APPROVED') fail('top-level verdict must be APPROVED.');
|
|
82
|
+
|
|
83
|
+
const timestamp = parseTimestamp(evidence.timestamp, 'timestamp');
|
|
84
|
+
const expires = parseTimestamp(evidence.expires, 'expires');
|
|
85
|
+
if (timestamp > now) fail('timestamp is in the future.');
|
|
86
|
+
if (expires <= now) fail('evidence has expired.');
|
|
87
|
+
if (expires - timestamp !== VALIDITY_MS) {
|
|
88
|
+
fail('expires must be exactly one hour after timestamp.');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof evidence.consensus_ratio !== 'number'
|
|
92
|
+
|| !Number.isFinite(evidence.consensus_ratio)
|
|
93
|
+
|| evidence.consensus_ratio < 0.90
|
|
94
|
+
|| evidence.consensus_ratio > 1) {
|
|
95
|
+
fail('consensus_ratio must be a finite number from 0.90 through 1.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!Array.isArray(evidence.experts) || evidence.experts.length !== 3) {
|
|
99
|
+
fail('experts must contain exactly three records.');
|
|
100
|
+
}
|
|
101
|
+
const roles = new Set();
|
|
102
|
+
const models = new Set();
|
|
103
|
+
evidence.experts.forEach((expert, index) => {
|
|
104
|
+
validateExpert(expert, index, roles, models);
|
|
105
|
+
});
|
|
106
|
+
if (roles.size !== REQUIRED_ROLES.length) fail('all required expert roles must be present.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function main() {
|
|
110
|
+
const [file, expectedCommit, expectedBranch, nowValue] = process.argv.slice(2);
|
|
111
|
+
if (!file || !expectedCommit || !expectedBranch) {
|
|
112
|
+
fail('usage: validate-code-walkthrough.cjs <file> <commit> <branch> [now].');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let evidence;
|
|
116
|
+
try {
|
|
117
|
+
evidence = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
118
|
+
} catch {
|
|
119
|
+
fail('file must exist and contain valid JSON.');
|
|
120
|
+
}
|
|
121
|
+
const now = nowValue === undefined ? Date.now() : parseTimestamp(nowValue, 'current time');
|
|
122
|
+
validateEvidence(evidence, expectedCommit, expectedBranch, now);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main();
|
package/hooks/pre-commit
CHANGED
|
@@ -14,7 +14,21 @@
|
|
|
14
14
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
15
|
ADAPTER_DIR=""
|
|
16
16
|
GLOBAL_ADAPTER_DIR="$HOME/.config/xp-gate/adapters"
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
# BEGIN GIT CONTEXT FALLBACK
|
|
19
|
+
if ! declare -F run_without_git_context >/dev/null 2>&1; then
|
|
20
|
+
run_without_git_context() (
|
|
21
|
+
unset GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_CONFIG GIT_CONFIG_PARAMETERS
|
|
22
|
+
unset GIT_CONFIG_COUNT GIT_OBJECT_DIRECTORY GIT_DIR GIT_WORK_TREE
|
|
23
|
+
unset GIT_IMPLICIT_WORK_TREE GIT_GRAFT_FILE GIT_INDEX_FILE
|
|
24
|
+
unset GIT_NO_REPLACE_OBJECTS GIT_REPLACE_REF_BASE GIT_PREFIX
|
|
25
|
+
unset GIT_SHALLOW_FILE GIT_COMMON_DIR
|
|
26
|
+
"$@"
|
|
27
|
+
)
|
|
28
|
+
fi
|
|
29
|
+
# END GIT CONTEXT FALLBACK
|
|
30
|
+
|
|
31
|
+
PROJECT_GITHOOKS="$(run_without_git_context git rev-parse --show-toplevel 2>/dev/null)/githooks"
|
|
18
32
|
GATES_DIR="$SCRIPT_DIR/gates"
|
|
19
33
|
|
|
20
34
|
if [ -f "$GLOBAL_ADAPTER_DIR/adapter-common.sh" ]; then
|
|
@@ -89,7 +103,7 @@ _quality_report_on_exit() {
|
|
|
89
103
|
}
|
|
90
104
|
trap '_quality_report_on_exit' EXIT
|
|
91
105
|
|
|
92
|
-
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$(cd "$SCRIPT_DIR/../.." 2>/dev/null || echo "$SCRIPT_DIR/..")" )"
|
|
106
|
+
PROJECT_ROOT="$(run_without_git_context git rev-parse --show-toplevel 2>/dev/null || echo "$(cd "$SCRIPT_DIR/../.." 2>/dev/null || echo "$SCRIPT_DIR/..")" )"
|
|
93
107
|
if [[ -d "$PROJECT_ROOT/node_modules/.bin" ]]; then
|
|
94
108
|
export PATH="$PROJECT_ROOT/node_modules/.bin:$PATH"
|
|
95
109
|
fi
|
|
@@ -415,6 +429,17 @@ if [ -z "$CHANGED_FILES" ]; then
|
|
|
415
429
|
exit 0
|
|
416
430
|
fi
|
|
417
431
|
|
|
432
|
+
any_changed_files_match() {
|
|
433
|
+
local pattern
|
|
434
|
+
|
|
435
|
+
for pattern in "$@"; do
|
|
436
|
+
if printf '%s\n' "$CHANGED_FILES" | grep -qE -- "$pattern"; then
|
|
437
|
+
return 0
|
|
438
|
+
fi
|
|
439
|
+
done
|
|
440
|
+
return 1
|
|
441
|
+
}
|
|
442
|
+
|
|
418
443
|
# ============================================================================
|
|
419
444
|
# Multi-language detection (v0.15.0+): detect all languages from changed file
|
|
420
445
|
# extensions, with fallback to manifest-based detection.
|
|
@@ -1387,6 +1412,14 @@ else
|
|
|
1387
1412
|
# --- BLOCK path: New .ts/.tsx files without corresponding tests ---
|
|
1388
1413
|
NEW_TS_FILES=$(git diff --cached --name-only --diff-filter=A | grep -E '\.(ts|tsx)$' | grep -v '\.d\.ts$' | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' | grep -v '__snapshots__' | grep -v 'node_modules/' | grep -v '\.next/' | grep -v '\.nuxt/' | grep -v 'dist/' | grep -v 'build/' | grep -v '.turbo/' | grep -v '.cache/' | grep -Ev '(^|/)vitest\.config\.' | grep -Ev '(^|/)vite\.config\.' | grep -Ev '(^|/)jest\.config\.' | grep -Ev '(^|/)tsconfig\.' | grep -Ev '(^|/)eslint\.' | grep -Ev '(^|/)prettier\.' | grep -Ev '(^|/)tailwind\.' || true)
|
|
1389
1414
|
|
|
1415
|
+
# Strip PROJECT_SUBDIR prefix from new-TS-file paths when running in a subdirectory.
|
|
1416
|
+
# git diff returns repo-root relative paths, but after cd "$PROJECT_SUBDIR"
|
|
1417
|
+
# the test-pairing [ -f ] checks below need subdirectory-relative paths.
|
|
1418
|
+
# (Mirrors the v0.14.31+ Gate 5b fix at lines 1603-1608; Gate 5a was missing it.)
|
|
1419
|
+
if [ -n "$PROJECT_SUBDIR" ] && [ "$PROJECT_SUBDIR" != "." ] && [ -n "$NEW_TS_FILES" ]; then
|
|
1420
|
+
NEW_TS_FILES=$(echo "$NEW_TS_FILES" | sed "s|^${PROJECT_SUBDIR}/||")
|
|
1421
|
+
fi
|
|
1422
|
+
|
|
1390
1423
|
if [ -n "$NEW_TS_FILES" ]; then
|
|
1391
1424
|
BLOCKED_TS_FILES=0
|
|
1392
1425
|
for src_file in $NEW_TS_FILES; do
|
|
@@ -1749,8 +1782,8 @@ else
|
|
|
1749
1782
|
if [ "${CURRENT_LANG}" != "unknown" ] && [ -n "$ADAPTER_FILE" ]; then
|
|
1750
1783
|
if source "$ADAPTER_FILE" 2>/dev/null; then
|
|
1751
1784
|
# TypeScript: vitest with smart test selection
|
|
1752
|
-
if [ "$CURRENT_LANG" = "typescript" ] && [ -f "package.json" ]; then
|
|
1753
|
-
if npx vitest --version >/dev/null 2>&1; then
|
|
1785
|
+
if [ "$CURRENT_LANG" = "typescript" ] && { [ -f "package.json" ] || [ -f "$PROJECT_ROOT/package.json" ]; }; then
|
|
1786
|
+
if run_without_git_context npx vitest --version >/dev/null 2>&1; then
|
|
1754
1787
|
# Count changed test files to decide strategy
|
|
1755
1788
|
CHANGED_TEST_FILE_COUNT=$(echo "$CHANGED_TEST_FILES" | grep -c '.' || echo "0")
|
|
1756
1789
|
|
|
@@ -1766,7 +1799,7 @@ else
|
|
|
1766
1799
|
if [ "$CHANGED_TEST_FILE_COUNT" -gt 20 ] 2>/dev/null; then
|
|
1767
1800
|
# Too many test files changed → full run
|
|
1768
1801
|
echo "Too many test files changed ($CHANGED_TEST_FILE_COUNT), running full test suite..."
|
|
1769
|
-
TESTS_OUTPUT=$(npx vitest run --coverage 2>&1)
|
|
1802
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage 2>&1)
|
|
1770
1803
|
TESTS_EXIT_CODE=$?
|
|
1771
1804
|
echo "$TESTS_OUTPUT" | head -60
|
|
1772
1805
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1781,7 +1814,7 @@ else
|
|
|
1781
1814
|
SMART_TARGETS=$(echo "$CHANGED_TEST_FILES" | tr '\n' ' ')
|
|
1782
1815
|
echo "Running ${CHANGED_TEST_FILE_COUNT} changed test file(s) with coverage..."
|
|
1783
1816
|
echo " Target(s): $(echo "$CHANGED_TEST_FILES" | tr '\n' ' ')"
|
|
1784
|
-
TESTS_OUTPUT=$(npx vitest run --coverage $SMART_TARGETS 2>&1)
|
|
1817
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage $SMART_TARGETS 2>&1)
|
|
1785
1818
|
TESTS_EXIT_CODE=$?
|
|
1786
1819
|
echo "$TESTS_OUTPUT" | head -60
|
|
1787
1820
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1825,7 +1858,7 @@ else
|
|
|
1825
1858
|
if [ -n "$RELATED_TESTS" ]; then
|
|
1826
1859
|
echo " Found related test(s): $(echo "$RELATED_TESTS" | tr '\n' ' ')"
|
|
1827
1860
|
PARTIAL_TEST_RUN=true # Mark as partial run for coverage check
|
|
1828
|
-
TESTS_OUTPUT=$(npx vitest run --coverage $RELATED_TESTS 2>&1)
|
|
1861
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage $RELATED_TESTS 2>&1)
|
|
1829
1862
|
TESTS_EXIT_CODE=$?
|
|
1830
1863
|
echo "$TESTS_OUTPUT" | head -60
|
|
1831
1864
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1838,7 +1871,7 @@ else
|
|
|
1838
1871
|
else
|
|
1839
1872
|
# No related tests found → full run is safest
|
|
1840
1873
|
echo " No related test files found, running full test suite..."
|
|
1841
|
-
TESTS_OUTPUT=$(npx vitest run --coverage 2>&1)
|
|
1874
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage 2>&1)
|
|
1842
1875
|
TESTS_EXIT_CODE=$?
|
|
1843
1876
|
echo "$TESTS_OUTPUT" | head -60
|
|
1844
1877
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1857,7 +1890,7 @@ else
|
|
|
1857
1890
|
fi
|
|
1858
1891
|
else
|
|
1859
1892
|
# Fallback: run_tests without coverage
|
|
1860
|
-
TESTS_OUTPUT=$(run_tests 2>&1)
|
|
1893
|
+
TESTS_OUTPUT=$(run_without_git_context run_tests 2>&1)
|
|
1861
1894
|
TESTS_EXIT_CODE=$?
|
|
1862
1895
|
echo "$TESTS_OUTPUT" | head -50
|
|
1863
1896
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1868,15 +1901,21 @@ else
|
|
|
1868
1901
|
fi
|
|
1869
1902
|
else
|
|
1870
1903
|
# Non-TypeScript: standard adapter test flow
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1904
|
+
# Skip for Python — tests already run with coverage in the coverage section below.
|
|
1905
|
+
# Running them here first without coverage doubles pre-commit time for no benefit.
|
|
1906
|
+
if [ "$CURRENT_LANG" = "python" ]; then
|
|
1907
|
+
echo "ℹ️ Python tests deferred to coverage section (single pass)."
|
|
1908
|
+
else
|
|
1909
|
+
TESTS_OUTPUT=$(run_without_git_context run_tests 2>&1)
|
|
1910
|
+
TESTS_EXIT_CODE=$?
|
|
1911
|
+
echo "$TESTS_OUTPUT" | head -50
|
|
1912
|
+
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
1913
|
+
echo ""
|
|
1914
|
+
echo "❌ BLOCKED - Tests FAILED"
|
|
1915
|
+
exit 1
|
|
1916
|
+
else
|
|
1917
|
+
echo "✅ PASSED - Unit tests passed."
|
|
1918
|
+
fi
|
|
1880
1919
|
fi
|
|
1881
1920
|
fi
|
|
1882
1921
|
else
|
|
@@ -2056,6 +2095,45 @@ else
|
|
|
2056
2095
|
echo "⚠️ coverage/lcov.info not found. Ensure dart test --coverage was run."
|
|
2057
2096
|
fi
|
|
2058
2097
|
;;
|
|
2098
|
+
"powershell")
|
|
2099
|
+
COVERAGE_ENFORCED=true
|
|
2100
|
+
if [ -f "coverage.xml" ]; then
|
|
2101
|
+
COVERAGE_RESULT=$(node -e "
|
|
2102
|
+
try {
|
|
2103
|
+
const fs = require('fs');
|
|
2104
|
+
const xml = fs.readFileSync('coverage.xml', 'utf8');
|
|
2105
|
+
const counters = [...xml.matchAll(/<counter\b([^>]*)\/?\s*>/gi)]
|
|
2106
|
+
.map((match) => Object.fromEntries(
|
|
2107
|
+
[...match[1].matchAll(/([\w:-]+)\s*=\s*(['\"])(.*?)\2/g)]
|
|
2108
|
+
.map((attribute) => [attribute[1].toUpperCase(), attribute[3]])
|
|
2109
|
+
))
|
|
2110
|
+
.filter((counter) => counter.TYPE === 'LINE');
|
|
2111
|
+
const reportCounter = counters.at(-1);
|
|
2112
|
+
const missed = Number(reportCounter?.MISSED);
|
|
2113
|
+
const covered = Number(reportCounter?.COVERED);
|
|
2114
|
+
const total = missed + covered;
|
|
2115
|
+
if (!Number.isFinite(missed) || !Number.isFinite(covered) || missed < 0 || covered < 0 || total <= 0) {
|
|
2116
|
+
throw new Error('invalid LINE counter');
|
|
2117
|
+
}
|
|
2118
|
+
const percentage = (covered / total) * 100;
|
|
2119
|
+
console.log((percentage < 80 ? 'below' : 'pass') + '|' + Math.round(percentage));
|
|
2120
|
+
} catch (error) { console.log('parse_error'); }
|
|
2121
|
+
" 2>/dev/null) || COVERAGE_RESULT="parse_error"
|
|
2122
|
+
if [ "$COVERAGE_RESULT" != "parse_error" ] && [ -n "$COVERAGE_RESULT" ]; then
|
|
2123
|
+
COVERAGE_STATUS=${COVERAGE_RESULT%%|*}
|
|
2124
|
+
COVERAGE_PERCENT=${COVERAGE_RESULT#*|}
|
|
2125
|
+
if [ "$COVERAGE_STATUS" = "below" ]; then
|
|
2126
|
+
echo "❌ BLOCKED - PowerShell coverage ${COVERAGE_PERCENT}% below 80% threshold"
|
|
2127
|
+
exit 1
|
|
2128
|
+
fi
|
|
2129
|
+
echo "PowerShell coverage: ${COVERAGE_PERCENT}% ✅ (≥ 80%)"
|
|
2130
|
+
else
|
|
2131
|
+
echo "⚠️ Could not parse PowerShell coverage from coverage.xml"
|
|
2132
|
+
fi
|
|
2133
|
+
else
|
|
2134
|
+
echo "⚠️ coverage.xml not found. Ensure Pester coverage was run."
|
|
2135
|
+
fi
|
|
2136
|
+
;;
|
|
2059
2137
|
*)
|
|
2060
2138
|
# Default: attempt to parse coverage from common formats regardless of language
|
|
2061
2139
|
COVERAGE_ENFORCED=true
|
|
@@ -2165,6 +2243,15 @@ elif has_project_lang "powershell" 2>/dev/null || [ "$PROJECT_LANG" = "powershel
|
|
|
2165
2243
|
echo " ⏭️ SKIPPED - Architecture validation (no tool for PowerShell)"
|
|
2166
2244
|
|
|
2167
2245
|
else
|
|
2246
|
+
run_archlint() (
|
|
2247
|
+
cd "$PROJECT_ROOT" || exit 1
|
|
2248
|
+
if [ "$ARCHLINT_KIND" = "npx" ]; then
|
|
2249
|
+
npx @archlinter/cli "$@"
|
|
2250
|
+
else
|
|
2251
|
+
"$ARCHLINT_BIN" "$@"
|
|
2252
|
+
fi
|
|
2253
|
+
)
|
|
2254
|
+
|
|
2168
2255
|
# Check if architecture config exists (use $PROJECT_ROOT — hook may have cd'd to a subdir via ARCH-03)
|
|
2169
2256
|
if [ -f "$PROJECT_ROOT/architecture.yaml" ] || [ -f "$PROJECT_ROOT/.architecturerc" ] || [ -f "$PROJECT_ROOT/.archlint.yaml" ]; then
|
|
2170
2257
|
# Different architecture tools per language
|
|
@@ -2172,12 +2259,14 @@ else
|
|
|
2172
2259
|
case "$CURRENT_LANG" in
|
|
2173
2260
|
"typescript")
|
|
2174
2261
|
# Try local npx install first (devDependencies), fall back to global
|
|
2175
|
-
if
|
|
2176
|
-
|
|
2262
|
+
if [ -x "$PROJECT_ROOT/node_modules/.bin/archlint" ]; then
|
|
2263
|
+
ARCHLINT_KIND="binary"
|
|
2264
|
+
ARCHLINT_BIN="$PROJECT_ROOT/node_modules/.bin/archlint"
|
|
2177
2265
|
elif command -v archlint >/dev/null 2>&1; then
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2266
|
+
ARCHLINT_KIND="binary"
|
|
2267
|
+
ARCHLINT_BIN="$(command -v archlint)"
|
|
2268
|
+
elif (cd "$PROJECT_ROOT" && npx @archlinter/cli --version) >/dev/null 2>&1; then
|
|
2269
|
+
ARCHLINT_KIND="npx"
|
|
2181
2270
|
else
|
|
2182
2271
|
echo " ❌ BLOCKED - archlint not installed"
|
|
2183
2272
|
echo " Install with: npm install --save-dev @archlinter/cli"
|
|
@@ -2186,17 +2275,17 @@ else
|
|
|
2186
2275
|
echo " Running archlint for TypeScript..."
|
|
2187
2276
|
# Use diff mode with baseline for ratchet (block only new violations)
|
|
2188
2277
|
# Fall back to raw scan if no baseline exists (new project, zero-tolerance)
|
|
2189
|
-
if [ -f "
|
|
2190
|
-
ARCHLINT_OUTPUT=$(
|
|
2278
|
+
if [ -f "$PROJECT_ROOT/.architecture-baseline.json" ]; then
|
|
2279
|
+
ARCHLINT_OUTPUT=$(run_archlint diff .architecture-baseline.json --fail-on high 2>&1)
|
|
2191
2280
|
else
|
|
2192
|
-
ARCHLINT_OUTPUT=$(
|
|
2281
|
+
ARCHLINT_OUTPUT=$(run_archlint scan . -f table --quiet 2>&1)
|
|
2193
2282
|
fi
|
|
2194
2283
|
ARCHLINT_EXIT=$?
|
|
2195
2284
|
echo "$ARCHLINT_OUTPUT" | tail -30
|
|
2196
2285
|
if [ "$ARCHLINT_EXIT" -ne 0 ]; then
|
|
2197
2286
|
echo ""
|
|
2198
2287
|
echo "❌ BLOCKED - Architecture violations detected"
|
|
2199
|
-
|
|
2288
|
+
if [ -f "$PROJECT_ROOT/.architecture-baseline.json" ]; then
|
|
2200
2289
|
echo "New architecture violations found (baseline ratchet mode)."
|
|
2201
2290
|
echo "Fix the new violations above, or update the baseline with: npx @archlinter/cli snapshot -o .architecture-baseline.json"
|
|
2202
2291
|
else
|
|
@@ -2205,7 +2294,7 @@ else
|
|
|
2205
2294
|
fi
|
|
2206
2295
|
exit 1
|
|
2207
2296
|
fi
|
|
2208
|
-
|
|
2297
|
+
if [ -f "$PROJECT_ROOT/.architecture-baseline.json" ]; then
|
|
2209
2298
|
echo " ✅ TypeScript architecture validation completed (baseline ratchet mode)."
|
|
2210
2299
|
else
|
|
2211
2300
|
echo " ✅ TypeScript architecture validation completed (zero-tolerance mode). no baseline found."
|
|
@@ -2276,7 +2365,7 @@ else
|
|
|
2276
2365
|
echo " Required configuration file (.archlint.yaml / architecture.yaml) is NOT found."
|
|
2277
2366
|
echo " Architecture Quality Gate requires explicit architecture constraints."
|
|
2278
2367
|
echo ""
|
|
2279
|
-
|
|
2368
|
+
echo " Quick fix: create .archlint.yaml with: npx archlint init --no-interactive"
|
|
2280
2369
|
echo " Or architecture.yaml for custom Clean Architecture layer definitions:"
|
|
2281
2370
|
echo " layers:"
|
|
2282
2371
|
echo " - name: api"
|
|
@@ -2451,13 +2540,20 @@ echo ""
|
|
|
2451
2540
|
|
|
2452
2541
|
# ============================================================================
|
|
2453
2542
|
# GATE 10: Semgrep SAST Security Scan
|
|
2454
|
-
# Extracted to gate-10.sh for maintainability
|
|
2543
|
+
# Extracted to gate-10.sh for maintainability
|
|
2455
2544
|
# v0.14.3+ (Issue #312): Skip if no code files changed
|
|
2545
|
+
# v0.18.5 (Issue #397): Fix gate status variable collision — SAST now sets GATE_10_STATUS
|
|
2456
2546
|
# ============================================================================
|
|
2457
2547
|
if any_changed_files_match "\.(ts|tsx|js|jsx|py|go|java|kt|cpp|c|swift|m|dart|sh)$"; then
|
|
2458
|
-
|
|
2548
|
+
if [ -f "$GATE_DIR/gate-10.sh" ]; then
|
|
2549
|
+
source "$GATE_DIR/gate-10.sh"
|
|
2550
|
+
else
|
|
2551
|
+
echo "⏭️ SKIPPED - SAST (gate-10.sh not found)"
|
|
2552
|
+
GATE_10_STATUS="SKIP"
|
|
2553
|
+
fi
|
|
2459
2554
|
else
|
|
2460
2555
|
echo "✅ PASSED - No code files changed, skipping SAST scan."
|
|
2556
|
+
GATE_10_STATUS="PASS"
|
|
2461
2557
|
fi
|
|
2462
2558
|
# ============================================================================
|
|
2463
2559
|
|
|
@@ -2561,7 +2657,7 @@ generate_quality_report() {
|
|
|
2561
2657
|
# ── Branch-level quality status file ─────────────────────────────────────────────
|
|
2562
2658
|
local STATUS_DIR=".xp-gate/quality-status"
|
|
2563
2659
|
local REPORT_FILE="${STATUS_DIR}/${BRANCH}.json"
|
|
2564
|
-
mkdir -p "$
|
|
2660
|
+
mkdir -p "$(dirname "$REPORT_FILE")"
|
|
2565
2661
|
|
|
2566
2662
|
cat > "$REPORT_FILE" << ENDJSON
|
|
2567
2663
|
{
|
|
@@ -2714,5 +2810,6 @@ if [ -n "$_wp_marker" ] && [ "$_wp_marker" != "/xp-gate-precommit-marker" ]; the
|
|
|
2714
2810
|
fi
|
|
2715
2811
|
|
|
2716
2812
|
generate_quality_report
|
|
2813
|
+
_QUALITY_REPORT_DONE=1
|
|
2717
2814
|
|
|
2718
|
-
exit 0
|
|
2815
|
+
exit 0
|