tribunal-kit 6.0.0 → 7.0.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/.agent/.manifest.json +78 -0
- package/.agent/ARCHITECTURE.md +19 -241
- package/.agent/agents/accessibility-reviewer.md +23 -16
- package/.agent/agents/ai-code-reviewer.md +25 -21
- package/.agent/agents/anti-pattern-reviewer.md +12 -7
- package/.agent/agents/api-architect.md +1 -0
- package/.agent/agents/backend-specialist.md +23 -22
- package/.agent/agents/cloud-engineer.md +1 -0
- package/.agent/agents/code-archaeologist.md +1 -0
- package/.agent/agents/complexity-reviewer.md +10 -7
- package/.agent/agents/database-architect.md +3 -2
- package/.agent/agents/db-latency-auditor.md +6 -2
- package/.agent/agents/debugger.md +3 -2
- package/.agent/agents/dependency-reviewer.md +1 -0
- package/.agent/agents/devops-engineer.md +9 -8
- package/.agent/agents/documentation-writer.md +6 -5
- package/.agent/agents/explorer-agent.md +1 -0
- package/.agent/agents/frontend-reviewer.md +21 -17
- package/.agent/agents/frontend-specialist.md +23 -20
- package/.agent/agents/game-developer.md +1 -0
- package/.agent/agents/interaction-reviewer.md +10 -5
- package/.agent/agents/logic-reviewer.md +3 -0
- package/.agent/agents/minimalist-reviewer.md +6 -0
- package/.agent/agents/mobile-developer.md +7 -6
- package/.agent/agents/mobile-reviewer.md +14 -13
- package/.agent/agents/orchestrator.md +3 -0
- package/.agent/agents/penetration-tester.md +1 -0
- package/.agent/agents/performance-optimizer.md +6 -5
- package/.agent/agents/performance-reviewer.md +6 -6
- package/.agent/agents/pipeline-reviewer.md +144 -0
- package/.agent/agents/precedence-reviewer.md +1 -0
- package/.agent/agents/product-manager.md +1 -0
- package/.agent/agents/product-owner.md +1 -0
- package/.agent/agents/product-reviewer.md +12 -7
- package/.agent/agents/project-planner.md +3 -0
- package/.agent/agents/qa-automation-engineer.md +50 -43
- package/.agent/agents/resilience-reviewer.md +1 -0
- package/.agent/agents/schema-reviewer.md +1 -0
- package/.agent/agents/security-auditor.md +24 -24
- package/.agent/agents/seo-specialist.md +33 -23
- package/.agent/agents/sql-reviewer.md +5 -4
- package/.agent/agents/supervisor-agent.md +12 -2
- package/.agent/agents/swarm-worker-contracts.md +13 -13
- package/.agent/agents/swarm-worker-registry.md +51 -51
- package/.agent/agents/system-architect.md +1 -0
- package/.agent/agents/test-coverage-reviewer.md +25 -22
- package/.agent/agents/test-engineer.md +12 -11
- package/.agent/agents/throughput-optimizer.md +45 -40
- package/.agent/agents/type-safety-reviewer.md +5 -4
- package/.agent/agents/ui-ux-auditor.md +6 -4
- package/.agent/agents/ui-visual-auditor.md +3 -0
- package/.agent/agents/ux-reviewer.md +11 -6
- package/.agent/agents/visual-reviewer.md +11 -6
- package/.agent/agents/vitals-reviewer.md +5 -4
- package/.agent/history/memory/.memory.idx +3716 -1
- package/.agent/history/memory/MEMORY.md +246 -1
- package/.agent/mcp_config.json +1 -6
- package/.agent/routing_index.json +2034 -263
- package/.agent/rules/GEMINI.md +12 -10
- package/.agent/rules/GEMINI_PLANNER.md +19 -21
- package/.agent/scripts/_colors.js +52 -54
- package/.agent/scripts/_utils.js +52 -53
- package/.agent/scripts/auto_preview.js +39 -43
- package/.agent/scripts/bundle_analyzer.js +47 -60
- package/.agent/scripts/case_law_manager.js +200 -294
- package/.agent/scripts/checklist.js +73 -94
- package/.agent/scripts/cicd_validator.js +305 -0
- package/.agent/scripts/compile_router.py +4 -111
- package/.agent/scripts/context_broker.js +317 -412
- package/.agent/scripts/contract_engine.js +402 -0
- package/.agent/scripts/dependency_analyzer.js +112 -124
- package/.agent/scripts/graph_builder.js +75 -103
- package/.agent/scripts/graph_visualizer.js +10 -12
- package/.agent/scripts/graph_zoom.js +38 -55
- package/.agent/scripts/guardrail_engine.js +195 -145
- package/.agent/scripts/impact_classifier.js +55 -30
- package/.agent/scripts/inner_loop_validator.js +108 -159
- package/.agent/scripts/integrity_manifest.js +59 -67
- package/.agent/scripts/lint_runner.js +65 -75
- package/.agent/scripts/marathon_harness.js +131 -208
- package/.agent/scripts/migrate_skills_frontmatter.py +4 -63
- package/.agent/scripts/minify_context.js +29 -33
- package/.agent/scripts/minimal_change_engine.js +95 -87
- package/.agent/scripts/mutation_runner.js +46 -62
- package/.agent/scripts/pipeline_engine.js +320 -209
- package/.agent/scripts/prompt_compiler.js +118 -93
- package/.agent/scripts/schema_validator.js +73 -121
- package/.agent/scripts/security_scan.js +88 -117
- package/.agent/scripts/session_manager.js +52 -69
- package/.agent/scripts/signal_detector.js +24 -22
- package/.agent/scripts/skill_evolution.js +184 -245
- package/.agent/scripts/skill_integrator.js +35 -48
- package/.agent/scripts/socratic_gate_policy.js +15 -12
- package/.agent/scripts/strengthen_skills.js +29 -33
- package/.agent/scripts/swarm_dispatcher.js +120 -151
- package/.agent/scripts/test_runner.js +54 -56
- package/.agent/scripts/token_budget_broker.js +9 -7
- package/.agent/scripts/trace_engine.js +126 -0
- package/.agent/scripts/verify_all.js +76 -93
- package/.agent/scripts/visual_audit.js +16 -21
- package/.agent/skill_topic_map.json +237 -0
- package/.agent/skills/12-principles-of-animation/SKILL.md +7 -0
- package/.agent/skills/60fps-animation/SKILL.md +7 -0
- package/.agent/skills/accessible-animation/SKILL.md +5 -1
- package/.agent/skills/adapt/SKILL.md +7 -1
- package/.agent/skills/advanced-rag-pipelines/SKILL.md +1 -0
- package/.agent/skills/agent-organizer/SKILL.md +1 -0
- package/.agent/skills/agentic-patterns/SKILL.md +33 -25
- package/.agent/skills/agentic-workflows-2026/SKILL.md +80 -0
- package/.agent/skills/ai-app-hardening/SKILL.md +59 -0
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +7 -6
- package/.agent/skills/animation-on-scroll/SKILL.md +24 -15
- package/.agent/skills/animation-systems/SKILL.md +11 -10
- package/.agent/skills/antfu-conventions/SKILL.md +7 -2
- package/.agent/skills/api-patterns/SKILL.md +14 -10
- package/.agent/skills/api-security-auditor/SKILL.md +15 -14
- package/.agent/skills/app-builder/SKILL.md +1 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +2 -2
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +12 -12
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +3 -3
- package/.agent/skills/apple-design/SKILL.md +8 -7
- package/.agent/skills/architecture/SKILL.md +3 -2
- package/.agent/skills/audit-and-fix/SKILL.md +8 -5
- package/.agent/skills/authentication-best-practices/SKILL.md +9 -8
- package/.agent/skills/backend-security-expert/SKILL.md +1 -0
- package/.agent/skills/baseline-ui/SKILL.md +8 -7
- package/.agent/skills/bash-linux/SKILL.md +1 -0
- package/.agent/skills/behavioral-modes/SKILL.md +1 -0
- package/.agent/skills/better-colors/SKILL.md +11 -7
- package/.agent/skills/better-typography/SKILL.md +30 -16
- package/.agent/skills/better-ui/SKILL.md +17 -10
- package/.agent/skills/bolder/SKILL.md +6 -0
- package/.agent/skills/brainstorming/SKILL.md +6 -4
- package/.agent/skills/browser-native-ai/SKILL.md +5 -4
- package/.agent/skills/build-primitive/SKILL.md +5 -0
- package/.agent/skills/building-native-ui/SKILL.md +1 -0
- package/.agent/skills/cicd-pro/SKILL.md +2 -1
- package/.agent/skills/clarify/SKILL.md +19 -14
- package/.agent/skills/clean-code/SKILL.md +16 -15
- package/.agent/skills/cloud-architect/SKILL.md +1 -0
- package/.agent/skills/cobejs/SKILL.md +7 -9
- package/.agent/skills/code-review-checklist/SKILL.md +1 -0
- package/.agent/skills/codebase-design/SKILL.md +7 -2
- package/.agent/skills/colorize/SKILL.md +8 -3
- package/.agent/skills/compact-landing/SKILL.md +8 -3
- package/.agent/skills/company-logos/SKILL.md +6 -0
- package/.agent/skills/config-validator/SKILL.md +10 -9
- package/.agent/skills/containerization-pro/SKILL.md +6 -5
- package/.agent/skills/context-engineering-pro/SKILL.md +70 -0
- package/.agent/skills/create-design-md/SKILL.md +13 -5
- package/.agent/skills/critique/SKILL.md +10 -8
- package/.agent/skills/csharp-developer/SKILL.md +1 -0
- package/.agent/skills/data-validation-schemas/SKILL.md +28 -23
- package/.agent/skills/database-design/SKILL.md +2 -1
- package/.agent/skills/delight/SKILL.md +6 -0
- package/.agent/skills/deployment-procedures/SKILL.md +2 -1
- package/.agent/skills/design-lab/SKILL.md +5 -0
- package/.agent/skills/devops-engineer/SKILL.md +3 -2
- package/.agent/skills/devops-incident-responder/SKILL.md +2 -1
- package/.agent/skills/diagnosing-bugs/SKILL.md +12 -2
- package/.agent/skills/distill/SKILL.md +7 -2
- package/.agent/skills/documentation-templates/SKILL.md +3 -2
- package/.agent/skills/domain-modeling/SKILL.md +12 -4
- package/.agent/skills/duckdb-analytical-sql/SKILL.md +59 -0
- package/.agent/skills/edge-ai-mobile/SKILL.md +50 -0
- package/.agent/skills/edge-computing/SKILL.md +9 -8
- package/.agent/skills/emil-design-eng/SKILL.md +43 -27
- package/.agent/skills/error-resilience/SKILL.md +34 -16
- package/.agent/skills/expo-router-v4/SKILL.md +81 -0
- package/.agent/skills/extract-design-system/SKILL.md +12 -11
- package/.agent/skills/fabel-protocol/SKILL.md +24 -17
- package/.agent/skills/fixing-accessibility/SKILL.md +23 -16
- package/.agent/skills/fixing-metadata/SKILL.md +24 -13
- package/.agent/skills/fixing-motion-performance/SKILL.md +7 -5
- package/.agent/skills/framer-motion-expert/SKILL.md +29 -15
- package/.agent/skills/frontend-design/SKILL.md +21 -13
- package/.agent/skills/frontend-security-expert/SKILL.md +1 -0
- package/.agent/skills/game-design-expert/SKILL.md +1 -0
- package/.agent/skills/game-engineering-expert/SKILL.md +1 -0
- package/.agent/skills/generative-ui-expert/SKILL.md +3 -2
- package/.agent/skills/geo-fundamentals/SKILL.md +2 -1
- package/.agent/skills/git-pro/SKILL.md +10 -9
- package/.agent/skills/github-operations/SKILL.md +2 -1
- package/.agent/skills/gpt-taste/SKILL.md +7 -0
- package/.agent/skills/gsap-core/SKILL.md +26 -22
- package/.agent/skills/gsap-frameworks/SKILL.md +6 -5
- package/.agent/skills/gsap-performance/SKILL.md +4 -3
- package/.agent/skills/gsap-plugins/SKILL.md +34 -33
- package/.agent/skills/gsap-react/SKILL.md +9 -8
- package/.agent/skills/gsap-scrolltrigger/SKILL.md +34 -33
- package/.agent/skills/gsap-timeline/SKILL.md +18 -15
- package/.agent/skills/gsap-utils/SKILL.md +25 -24
- package/.agent/skills/harden/SKILL.md +9 -3
- package/.agent/skills/harness-protocol/SKILL.md +1 -0
- package/.agent/skills/i18n-localization/SKILL.md +10 -9
- package/.agent/skills/impeccable/SKILL.md +5 -0
- package/.agent/skills/improve-codebase-architecture/SKILL.md +10 -5
- package/.agent/skills/improve-ui/SKILL.md +12 -5
- package/.agent/skills/intelligent-routing/SKILL.md +7 -6
- package/.agent/skills/knowledge-graph/SKILL.md +1 -0
- package/.agent/skills/landing-page/SKILL.md +1 -0
- package/.agent/skills/lint-and-validate/SKILL.md +9 -4
- package/.agent/skills/llm-engineering/SKILL.md +55 -54
- package/.agent/skills/local-first/SKILL.md +20 -19
- package/.agent/skills/local-first-architecture/SKILL.md +6 -1
- package/.agent/skills/lottie-animation/SKILL.md +7 -3
- package/.agent/skills/marquee-loop/SKILL.md +1 -0
- package/.agent/skills/masked-reveal/SKILL.md +17 -4
- package/.agent/skills/mcp-builder/SKILL.md +13 -10
- package/.agent/skills/micro-interaction/SKILL.md +20 -5
- package/.agent/skills/mobile-design/SKILL.md +26 -15
- package/.agent/skills/monorepo-management/SKILL.md +3 -2
- package/.agent/skills/morphing-icons/SKILL.md +16 -6
- package/.agent/skills/motion-engineering/SKILL.md +19 -15
- package/.agent/skills/nextjs-react-expert/SKILL.md +26 -21
- package/.agent/skills/nodejs-best-practices/SKILL.md +89 -76
- package/.agent/skills/observability/SKILL.md +45 -44
- package/.agent/skills/opentelemetry-observability/SKILL.md +59 -0
- package/.agent/skills/page-transition-animation/SKILL.md +6 -3
- package/.agent/skills/parallel-agents/SKILL.md +7 -2
- package/.agent/skills/performance-profiling/SKILL.md +24 -13
- package/.agent/skills/plan-writing/SKILL.md +1 -0
- package/.agent/skills/platform-engineer/SKILL.md +3 -2
- package/.agent/skills/platform-engineering-opentofu/SKILL.md +68 -0
- package/.agent/skills/playwright-ai-e2e/SKILL.md +60 -0
- package/.agent/skills/playwright-best-practices/SKILL.md +29 -28
- package/.agent/skills/polish/SKILL.md +13 -12
- package/.agent/skills/powershell-windows/SKILL.md +1 -0
- package/.agent/skills/pricing-page/SKILL.md +8 -3
- package/.agent/skills/product-aware-heuristics/SKILL.md +122 -72
- package/.agent/skills/progressive-blur/SKILL.md +9 -4
- package/.agent/skills/project-idioms/SKILL.md +1 -0
- package/.agent/skills/property-based-testing/SKILL.md +58 -0
- package/.agent/skills/python-patterns/SKILL.md +1 -0
- package/.agent/skills/python-pro/SKILL.md +1 -0
- package/.agent/skills/quieter/SKILL.md +6 -0
- package/.agent/skills/react-doctor/SKILL.md +6 -0
- package/.agent/skills/react-specialist/SKILL.md +32 -28
- package/.agent/skills/readme-builder/SKILL.md +1 -0
- package/.agent/skills/realtime-patterns/SKILL.md +34 -33
- package/.agent/skills/red-team-tactics/SKILL.md +1 -0
- package/.agent/skills/redesign-skill/SKILL.md +6 -0
- package/.agent/skills/review-animations/SKILL.md +5 -2
- package/.agent/skills/review-animations/STANDARDS.md +37 -23
- package/.agent/skills/rust-pro/SKILL.md +1 -0
- package/.agent/skills/seo-fundamentals/SKILL.md +4 -3
- package/.agent/skills/server-management/SKILL.md +1 -0
- package/.agent/skills/shadcn-ui-expert/SKILL.md +28 -22
- package/.agent/skills/shape/SKILL.md +9 -2
- package/.agent/skills/skill-creator/SKILL.md +1 -0
- package/.agent/skills/soft-skill/SKILL.md +6 -0
- package/.agent/skills/sounds-on-the-web/SKILL.md +1 -0
- package/.agent/skills/sql-pro/SKILL.md +1 -0
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +1 -0
- package/.agent/skills/svg-animation/SKILL.md +16 -7
- package/.agent/skills/swiftui-expert/SKILL.md +1 -0
- package/.agent/skills/swiss-design/SKILL.md +5 -0
- package/.agent/skills/system-design-pro/SKILL.md +1 -0
- package/.agent/skills/systematic-debugging/SKILL.md +1 -0
- package/.agent/skills/tailwind-patterns/SKILL.md +25 -11
- package/.agent/skills/taste-skill/SKILL.md +6 -0
- package/.agent/skills/tdd-workflow/SKILL.md +5 -0
- package/.agent/skills/test-result-analyzer/SKILL.md +1 -0
- package/.agent/skills/testing-patterns/SKILL.md +123 -115
- package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +5 -0
- package/.agent/skills/thinking-protocol/SKILL.md +3 -0
- package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +9 -8
- package/.agent/skills/transitions-dev/SKILL.md +19 -6
- package/.agent/skills/trend-researcher/SKILL.md +1 -0
- package/.agent/skills/typescript-advanced/SKILL.md +23 -18
- package/.agent/skills/typeset/SKILL.md +10 -1
- package/.agent/skills/ui-reasoning-engine/SKILL.md +67 -48
- package/.agent/skills/ui-skill-packs/SKILL.md +6 -1
- package/.agent/skills/ui-skills-root/SKILL.md +39 -38
- package/.agent/skills/ui-ux-pro-max/SKILL.md +34 -26
- package/.agent/skills/ui-ux-researcher/SKILL.md +1 -0
- package/.agent/skills/vector-search-pgvector/SKILL.md +78 -0
- package/.agent/skills/vue-expert/SKILL.md +22 -19
- package/.agent/skills/vulnerability-scanner/SKILL.md +22 -21
- package/.agent/skills/web-accessibility-auditor/SKILL.md +2 -1
- package/.agent/skills/web-design-guidelines/SKILL.md +16 -10
- package/.agent/skills/web-quality-audit/SKILL.md +5 -0
- package/.agent/skills/webapp-testing/SKILL.md +14 -13
- package/.agent/skills/webgpu-performance/SKILL.md +1 -0
- package/.agent/skills/whimsy-injector/SKILL.md +9 -5
- package/.agent/skills/workflow-optimizer/SKILL.md +1 -0
- package/.agent/skills/zero-trust-passkeys/SKILL.md +90 -0
- package/.agent/templates/DESIGN.md +119 -110
- package/.agent/templates/design-tokens.json +9 -6
- package/.agent/workflows/acf.md +1 -0
- package/.agent/workflows/api-tester.md +1 -0
- package/.agent/workflows/audit.md +1 -0
- package/.agent/workflows/brainstorm.md +1 -0
- package/.agent/workflows/changelog.md +1 -0
- package/.agent/workflows/contract.md +60 -0
- package/.agent/workflows/create.md +1 -0
- package/.agent/workflows/debug.md +1 -0
- package/.agent/workflows/deploy.md +1 -0
- package/.agent/workflows/enhance.md +1 -0
- package/.agent/workflows/fix-ci.md +89 -0
- package/.agent/workflows/fix.md +1 -0
- package/.agent/workflows/generate.md +1 -0
- package/.agent/workflows/marathon.md +1 -0
- package/.agent/workflows/migrate.md +1 -0
- package/.agent/workflows/minimal.md +9 -7
- package/.agent/workflows/orchestrate.md +1 -0
- package/.agent/workflows/performance-benchmarker.md +1 -0
- package/.agent/workflows/pipeline.md +26 -21
- package/.agent/workflows/plan.md +1 -0
- package/.agent/workflows/preview.md +1 -0
- package/.agent/workflows/refactor.md +3 -2
- package/.agent/workflows/review-ai.md +4 -3
- package/.agent/workflows/review.md +1 -0
- package/.agent/workflows/session.md +1 -0
- package/.agent/workflows/status.md +1 -0
- package/.agent/workflows/strengthen-skills.md +1 -0
- package/.agent/workflows/super-prompt.md +1 -0
- package/.agent/workflows/swarm.md +15 -3
- package/.agent/workflows/test.md +8 -7
- package/.agent/workflows/tribunal-backend.md +3 -2
- package/.agent/workflows/tribunal-cicd.md +104 -0
- package/.agent/workflows/tribunal-database.md +1 -0
- package/.agent/workflows/tribunal-frontend.md +8 -6
- package/.agent/workflows/tribunal-full.md +15 -14
- package/.agent/workflows/tribunal-mobile.md +6 -5
- package/.agent/workflows/tribunal-performance.md +1 -0
- package/.agent/workflows/tribunal-speed.md +1 -0
- package/.agent/workflows/tribunal-ui.md +29 -21
- package/.agent/workflows/ui-ux-pro-max.md +19 -15
- package/CONTRIBUTING.md +3 -3
- package/README.md +160 -88
- package/SECURITY.md +11 -11
- package/bin/mcp-server.js +512 -233
- package/bin/tribunal-kit.js +29 -30
- package/bin/wrapper.js +45 -48
- package/dist/cli.js +26 -0
- package/dist/commands/case.js +2 -0
- package/dist/commands/contract.js +440 -0
- package/dist/commands/memory.js +3 -2
- package/dist/commands/native.js +36 -0
- package/dist/commands/status.js +6 -0
- package/dist/esm/index.mjs +32 -0
- package/dist/index.d.ts +58 -3
- package/package.json +20 -16
- package/scripts/benchmark.js +62 -86
- package/scripts/changelog.js +67 -73
- package/scripts/fix-vbc.js +13 -15
- package/scripts/fix-vbc.ps1 +2 -118
- package/scripts/stress_benchmark.js +358 -0
- package/scripts/sync-version.js +71 -41
- package/scripts/validate-payload.js +23 -30
- package/scripts/visual_audit.js +23 -13
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
* node .agent/scripts/checklist.js . --skip security,seo
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
'use strict';
|
|
21
21
|
|
|
22
|
-
const fs = require(
|
|
23
|
-
const path = require(
|
|
24
|
-
const { execFileSync } = require(
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const { execFileSync } = require('child_process');
|
|
25
25
|
|
|
26
26
|
const {
|
|
27
27
|
RED,
|
|
@@ -37,30 +37,30 @@ const {
|
|
|
37
37
|
timer,
|
|
38
38
|
formatMs,
|
|
39
39
|
fail,
|
|
40
|
-
} = require(
|
|
40
|
+
} = require('./_colors');
|
|
41
41
|
|
|
42
|
-
const { walkDir } = require(
|
|
42
|
+
const { walkDir } = require('./_utils');
|
|
43
43
|
|
|
44
44
|
// ── Results Tracking ────────────────────────────────────────────────────────
|
|
45
45
|
|
|
46
46
|
const RESULTS = [];
|
|
47
47
|
|
|
48
48
|
function trackOk(label, ms) {
|
|
49
|
-
const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` :
|
|
49
|
+
const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : '';
|
|
50
50
|
console.log(` ${GREEN}✅ ${label}${RESET} ${timing}`);
|
|
51
|
-
RESULTS.push({ name: label, status:
|
|
51
|
+
RESULTS.push({ name: label, status: 'pass', ms });
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function trackFail(label, ms, note) {
|
|
55
|
-
const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` :
|
|
55
|
+
const timing = ms != null ? `${DIM}(${formatMs(ms)})${RESET}` : '';
|
|
56
56
|
console.log(` ${RED}❌ ${label}${RESET} ${timing}`);
|
|
57
57
|
if (note) console.log(` ${note.slice(0, 500)}`);
|
|
58
|
-
RESULTS.push({ name: label, status:
|
|
58
|
+
RESULTS.push({ name: label, status: 'fail', ms });
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function trackSkip(label, reason) {
|
|
62
62
|
console.log(` ${YELLOW}⏭️ ${label} — ${reason}${RESET}`);
|
|
63
|
-
RESULTS.push({ name: label, status:
|
|
63
|
+
RESULTS.push({ name: label, status: 'skip' });
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
@@ -75,25 +75,25 @@ function runCheck(label, cmd, cwd) {
|
|
|
75
75
|
try {
|
|
76
76
|
execFileSync(cmd[0], cmd.slice(1), {
|
|
77
77
|
cwd,
|
|
78
|
-
stdio:
|
|
78
|
+
stdio: 'pipe',
|
|
79
79
|
timeout: 60000,
|
|
80
|
-
encoding:
|
|
81
|
-
shell: process.platform ===
|
|
80
|
+
encoding: 'utf8',
|
|
81
|
+
shell: process.platform === 'win32',
|
|
82
82
|
});
|
|
83
83
|
trackOk(`${label} passed`, elapsed());
|
|
84
84
|
return true;
|
|
85
85
|
} catch (err) {
|
|
86
86
|
const ms = elapsed();
|
|
87
|
-
if (err.code ===
|
|
88
|
-
trackSkip(label,
|
|
87
|
+
if (err.code === 'ENOENT') {
|
|
88
|
+
trackSkip(label, 'command not found (tool not installed)');
|
|
89
89
|
return true; // Don't block on tools that aren't installed
|
|
90
90
|
}
|
|
91
91
|
if (err.killed) {
|
|
92
|
-
trackFail(label, ms,
|
|
92
|
+
trackFail(label, ms, 'timed out after 60s');
|
|
93
93
|
return false;
|
|
94
94
|
}
|
|
95
|
-
const output = ((err.stdout ||
|
|
96
|
-
trackFail(`${label} failed`, ms, output ||
|
|
95
|
+
const output = ((err.stdout || '') + (err.stderr || '')).trim();
|
|
96
|
+
trackFail(`${label} failed`, ms, output || 'non-zero exit code');
|
|
97
97
|
return false;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -107,38 +107,36 @@ function runCheck(label, cmd, cwd) {
|
|
|
107
107
|
function checkSecrets(projectRoot) {
|
|
108
108
|
const elapsed = timer();
|
|
109
109
|
const dangerousPatterns = [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
110
|
+
'password=',
|
|
111
|
+
'secret=',
|
|
112
|
+
'api_key=',
|
|
113
|
+
'apikey=',
|
|
114
|
+
'auth_token=',
|
|
115
|
+
'private_key=',
|
|
116
116
|
];
|
|
117
117
|
let foundIssues = false;
|
|
118
|
-
const sourceExtensions = new Set([
|
|
118
|
+
const sourceExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.py']);
|
|
119
119
|
|
|
120
120
|
const files = walkDir(projectRoot, { extensions: sourceExtensions });
|
|
121
121
|
|
|
122
122
|
for (const fullPath of files) {
|
|
123
123
|
// Skip .env files — they are allowed to contain secrets
|
|
124
|
-
if (path.basename(fullPath).startsWith(
|
|
124
|
+
if (path.basename(fullPath).startsWith('.env')) continue;
|
|
125
125
|
|
|
126
126
|
let content;
|
|
127
127
|
try {
|
|
128
|
-
content = fs.readFileSync(fullPath,
|
|
128
|
+
content = fs.readFileSync(fullPath, 'utf8');
|
|
129
129
|
} catch {
|
|
130
130
|
continue;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
const lines = content.split(
|
|
133
|
+
const lines = content.split('\n');
|
|
134
134
|
for (let i = 0; i < lines.length; i++) {
|
|
135
135
|
const lineLower = lines[i].toLowerCase().trim();
|
|
136
|
-
const hasPattern = dangerousPatterns.some(
|
|
137
|
-
if (hasPattern && lineLower.includes(
|
|
136
|
+
const hasPattern = dangerousPatterns.some(p => lineLower.includes(p));
|
|
137
|
+
if (hasPattern && lineLower.includes('=') && !lineLower.startsWith('#')) {
|
|
138
138
|
const rel = path.relative(projectRoot, fullPath);
|
|
139
|
-
fail(
|
|
140
|
-
`Possible secret: ${rel}:${i + 1} → ${lines[i].trim().slice(0, 80)}`,
|
|
141
|
-
);
|
|
139
|
+
fail(`Possible secret: ${rel}:${i + 1} → ${lines[i].trim().slice(0, 80)}`);
|
|
142
140
|
foundIssues = true;
|
|
143
141
|
}
|
|
144
142
|
}
|
|
@@ -148,7 +146,7 @@ function checkSecrets(projectRoot) {
|
|
|
148
146
|
if (!foundIssues) {
|
|
149
147
|
trackOk(`Secret scan — ${files.length} files clean`, ms);
|
|
150
148
|
} else {
|
|
151
|
-
trackFail(
|
|
149
|
+
trackFail('Secret scan — hardcoded credentials detected', ms);
|
|
152
150
|
}
|
|
153
151
|
return !foundIssues;
|
|
154
152
|
}
|
|
@@ -166,76 +164,61 @@ function runAll(projectRoot, url, skipTiers) {
|
|
|
166
164
|
const totalTimer = timer();
|
|
167
165
|
|
|
168
166
|
// Priority 1 — Security
|
|
169
|
-
if (!skipTiers.includes(
|
|
170
|
-
console.log(sectionHeader(
|
|
167
|
+
if (!skipTiers.includes('security')) {
|
|
168
|
+
console.log(sectionHeader('Security — Secret Scan', 1));
|
|
171
169
|
if (!checkSecrets(projectRoot)) failures++;
|
|
172
170
|
} else {
|
|
173
|
-
trackSkip(
|
|
171
|
+
trackSkip('Security', 'skipped by flag');
|
|
174
172
|
}
|
|
175
173
|
|
|
176
174
|
// Priority 2 — Lint
|
|
177
|
-
if (!skipTiers.includes(
|
|
178
|
-
console.log(sectionHeader(
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
"ESLint",
|
|
182
|
-
["npx", "eslint", ".", "--max-warnings=0"],
|
|
183
|
-
projectRoot,
|
|
184
|
-
)
|
|
185
|
-
)
|
|
186
|
-
failures++;
|
|
187
|
-
if (!runCheck("TypeScript", ["npx", "tsc", "--noEmit"], projectRoot))
|
|
188
|
-
failures++;
|
|
175
|
+
if (!skipTiers.includes('lint')) {
|
|
176
|
+
console.log(sectionHeader('Lint', 2));
|
|
177
|
+
if (!runCheck('ESLint', ['npx', 'eslint', '.', '--max-warnings=0'], projectRoot)) failures++;
|
|
178
|
+
if (!runCheck('TypeScript', ['npx', 'tsc', '--noEmit'], projectRoot)) failures++;
|
|
189
179
|
} else {
|
|
190
|
-
trackSkip(
|
|
180
|
+
trackSkip('Lint', 'skipped by flag');
|
|
191
181
|
}
|
|
192
182
|
|
|
193
183
|
// Priority 3 — Schema
|
|
194
|
-
if (!skipTiers.includes(
|
|
195
|
-
console.log(sectionHeader(
|
|
196
|
-
trackSkip(
|
|
184
|
+
if (!skipTiers.includes('schema')) {
|
|
185
|
+
console.log(sectionHeader('Schema Validation', 3));
|
|
186
|
+
trackSkip('Schema', 'run manually if you have DB migrations');
|
|
197
187
|
} else {
|
|
198
|
-
trackSkip(
|
|
188
|
+
trackSkip('Schema', 'skipped by flag');
|
|
199
189
|
}
|
|
200
190
|
|
|
201
191
|
// Priority 4 — Tests
|
|
202
|
-
if (!skipTiers.includes(
|
|
203
|
-
console.log(sectionHeader(
|
|
204
|
-
if (
|
|
205
|
-
!runCheck(
|
|
206
|
-
"Test suite",
|
|
207
|
-
["npm", "test", "--", "--passWithNoTests"],
|
|
208
|
-
projectRoot,
|
|
209
|
-
)
|
|
210
|
-
)
|
|
192
|
+
if (!skipTiers.includes('tests')) {
|
|
193
|
+
console.log(sectionHeader('Tests', 4));
|
|
194
|
+
if (!runCheck('Test suite', ['npm', 'test', '--', '--passWithNoTests'], projectRoot))
|
|
211
195
|
failures++;
|
|
212
196
|
} else {
|
|
213
|
-
trackSkip(
|
|
197
|
+
trackSkip('Tests', 'skipped by flag');
|
|
214
198
|
}
|
|
215
199
|
|
|
216
200
|
// Priority 5 — UX
|
|
217
|
-
if (!skipTiers.includes(
|
|
218
|
-
console.log(sectionHeader(
|
|
219
|
-
trackSkip(
|
|
201
|
+
if (!skipTiers.includes('ux')) {
|
|
202
|
+
console.log(sectionHeader('UX / Accessibility', 5));
|
|
203
|
+
trackSkip('UX audit', 'run /preview start then check with Lighthouse');
|
|
220
204
|
} else {
|
|
221
|
-
trackSkip(
|
|
205
|
+
trackSkip('UX', 'skipped by flag');
|
|
222
206
|
}
|
|
223
207
|
|
|
224
208
|
// Priority 6 — SEO
|
|
225
|
-
if (!skipTiers.includes(
|
|
226
|
-
console.log(sectionHeader(
|
|
227
|
-
trackSkip(
|
|
209
|
+
if (!skipTiers.includes('seo')) {
|
|
210
|
+
console.log(sectionHeader('SEO', 6));
|
|
211
|
+
trackSkip('SEO check', 'use /ui-ux-pro-max for SEO-sensitive pages');
|
|
228
212
|
} else {
|
|
229
|
-
trackSkip(
|
|
213
|
+
trackSkip('SEO', 'skipped by flag');
|
|
230
214
|
}
|
|
231
215
|
|
|
232
216
|
// Priority 7 — Lighthouse / E2E
|
|
233
|
-
if (url && !skipTiers.includes(
|
|
234
|
-
console.log(sectionHeader(
|
|
235
|
-
if (!runCheck(
|
|
236
|
-
failures++;
|
|
217
|
+
if (url && !skipTiers.includes('e2e')) {
|
|
218
|
+
console.log(sectionHeader('Lighthouse / E2E', 7));
|
|
219
|
+
if (!runCheck('Playwright E2E', ['npx', 'playwright', 'test'], projectRoot)) failures++;
|
|
237
220
|
} else if (!url) {
|
|
238
|
-
trackSkip(
|
|
221
|
+
trackSkip('E2E / Lighthouse', 'pass --url to enable');
|
|
239
222
|
}
|
|
240
223
|
|
|
241
224
|
// ━━━ Summary ━━━
|
|
@@ -243,22 +226,18 @@ function runAll(projectRoot, url, skipTiers) {
|
|
|
243
226
|
console.log(`\n${BOLD}${CYAN}━━━ Checklist Summary ━━━${RESET}`);
|
|
244
227
|
summaryTable(RESULTS);
|
|
245
228
|
|
|
246
|
-
const passCount = RESULTS.filter(
|
|
247
|
-
const failCount = RESULTS.filter(
|
|
248
|
-
const skipCount = RESULTS.filter(
|
|
229
|
+
const passCount = RESULTS.filter(r => r.status === 'pass').length;
|
|
230
|
+
const failCount = RESULTS.filter(r => r.status === 'fail').length;
|
|
231
|
+
const skipCount = RESULTS.filter(r => r.status === 'skip').length;
|
|
249
232
|
|
|
233
|
+
console.log(`\n ${DIM}Total: ${RESULTS.length} checks in ${formatMs(totalMs)}${RESET}`);
|
|
250
234
|
console.log(
|
|
251
|
-
|
|
252
|
-
);
|
|
253
|
-
console.log(
|
|
254
|
-
` ${GREEN}${passCount} passed${RESET} ${failCount > 0 ? `${RED}${failCount} failed${RESET} ` : ""}${skipCount > 0 ? `${YELLOW}${skipCount} skipped${RESET}` : ""}`,
|
|
235
|
+
` ${GREEN}${passCount} passed${RESET} ${failCount > 0 ? `${RED}${failCount} failed${RESET} ` : ''}${skipCount > 0 ? `${YELLOW}${skipCount} skipped${RESET}` : ''}`,
|
|
255
236
|
);
|
|
256
237
|
|
|
257
238
|
console.log();
|
|
258
239
|
if (failures === 0) {
|
|
259
|
-
console.log(
|
|
260
|
-
`${GREEN}${BOLD} ✔ All checks passed — ready to proceed.${RESET}`,
|
|
261
|
-
);
|
|
240
|
+
console.log(`${GREEN}${BOLD} ✔ All checks passed — ready to proceed.${RESET}`);
|
|
262
241
|
} else {
|
|
263
242
|
console.log(
|
|
264
243
|
`${RED}${BOLD} ✖ ${failures} tier(s) failed — fix critical issues before proceeding.${RESET}`,
|
|
@@ -277,14 +256,14 @@ function parseArgs(argv) {
|
|
|
277
256
|
const raw = argv.slice(2);
|
|
278
257
|
|
|
279
258
|
for (let i = 0; i < raw.length; i++) {
|
|
280
|
-
if (raw[i] ===
|
|
259
|
+
if (raw[i] === '--url' && raw[i + 1]) {
|
|
281
260
|
args.url = raw[++i];
|
|
282
|
-
} else if (raw[i] ===
|
|
261
|
+
} else if (raw[i] === '--skip' && raw[i + 1]) {
|
|
283
262
|
args.skip = raw[++i]
|
|
284
|
-
.split(
|
|
285
|
-
.map(
|
|
263
|
+
.split(',')
|
|
264
|
+
.map(s => s.trim().toLowerCase())
|
|
286
265
|
.filter(Boolean);
|
|
287
|
-
} else if (!raw[i].startsWith(
|
|
266
|
+
} else if (!raw[i].startsWith('--') && !args.path) {
|
|
288
267
|
args.path = raw[i];
|
|
289
268
|
}
|
|
290
269
|
}
|
|
@@ -307,7 +286,7 @@ function main() {
|
|
|
307
286
|
process.exit(1);
|
|
308
287
|
}
|
|
309
288
|
|
|
310
|
-
console.log(banner(
|
|
289
|
+
console.log(banner('checklist.js', { Project: projectRoot }));
|
|
311
290
|
|
|
312
291
|
const failures = runAll(projectRoot, args.url, args.skip);
|
|
313
292
|
process.exit(failures > 0 ? 1 : 0);
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cicd_validator.js — Deterministic CI/CD & GitHub Actions Validator for Tribunal Kit.
|
|
4
|
+
*
|
|
5
|
+
* Scans .github/workflows/*.yml, .gitlab-ci.yml, and Dockerfiles for:
|
|
6
|
+
* - Deprecated Action versions (v1-v3)
|
|
7
|
+
* - Security vulnerabilities (pull_request_target injection, script injection)
|
|
8
|
+
* - Concurrency race conditions
|
|
9
|
+
* - Excessive permissions (permissions: write-all)
|
|
10
|
+
* - Non-deterministic lockfile usage (npm install vs npm ci)
|
|
11
|
+
* - Static credential patterns
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node .agent/scripts/cicd_validator.js [target-directory]
|
|
15
|
+
* node .agent/scripts/cicd_validator.js . --json
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
RED,
|
|
25
|
+
GREEN,
|
|
26
|
+
YELLOW,
|
|
27
|
+
BOLD,
|
|
28
|
+
RESET,
|
|
29
|
+
banner,
|
|
30
|
+
sectionHeader,
|
|
31
|
+
summaryTable,
|
|
32
|
+
timer,
|
|
33
|
+
formatMs,
|
|
34
|
+
ok,
|
|
35
|
+
fail,
|
|
36
|
+
warn,
|
|
37
|
+
skip,
|
|
38
|
+
} = require('./_colors');
|
|
39
|
+
|
|
40
|
+
const DEPRECATED_ACTIONS = [
|
|
41
|
+
{ pattern: /actions\/checkout@(v[123]|master)/i, name: 'actions/checkout', recommended: '@v4' },
|
|
42
|
+
{
|
|
43
|
+
pattern: /actions\/setup-node@(v[123]|master)/i,
|
|
44
|
+
name: 'actions/setup-node',
|
|
45
|
+
recommended: '@v4',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
pattern: /actions\/setup-python@(v[1234]|master)/i,
|
|
49
|
+
name: 'actions/setup-python',
|
|
50
|
+
recommended: '@v5',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
pattern: /actions\/upload-artifact@(v[123]|master)/i,
|
|
54
|
+
name: 'actions/upload-artifact',
|
|
55
|
+
recommended: '@v4',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
pattern: /actions\/download-artifact@(v[123]|master)/i,
|
|
59
|
+
name: 'actions/download-artifact',
|
|
60
|
+
recommended: '@v4',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
pattern: /docker\/build-push-action@(v[1234]|master)/i,
|
|
64
|
+
name: 'docker/build-push-action',
|
|
65
|
+
recommended: '@v5',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
pattern: /aws-actions\/configure-aws-credentials@(v[123]|master)/i,
|
|
69
|
+
name: 'aws-actions/configure-aws-credentials',
|
|
70
|
+
recommended: '@v4',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
function findWorkflowFiles(dir) {
|
|
75
|
+
const files = [];
|
|
76
|
+
const githubWorkflows = path.join(dir, '.github', 'workflows');
|
|
77
|
+
if (fs.existsSync(githubWorkflows)) {
|
|
78
|
+
try {
|
|
79
|
+
const entries = fs.readdirSync(githubWorkflows);
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
if (entry.endsWith('.yml') || entry.endsWith('.yaml')) {
|
|
82
|
+
files.push(path.join(githubWorkflows, entry));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (_e) {}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const gitlabCi = path.join(dir, '.gitlab-ci.yml');
|
|
89
|
+
if (fs.existsSync(gitlabCi)) {
|
|
90
|
+
files.push(gitlabCi);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const dockerfile = path.join(dir, 'Dockerfile');
|
|
94
|
+
if (fs.existsSync(dockerfile)) {
|
|
95
|
+
files.push(dockerfile);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return files;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function auditWorkflowContent(filePath, content) {
|
|
102
|
+
const issues = [];
|
|
103
|
+
const lines = content.split('\n');
|
|
104
|
+
const isGithubWorkflow = filePath.includes('.github');
|
|
105
|
+
|
|
106
|
+
if (isGithubWorkflow) {
|
|
107
|
+
// Check for concurrency block
|
|
108
|
+
if (!content.includes('concurrency:')) {
|
|
109
|
+
const hasDeploy = /deploy|publish|release/i.test(content);
|
|
110
|
+
if (hasDeploy) {
|
|
111
|
+
issues.push({
|
|
112
|
+
level: 'WARN',
|
|
113
|
+
code: 'CI-03',
|
|
114
|
+
message:
|
|
115
|
+
"Deploy workflow is missing a top-level 'concurrency:' block to prevent race conditions.",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check for dangerous pull_request_target with head checkout
|
|
121
|
+
if (
|
|
122
|
+
content.includes('pull_request_target') &&
|
|
123
|
+
/github\.event\.pull_request\.head\.sha|github\.head_ref/i.test(content)
|
|
124
|
+
) {
|
|
125
|
+
issues.push({
|
|
126
|
+
level: 'FAIL',
|
|
127
|
+
code: 'CI-02',
|
|
128
|
+
message:
|
|
129
|
+
"CRITICAL: 'pull_request_target' trigger with PR head checkout is vulnerable to pwn-request code execution.",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check for excessive permissions
|
|
134
|
+
if (/permissions:\s*write-all/i.test(content)) {
|
|
135
|
+
issues.push({
|
|
136
|
+
level: 'FAIL',
|
|
137
|
+
code: 'CI-04',
|
|
138
|
+
message: "'permissions: write-all' violates the principle of least privilege.",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check line by line
|
|
143
|
+
for (let i = 0; i < lines.length; i++) {
|
|
144
|
+
const line = lines[i];
|
|
145
|
+
const lineNum = i + 1;
|
|
146
|
+
|
|
147
|
+
// Deprecated actions
|
|
148
|
+
for (const dep of DEPRECATED_ACTIONS) {
|
|
149
|
+
if (dep.pattern.test(line)) {
|
|
150
|
+
issues.push({
|
|
151
|
+
level: 'FAIL',
|
|
152
|
+
code: 'CI-01',
|
|
153
|
+
line: lineNum,
|
|
154
|
+
message: `Deprecated action version '${dep.name}'. Upgrade to ${dep.recommended}.`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Script injection in run:
|
|
160
|
+
if (
|
|
161
|
+
/run:\s*.*?\$\{\{\s*github\.event\.(issue\.title|issue\.body|comment\.body|pull_request\.title)\s*\}\}/i.test(
|
|
162
|
+
line,
|
|
163
|
+
)
|
|
164
|
+
) {
|
|
165
|
+
issues.push({
|
|
166
|
+
level: 'FAIL',
|
|
167
|
+
code: 'CI-10',
|
|
168
|
+
line: lineNum,
|
|
169
|
+
message:
|
|
170
|
+
"Script injection risk: Untrusted GitHub event context embedded directly in 'run:' command. Use environment variables.",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Non-deterministic npm install in CI
|
|
175
|
+
if (/\brun:\s*.*?\bnpm\s+install\b(?!.*--dry-run)/i.test(line) && !/npm\s+ci/i.test(line)) {
|
|
176
|
+
issues.push({
|
|
177
|
+
level: 'WARN',
|
|
178
|
+
code: 'CI-06',
|
|
179
|
+
line: lineNum,
|
|
180
|
+
message:
|
|
181
|
+
"Use 'npm ci' instead of 'npm install' in CI pipelines for deterministic builds.",
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Static secret hardcoding
|
|
186
|
+
if (/AKIA[0-9A-Z]{16}/.test(line)) {
|
|
187
|
+
issues.push({
|
|
188
|
+
level: 'FAIL',
|
|
189
|
+
code: 'CI-05',
|
|
190
|
+
line: lineNum,
|
|
191
|
+
message: 'Hardcoded AWS Access Key ID detected.',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return issues;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function main() {
|
|
201
|
+
const args = process.argv.slice(2);
|
|
202
|
+
const targetDir = args.find(a => !a.startsWith('-')) || process.cwd();
|
|
203
|
+
const jsonOutput = args.includes('--json');
|
|
204
|
+
|
|
205
|
+
const elapsed = timer();
|
|
206
|
+
const files = findWorkflowFiles(targetDir);
|
|
207
|
+
|
|
208
|
+
if (!jsonOutput) {
|
|
209
|
+
banner('TRIBUNAL CI/CD PIPELINE VALIDATOR');
|
|
210
|
+
sectionHeader(`Scanning workflows in: ${path.resolve(targetDir)}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (files.length === 0) {
|
|
214
|
+
if (jsonOutput) {
|
|
215
|
+
console.log(JSON.stringify({ files: 0, passes: 0, failures: 0, warnings: 0, issues: [] }));
|
|
216
|
+
} else {
|
|
217
|
+
skip('No CI/CD workflow files (.github/workflows, .gitlab-ci.yml, Dockerfile) found.');
|
|
218
|
+
}
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let totalFailures = 0;
|
|
223
|
+
let totalWarnings = 0;
|
|
224
|
+
const allIssues = [];
|
|
225
|
+
const resultsTable = [];
|
|
226
|
+
|
|
227
|
+
for (const file of files) {
|
|
228
|
+
const relPath = path.relative(targetDir, file);
|
|
229
|
+
try {
|
|
230
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
231
|
+
const fileIssues = auditWorkflowContent(file, content);
|
|
232
|
+
|
|
233
|
+
const failures = fileIssues.filter(i => i.level === 'FAIL');
|
|
234
|
+
const warnings = fileIssues.filter(i => i.level === 'WARN');
|
|
235
|
+
|
|
236
|
+
totalFailures += failures.length;
|
|
237
|
+
totalWarnings += warnings.length;
|
|
238
|
+
|
|
239
|
+
if (!jsonOutput) {
|
|
240
|
+
if (failures.length === 0 && warnings.length === 0) {
|
|
241
|
+
ok(`${relPath} — clean`);
|
|
242
|
+
resultsTable.push({ name: relPath, status: 'pass' });
|
|
243
|
+
} else if (failures.length > 0) {
|
|
244
|
+
fail(`${relPath} — ${failures.length} errors, ${warnings.length} warnings`);
|
|
245
|
+
for (const issue of fileIssues) {
|
|
246
|
+
const loc = issue.line ? `Line ${issue.line}: ` : '';
|
|
247
|
+
const color = issue.level === 'FAIL' ? RED : YELLOW;
|
|
248
|
+
console.log(` ${color}[${issue.code}]${RESET} ${loc}${issue.message}`);
|
|
249
|
+
}
|
|
250
|
+
resultsTable.push({ name: relPath, status: 'fail' });
|
|
251
|
+
} else {
|
|
252
|
+
warn(`${relPath} — ${warnings.length} warnings`);
|
|
253
|
+
for (const issue of fileIssues) {
|
|
254
|
+
const loc = issue.line ? `Line ${issue.line}: ` : '';
|
|
255
|
+
console.log(` ${YELLOW}[${issue.code}]${RESET} ${loc}${issue.message}`);
|
|
256
|
+
}
|
|
257
|
+
resultsTable.push({ name: relPath, status: 'warn' });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const issue of fileIssues) {
|
|
262
|
+
allIssues.push({ file: relPath, ...issue });
|
|
263
|
+
}
|
|
264
|
+
} catch (err) {
|
|
265
|
+
totalFailures++;
|
|
266
|
+
if (!jsonOutput) {
|
|
267
|
+
fail(`${relPath} — failed to read/parse: ${err.message}`);
|
|
268
|
+
resultsTable.push({ name: relPath, status: 'fail' });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const ms = elapsed();
|
|
274
|
+
|
|
275
|
+
if (jsonOutput) {
|
|
276
|
+
console.log(
|
|
277
|
+
JSON.stringify(
|
|
278
|
+
{
|
|
279
|
+
files: files.length,
|
|
280
|
+
failures: totalFailures,
|
|
281
|
+
warnings: totalWarnings,
|
|
282
|
+
durationMs: ms,
|
|
283
|
+
issues: allIssues,
|
|
284
|
+
},
|
|
285
|
+
null,
|
|
286
|
+
2,
|
|
287
|
+
),
|
|
288
|
+
);
|
|
289
|
+
} else {
|
|
290
|
+
sectionHeader('Summary');
|
|
291
|
+
summaryTable(resultsTable);
|
|
292
|
+
console.log(
|
|
293
|
+
`\n${BOLD}Result:${RESET} ${totalFailures > 0 ? RED + 'FAILED' : GREEN + 'PASSED'}${RESET} ` +
|
|
294
|
+
`(${files.length} files scanned, ${totalFailures} errors, ${totalWarnings} warnings in ${formatMs(ms)})\n`,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
process.exit(totalFailures > 0 ? 1 : 0);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (require.main === module) {
|
|
302
|
+
main();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
module.exports = { auditWorkflowContent, findWorkflowFiles };
|