atabey 0.0.13 → 0.0.15
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/ATABEY.md +1 -1
- package/PRIVACY.md +8 -14
- package/README.md +405 -56
- package/bin/cli.js +72 -5
- package/dist/framework-mcp/src/index.js +988 -63
- package/dist/framework-mcp/src/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/compliance/mask_pii.d.ts +17 -0
- package/dist/framework-mcp/src/tools/compliance/mask_pii.js +56 -0
- package/dist/framework-mcp/src/tools/compliance/mask_pii.js.map +1 -0
- package/dist/framework-mcp/src/tools/definitions.js +10 -9
- package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
- package/dist/framework-mcp/src/tools/framework/run_tests.js +25 -0
- package/dist/framework-mcp/src/tools/framework/run_tests.js.map +1 -1
- package/dist/framework-mcp/src/tools/index.js +7 -8
- package/dist/framework-mcp/src/tools/index.js.map +1 -1
- package/dist/framework-mcp/src/tools/messaging/approve_operation.d.ts +22 -0
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js +81 -0
- package/dist/framework-mcp/src/tools/messaging/approve_operation.js.map +1 -0
- package/dist/framework-mcp/src/tools/messaging/ask_human.d.ts +15 -0
- package/dist/framework-mcp/src/tools/messaging/ask_human.js +89 -20
- package/dist/framework-mcp/src/tools/messaging/ask_human.js.map +1 -1
- package/dist/framework-mcp/src/tools/observability/check_ports.d.ts +1 -0
- package/dist/framework-mcp/src/tools/observability/check_ports.js +24 -8
- package/dist/framework-mcp/src/tools/observability/check_ports.js.map +1 -1
- package/dist/framework-mcp/src/tools/quality/analyze_code_quality.js +13 -64
- package/dist/framework-mcp/src/tools/quality/analyze_code_quality.js.map +1 -1
- package/dist/framework-mcp/src/tools/schemas.d.ts +48 -53
- package/dist/framework-mcp/src/tools/schemas.js +18 -22
- package/dist/framework-mcp/src/tools/schemas.js.map +1 -1
- package/dist/framework-mcp/src/tools/shell/run_command.js +146 -90
- package/dist/framework-mcp/src/tools/shell/run_command.js.map +1 -1
- package/dist/framework-mcp/src/tools/types.d.ts +11 -105
- package/dist/framework-mcp/src/tools/types.js +1 -0
- package/dist/framework-mcp/src/tools/types.js.map +1 -1
- package/dist/framework-mcp/src/utils/auth.d.ts +36 -0
- package/dist/framework-mcp/src/utils/auth.js +79 -0
- package/dist/framework-mcp/src/utils/auth.js.map +1 -0
- package/dist/framework-mcp/src/utils/auto-rollback.d.ts +71 -0
- package/dist/framework-mcp/src/utils/auto-rollback.js +266 -0
- package/dist/framework-mcp/src/utils/auto-rollback.js.map +1 -0
- package/dist/framework-mcp/src/utils/compliance.js +12 -4
- package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
- package/dist/framework-mcp/src/utils/context-optimizer.d.ts +69 -0
- package/dist/framework-mcp/src/utils/context-optimizer.js +157 -0
- package/dist/framework-mcp/src/utils/context-optimizer.js.map +1 -0
- package/dist/framework-mcp/src/utils/discipline.d.ts +68 -0
- package/dist/framework-mcp/src/utils/discipline.js +218 -0
- package/dist/framework-mcp/src/utils/discipline.js.map +1 -0
- package/dist/framework-mcp/src/utils/finops.d.ts +143 -0
- package/dist/framework-mcp/src/utils/finops.js +326 -0
- package/dist/framework-mcp/src/utils/finops.js.map +1 -0
- package/dist/framework-mcp/src/utils/human-in-loop.d.ts +95 -0
- package/dist/framework-mcp/src/utils/human-in-loop.js +216 -0
- package/dist/framework-mcp/src/utils/human-in-loop.js.map +1 -0
- package/dist/framework-mcp/src/utils/license-scanner.d.ts +71 -0
- package/dist/framework-mcp/src/utils/license-scanner.js +284 -0
- package/dist/framework-mcp/src/utils/license-scanner.js.map +1 -0
- package/dist/framework-mcp/src/utils/loop-detector.d.ts +105 -0
- package/dist/framework-mcp/src/utils/loop-detector.js +362 -0
- package/dist/framework-mcp/src/utils/loop-detector.js.map +1 -0
- package/dist/framework-mcp/src/utils/quality.d.ts +19 -0
- package/dist/framework-mcp/src/utils/quality.js +125 -0
- package/dist/framework-mcp/src/utils/quality.js.map +1 -0
- package/dist/framework-mcp/src/utils/rules-engine.d.ts +78 -0
- package/dist/framework-mcp/src/utils/rules-engine.js +232 -0
- package/dist/framework-mcp/src/utils/rules-engine.js.map +1 -0
- package/dist/framework-mcp/src/utils/silent-router.d.ts +49 -0
- package/dist/framework-mcp/src/utils/silent-router.js +155 -0
- package/dist/framework-mcp/src/utils/silent-router.js.map +1 -0
- package/dist/framework-mcp/src/utils/storage.d.ts +1 -0
- package/dist/framework-mcp/src/utils/storage.js +2 -2
- package/dist/framework-mcp/src/utils/storage.js.map +1 -1
- package/dist/framework-mcp/src/utils/telemetry-streamer.d.ts +133 -0
- package/dist/framework-mcp/src/utils/telemetry-streamer.js +397 -0
- package/dist/framework-mcp/src/utils/telemetry-streamer.js.map +1 -0
- package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js +2 -2
- package/dist/framework-mcp/tests/tools/file_system/file_system_tools.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/framework/run_tests.test.d.ts +1 -0
- package/dist/framework-mcp/tests/tools/framework/run_tests.test.js +49 -0
- package/dist/framework-mcp/tests/tools/framework/run_tests.test.js.map +1 -0
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js +14 -5
- package/dist/framework-mcp/tests/tools/messaging/send_message.test.js.map +1 -1
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.d.ts +1 -0
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.js +44 -0
- package/dist/framework-mcp/tests/tools/observability/check_ports.test.js.map +1 -0
- package/dist/framework-mcp/tests/tools/shell/run_command.test.js +44 -14
- package/dist/framework-mcp/tests/tools/shell/run_command.test.js.map +1 -1
- package/dist/framework-mcp/tests/utils/auto-rollback.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/auto-rollback.test.js +178 -0
- package/dist/framework-mcp/tests/utils/auto-rollback.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/context-optimizer.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/context-optimizer.test.js +62 -0
- package/dist/framework-mcp/tests/utils/context-optimizer.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/finops.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/finops.test.js +157 -0
- package/dist/framework-mcp/tests/utils/finops.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/human-in-loop.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/human-in-loop.test.js +59 -0
- package/dist/framework-mcp/tests/utils/human-in-loop.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/license-scanner.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/license-scanner.test.js +131 -0
- package/dist/framework-mcp/tests/utils/license-scanner.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/loop-detector.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/loop-detector.test.js +204 -0
- package/dist/framework-mcp/tests/utils/loop-detector.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/rules-engine.test.d.ts +1 -0
- package/dist/framework-mcp/tests/utils/rules-engine.test.js +106 -0
- package/dist/framework-mcp/tests/utils/rules-engine.test.js.map +1 -0
- package/dist/framework-mcp/tests/utils/telemetry-streamer.test.d.ts +7 -0
- package/dist/framework-mcp/tests/utils/telemetry-streamer.test.js +201 -0
- package/dist/framework-mcp/tests/utils/telemetry-streamer.test.js.map +1 -0
- package/dist/src/cli/commands/check.js +16 -1
- package/dist/src/cli/commands/check.js.map +1 -1
- package/dist/src/cli/commands/dashboard.js +188 -164
- package/dist/src/cli/commands/dashboard.js.map +1 -1
- package/dist/src/cli/commands/gateway.js +2 -2
- package/dist/src/cli/commands/gateway.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-core.d.ts +2 -0
- package/dist/src/cli/commands/init/scaffold-core.js +16 -1
- package/dist/src/cli/commands/init/scaffold-core.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-ops.js +2 -2
- package/dist/src/cli/commands/init/scaffold-ops.js.map +1 -1
- package/dist/src/cli/commands/init/scaffold-standards.js +13 -1
- package/dist/src/cli/commands/init/scaffold-standards.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts +4 -0
- package/dist/src/cli/commands/init.js +94 -31
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/mcp.js +3 -1
- package/dist/src/cli/commands/mcp.js.map +1 -1
- package/dist/src/cli/commands/quickstart.d.ts +1 -1
- package/dist/src/cli/commands/quickstart.js +12 -24
- package/dist/src/cli/commands/quickstart.js.map +1 -1
- package/dist/src/cli/commands/script.js +8 -1
- package/dist/src/cli/commands/script.js.map +1 -1
- package/dist/src/cli/index.js +24 -23
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/shims.js +7 -7
- package/dist/src/cli/utils/pkg.js +2 -3
- package/dist/src/cli/utils/pkg.js.map +1 -1
- package/dist/src/cli/utils/quality.d.ts +10 -0
- package/dist/src/cli/utils/quality.js +10 -0
- package/dist/src/cli/utils/quality.js.map +1 -0
- package/dist/src/modules/agents/definitions.js +45 -40
- package/dist/src/modules/agents/definitions.js.map +1 -1
- package/dist/src/modules/agents/registry/manager.js +6 -1
- package/dist/src/modules/agents/registry/manager.js.map +1 -1
- package/dist/src/modules/engines/agent-executor.d.ts +6 -10
- package/dist/src/modules/engines/agent-executor.js +63 -21
- package/dist/src/modules/engines/agent-executor.js.map +1 -1
- package/dist/src/modules/engines/agent-loop.d.ts +34 -0
- package/dist/src/modules/engines/agent-loop.js +102 -0
- package/dist/src/modules/engines/agent-loop.js.map +1 -0
- package/dist/src/modules/engines/evaluation-engine.d.ts +12 -2
- package/dist/src/modules/engines/evaluation-engine.js +78 -11
- package/dist/src/modules/engines/evaluation-engine.js.map +1 -1
- package/dist/src/modules/engines/quality-gate.d.ts +13 -5
- package/dist/src/modules/engines/quality-gate.js +39 -31
- package/dist/src/modules/engines/quality-gate.js.map +1 -1
- package/dist/src/modules/engines/routing-engine.d.ts +5 -3
- package/dist/src/modules/engines/routing-engine.js +16 -9
- package/dist/src/modules/engines/routing-engine.js.map +1 -1
- package/dist/src/modules/gateway/cost-tracker.d.ts +5 -0
- package/dist/src/modules/gateway/cost-tracker.js +71 -0
- package/dist/src/modules/gateway/cost-tracker.js.map +1 -1
- package/dist/src/modules/gateway/gateway.d.ts +4 -0
- package/dist/src/modules/gateway/gateway.js +8 -0
- package/dist/src/modules/gateway/gateway.js.map +1 -1
- package/dist/src/shared/audit.js +3 -2
- package/dist/src/shared/audit.js.map +1 -1
- package/dist/src/shared/pii.d.ts +41 -9
- package/dist/src/shared/pii.js +122 -31
- package/dist/src/shared/pii.js.map +1 -1
- package/dist/src/shared/storage.d.ts +62 -0
- package/dist/src/shared/storage.js +47 -7
- package/dist/src/shared/storage.js.map +1 -1
- package/dist/tests/embedding.test.d.ts +1 -0
- package/dist/tests/embedding.test.js +82 -0
- package/dist/tests/embedding.test.js.map +1 -0
- package/dist/tests/engines/evaluation-engine.test.d.ts +1 -0
- package/dist/tests/engines/evaluation-engine.test.js +71 -0
- package/dist/tests/engines/evaluation-engine.test.js.map +1 -0
- package/dist/tests/engines/planning-engine.test.d.ts +1 -0
- package/dist/tests/engines/planning-engine.test.js +100 -0
- package/dist/tests/engines/planning-engine.test.js.map +1 -0
- package/dist/tests/engines/quality-gate.test.d.ts +1 -0
- package/dist/tests/engines/quality-gate.test.js +59 -0
- package/dist/tests/engines/quality-gate.test.js.map +1 -0
- package/dist/tests/engines/risk-engine.test.d.ts +1 -0
- package/dist/tests/engines/risk-engine.test.js +80 -0
- package/dist/tests/engines/risk-engine.test.js.map +1 -0
- package/dist/tests/engines/routing-engine.test.d.ts +1 -0
- package/dist/tests/engines/routing-engine.test.js +78 -0
- package/dist/tests/engines/routing-engine.test.js.map +1 -0
- package/dist/tests/integration/hermes_locking.test.js +1 -0
- package/dist/tests/integration/hermes_locking.test.js.map +1 -1
- package/dist/tests/modules/engines/agent-executor.test.js +1 -1
- package/dist/tests/modules/engines/agent-executor.test.js.map +1 -1
- package/dist/tests/modules/gateway/cost-tracker.test.js +20 -1
- package/dist/tests/modules/gateway/cost-tracker.test.js.map +1 -1
- package/dist/tests/shared/lock.test.d.ts +1 -0
- package/dist/tests/shared/lock.test.js +95 -0
- package/dist/tests/shared/lock.test.js.map +1 -0
- package/dist/tests/shared/pii.test.d.ts +1 -0
- package/dist/tests/shared/pii.test.js +183 -0
- package/dist/tests/shared/pii.test.js.map +1 -0
- package/framework-mcp/README.md +307 -35
- package/framework-mcp/dist/dashboard/assets/index-B_rK57vi.js +1221 -0
- package/framework-mcp/dist/dashboard/index.html +1 -1
- package/framework-mcp/dist/framework-mcp/src/index.js +988 -63
- package/framework-mcp/dist/framework-mcp/src/tools/compliance/mask_pii.js +55 -0
- package/framework-mcp/dist/framework-mcp/src/tools/definitions.js +10 -9
- package/framework-mcp/dist/framework-mcp/src/tools/framework/run_tests.js +25 -0
- package/framework-mcp/dist/framework-mcp/src/tools/index.js +7 -8
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/approve_operation.js +80 -0
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/ask_human.js +89 -20
- package/framework-mcp/dist/framework-mcp/src/tools/observability/check_ports.js +24 -8
- package/framework-mcp/dist/framework-mcp/src/tools/quality/analyze_code_quality.js +13 -64
- package/framework-mcp/dist/framework-mcp/src/tools/schemas.js +18 -22
- package/framework-mcp/dist/framework-mcp/src/tools/shell/run_command.js +146 -90
- package/framework-mcp/dist/framework-mcp/src/tools/types.js +1 -0
- package/framework-mcp/dist/framework-mcp/src/utils/auth.js +78 -0
- package/framework-mcp/dist/framework-mcp/src/utils/auto-rollback.js +265 -0
- package/framework-mcp/dist/framework-mcp/src/utils/compliance.js +12 -4
- package/framework-mcp/dist/framework-mcp/src/utils/context-optimizer.js +156 -0
- package/framework-mcp/dist/framework-mcp/src/utils/discipline.js +217 -0
- package/framework-mcp/dist/framework-mcp/src/utils/finops.js +325 -0
- package/framework-mcp/dist/framework-mcp/src/utils/human-in-loop.js +215 -0
- package/framework-mcp/dist/framework-mcp/src/utils/license-scanner.js +283 -0
- package/framework-mcp/dist/framework-mcp/src/utils/loop-detector.js +361 -0
- package/framework-mcp/dist/framework-mcp/src/utils/quality.js +124 -0
- package/framework-mcp/dist/framework-mcp/src/utils/rules-engine.js +231 -0
- package/framework-mcp/dist/framework-mcp/src/utils/silent-router.js +154 -0
- package/framework-mcp/dist/framework-mcp/src/utils/storage.js +2 -2
- package/framework-mcp/dist/framework-mcp/src/utils/telemetry-streamer.js +396 -0
- package/framework-mcp/dist/src/cli/platforms/core.js +72 -0
- package/framework-mcp/dist/src/cli/platforms/index.js +5 -0
- package/framework-mcp/dist/src/cli/platforms/paths.js +101 -0
- package/framework-mcp/dist/src/cli/platforms/scaffold.js +72 -0
- package/framework-mcp/dist/src/cli/platforms/utils.js +75 -0
- package/framework-mcp/dist/src/cli/utils/claude.js +57 -0
- package/framework-mcp/dist/src/cli/utils/compliance.js +12 -0
- package/framework-mcp/dist/src/cli/utils/fs.js +138 -0
- package/framework-mcp/dist/src/cli/utils/pkg.js +282 -0
- package/framework-mcp/dist/src/cli/utils/quality.js +9 -0
- package/framework-mcp/dist/src/modules/agents/definitions.js +563 -0
- package/framework-mcp/dist/src/modules/agents/registry/analyst.js +39 -0
- package/framework-mcp/dist/src/modules/agents/registry/architect.js +42 -0
- package/framework-mcp/dist/src/modules/agents/registry/backend.js +50 -0
- package/framework-mcp/dist/src/modules/agents/registry/database.js +45 -0
- package/framework-mcp/dist/src/modules/agents/registry/devops.js +45 -0
- package/framework-mcp/dist/src/modules/agents/registry/explorer.js +37 -0
- package/framework-mcp/dist/src/modules/agents/registry/frontend.js +52 -0
- package/framework-mcp/dist/src/modules/agents/registry/git.js +36 -0
- package/framework-mcp/dist/src/modules/agents/registry/manager.js +64 -0
- package/framework-mcp/dist/src/modules/agents/registry/mobile.js +41 -0
- package/framework-mcp/dist/src/modules/agents/registry/native.js +41 -0
- package/framework-mcp/dist/src/modules/agents/registry/quality.js +43 -0
- package/framework-mcp/dist/src/modules/agents/registry/security.js +42 -0
- package/framework-mcp/dist/src/modules/agents/types.js +1 -0
- package/framework-mcp/dist/src/modules/engines/evaluation-engine.js +169 -0
- package/framework-mcp/dist/src/modules/engines/quality-gate.js +92 -0
- package/framework-mcp/dist/src/modules/engines/risk-engine.js +105 -0
- package/framework-mcp/dist/src/modules/engines/routing-engine.js +408 -0
- package/framework-mcp/dist/src/modules/engines/types.js +1 -0
- package/framework-mcp/dist/src/modules/providers/definitions.js +140 -0
- package/framework-mcp/dist/src/modules/providers/registry.js +18 -0
- package/framework-mcp/dist/src/modules/providers/shared.js +104 -0
- package/framework-mcp/dist/src/modules/providers/types.js +1 -0
- package/framework-mcp/dist/src/modules/skills/adapter-skills.js +449 -0
- package/framework-mcp/dist/src/modules/skills/definitions.js +70 -0
- package/framework-mcp/dist/src/shared/audit.js +220 -0
- package/framework-mcp/dist/src/shared/errors.js +68 -0
- package/framework-mcp/dist/src/shared/lock.js +159 -0
- package/framework-mcp/dist/src/shared/pii.js +122 -31
- package/framework-mcp/dist/src/shared/retention.js +170 -0
- package/framework-mcp/dist/src/shared/storage.js +47 -7
- package/framework-mcp/dist/src/shared/string.js +29 -0
- package/framework-mcp/package.json +8 -2
- package/mcp.json +12 -6
- package/package.json +11 -5
- package/templates/full/ATABEY_FULL.md +1 -1
- package/framework-mcp/dist/dashboard/assets/index-BAPGLiWE.js +0 -267
|
@@ -1,107 +1,162 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
2
|
import { Metrics } from "../../utils/metrics.js";
|
|
3
|
+
// Each entry defines the executable and its allowed args prefix
|
|
3
4
|
const COMMAND_ALLOW_LIST = [
|
|
4
5
|
// Package managers
|
|
5
|
-
"npm test",
|
|
6
|
-
"npm run",
|
|
7
|
-
"npm install",
|
|
8
|
-
"npm ci",
|
|
9
|
-
"npm audit",
|
|
10
|
-
"npx vitest",
|
|
11
|
-
"npx tsc",
|
|
12
|
-
"npx eslint",
|
|
13
|
-
"yarn test",
|
|
14
|
-
"yarn run",
|
|
15
|
-
"yarn install",
|
|
16
|
-
"pnpm test",
|
|
17
|
-
"pnpm run",
|
|
18
|
-
"pnpm install",
|
|
6
|
+
{ cmd: "npm", args: ["test"] },
|
|
7
|
+
{ cmd: "npm", args: ["run"] },
|
|
8
|
+
{ cmd: "npm", args: ["install"] },
|
|
9
|
+
{ cmd: "npm", args: ["ci"] },
|
|
10
|
+
{ cmd: "npm", args: ["audit"] },
|
|
11
|
+
{ cmd: "npx", args: ["vitest"] },
|
|
12
|
+
{ cmd: "npx", args: ["tsc"] },
|
|
13
|
+
{ cmd: "npx", args: ["eslint"] },
|
|
14
|
+
{ cmd: "yarn", args: ["test"] },
|
|
15
|
+
{ cmd: "yarn", args: ["run"] },
|
|
16
|
+
{ cmd: "yarn", args: ["install"] },
|
|
17
|
+
{ cmd: "pnpm", args: ["test"] },
|
|
18
|
+
{ cmd: "pnpm", args: ["run"] },
|
|
19
|
+
{ cmd: "pnpm", args: ["install"] },
|
|
19
20
|
// Git
|
|
20
|
-
"git status",
|
|
21
|
-
"git diff",
|
|
22
|
-
"git log",
|
|
23
|
-
"git branch",
|
|
24
|
-
"git stash",
|
|
25
|
-
"git add",
|
|
26
|
-
"git commit",
|
|
27
|
-
"git push",
|
|
28
|
-
"git pull",
|
|
29
|
-
"git fetch",
|
|
30
|
-
"git merge",
|
|
31
|
-
"git rebase",
|
|
32
|
-
"git checkout",
|
|
33
|
-
"git reset",
|
|
21
|
+
{ cmd: "git", args: ["status"] },
|
|
22
|
+
{ cmd: "git", args: ["diff"] },
|
|
23
|
+
{ cmd: "git", args: ["log"] },
|
|
24
|
+
{ cmd: "git", args: ["branch"] },
|
|
25
|
+
{ cmd: "git", args: ["stash"] },
|
|
26
|
+
{ cmd: "git", args: ["add"] },
|
|
27
|
+
{ cmd: "git", args: ["commit"] },
|
|
28
|
+
{ cmd: "git", args: ["push"] },
|
|
29
|
+
{ cmd: "git", args: ["pull"] },
|
|
30
|
+
{ cmd: "git", args: ["fetch"] },
|
|
31
|
+
{ cmd: "git", args: ["merge"] },
|
|
32
|
+
{ cmd: "git", args: ["rebase"] },
|
|
33
|
+
{ cmd: "git", args: ["checkout"] },
|
|
34
|
+
{ cmd: "git", args: ["reset"] },
|
|
34
35
|
// Build tools
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"uniq",
|
|
79
|
-
"echo",
|
|
80
|
-
"pwd",
|
|
81
|
-
"which",
|
|
82
|
-
"file",
|
|
83
|
-
"du",
|
|
84
|
-
"df",
|
|
36
|
+
{ cmd: "make" },
|
|
37
|
+
{ cmd: "cmake" },
|
|
38
|
+
{ cmd: "dotnet", args: ["test"] },
|
|
39
|
+
{ cmd: "dotnet", args: ["format"] },
|
|
40
|
+
{ cmd: "dotnet", args: ["build"] },
|
|
41
|
+
{ cmd: "dotnet", args: ["run"] },
|
|
42
|
+
{ cmd: "dotnet", args: ["restore"] },
|
|
43
|
+
{ cmd: "go", args: ["test"] },
|
|
44
|
+
{ cmd: "go", args: ["fmt"] },
|
|
45
|
+
{ cmd: "go", args: ["build"] },
|
|
46
|
+
{ cmd: "go", args: ["run"] },
|
|
47
|
+
{ cmd: "go", args: ["vet"] },
|
|
48
|
+
{ cmd: "go", args: ["mod"] },
|
|
49
|
+
{ cmd: "pytest" },
|
|
50
|
+
{ cmd: "ruff", args: ["check"] },
|
|
51
|
+
{ cmd: "ruff", args: ["format"] },
|
|
52
|
+
{ cmd: "mvn" },
|
|
53
|
+
{ cmd: "./gradlew" },
|
|
54
|
+
{ cmd: "cargo", args: ["test"] },
|
|
55
|
+
{ cmd: "cargo", args: ["build"] },
|
|
56
|
+
{ cmd: "cargo", args: ["check"] },
|
|
57
|
+
{ cmd: "cargo", args: ["fmt"] },
|
|
58
|
+
// File operations (safe) — spawn prevents shell injection
|
|
59
|
+
{ cmd: "mkdir" },
|
|
60
|
+
{ cmd: "cp" },
|
|
61
|
+
{ cmd: "mv" },
|
|
62
|
+
{ cmd: "rmdir" },
|
|
63
|
+
{ cmd: "cat" },
|
|
64
|
+
{ cmd: "ls" },
|
|
65
|
+
{ cmd: "touch" },
|
|
66
|
+
{ cmd: "head" },
|
|
67
|
+
{ cmd: "tail" },
|
|
68
|
+
{ cmd: "wc" },
|
|
69
|
+
{ cmd: "grep" },
|
|
70
|
+
{ cmd: "find" },
|
|
71
|
+
{ cmd: "sort" },
|
|
72
|
+
{ cmd: "uniq" },
|
|
73
|
+
{ cmd: "echo" },
|
|
74
|
+
{ cmd: "pwd" },
|
|
75
|
+
{ cmd: "which" },
|
|
76
|
+
{ cmd: "file" },
|
|
77
|
+
{ cmd: "du" },
|
|
78
|
+
{ cmd: "df" },
|
|
85
79
|
];
|
|
86
80
|
const TIMEOUT = 30000; // 30 seconds
|
|
81
|
+
/**
|
|
82
|
+
* Parse a command string into executable + args array.
|
|
83
|
+
* Uses spawn for safety (no shell injection).
|
|
84
|
+
*/
|
|
85
|
+
function parseCommand(cmdString) {
|
|
86
|
+
const parts = cmdString.trim().split(/\s+/);
|
|
87
|
+
if (parts.length === 0)
|
|
88
|
+
return null;
|
|
89
|
+
return { cmd: parts[0], args: parts.slice(1) };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if a parsed command matches the allow list.
|
|
93
|
+
*/
|
|
94
|
+
function isAllowed(parsed) {
|
|
95
|
+
return COMMAND_ALLOW_LIST.some(entry => {
|
|
96
|
+
if (entry.cmd !== parsed.cmd)
|
|
97
|
+
return false;
|
|
98
|
+
if (!entry.args)
|
|
99
|
+
return true; // Any args allowed for this cmd
|
|
100
|
+
if (!parsed.args || parsed.args.length < entry.args.length)
|
|
101
|
+
return false;
|
|
102
|
+
// Check that args start with the allowed prefix
|
|
103
|
+
return entry.args.every((arg, i) => parsed.args[i] === arg);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
87
106
|
export function handleRunCommand(projectRoot, args) {
|
|
88
107
|
const command = args.command;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
// Parse command into executable + args
|
|
109
|
+
const parsed = parseCommand(command);
|
|
110
|
+
if (!parsed) {
|
|
111
|
+
return Promise.resolve({
|
|
112
|
+
content: [{ type: "text", text: "ERROR: Empty command." }],
|
|
113
|
+
isError: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (!isAllowed(parsed)) {
|
|
117
|
+
const allowedList = COMMAND_ALLOW_LIST.map(e => e.args ? `${e.cmd} ${e.args.join(" ")}` : e.cmd);
|
|
118
|
+
const errorMsg = `Command not allowed: "${command}". Only the following are allowed: ${allowedList.join(", ")}`;
|
|
92
119
|
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command} (denied)`, errorMsg);
|
|
93
120
|
return Promise.resolve({
|
|
94
121
|
content: [{ type: "text", text: `ERROR: ${errorMsg}` }],
|
|
95
122
|
isError: true,
|
|
96
123
|
});
|
|
97
124
|
}
|
|
125
|
+
// Reject any args containing shell metacharacters
|
|
126
|
+
const hasShellMetacharacters = parsed.args.some(a => /[;&|><$`\n\r]/.test(a));
|
|
127
|
+
if (hasShellMetacharacters) {
|
|
128
|
+
const errorMsg = "Command rejected: Shell metacharacters are forbidden to prevent command injection.";
|
|
129
|
+
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command} (denied: metacharacters)`, errorMsg);
|
|
130
|
+
return Promise.resolve({
|
|
131
|
+
content: [{ type: "text", text: `ERROR: ${errorMsg}` }],
|
|
132
|
+
isError: true,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
98
135
|
return new Promise((resolve) => {
|
|
99
|
-
|
|
136
|
+
const child = spawn(parsed.cmd, parsed.args, {
|
|
137
|
+
cwd: projectRoot,
|
|
138
|
+
timeout: TIMEOUT,
|
|
139
|
+
shell: false,
|
|
140
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
141
|
+
});
|
|
142
|
+
let stdout = "";
|
|
143
|
+
let stderr = "";
|
|
144
|
+
child.stdout.on("data", (data) => { stdout += data.toString(); });
|
|
145
|
+
child.stderr.on("data", (data) => { stderr += data.toString(); });
|
|
146
|
+
child.on("error", (err) => {
|
|
147
|
+
const errorMsg = `Failed to start command: ${err.message}`;
|
|
148
|
+
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command}`, errorMsg);
|
|
149
|
+
resolve({
|
|
150
|
+
content: [{ type: "text", text: `ERROR: ${errorMsg}` }],
|
|
151
|
+
isError: true,
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
child.on("close", (code) => {
|
|
100
155
|
const output = stdout + stderr;
|
|
101
156
|
const tokens = Metrics.estimateTokens(output);
|
|
102
157
|
Metrics.logUsage(projectRoot, "@mcp", `run_shell_command: ${command}`, tokens);
|
|
103
|
-
if (
|
|
104
|
-
const errorMsg = `Command failed with exit code ${
|
|
158
|
+
if (code !== 0) {
|
|
159
|
+
const errorMsg = `Command failed with exit code ${code}.`;
|
|
105
160
|
Metrics.logError(projectRoot, "@mcp", `run_shell_command: ${command}`, errorMsg);
|
|
106
161
|
resolve({
|
|
107
162
|
content: [{ type: "text", text: `ERROR: ${errorMsg}. Output: ${output}` }],
|
|
@@ -111,11 +166,12 @@ export function handleRunCommand(projectRoot, args) {
|
|
|
111
166
|
}
|
|
112
167
|
// Truncate long outputs
|
|
113
168
|
const MAX_OUTPUT_LENGTH = 5000;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
169
|
+
const truncatedOutput = output.length > MAX_OUTPUT_LENGTH
|
|
170
|
+
? output.substring(0, MAX_OUTPUT_LENGTH) + "... [TRUNCATED] ..."
|
|
171
|
+
: output;
|
|
172
|
+
resolve({
|
|
173
|
+
content: [{ type: "text", text: truncatedOutput }],
|
|
174
|
+
});
|
|
119
175
|
});
|
|
120
176
|
});
|
|
121
177
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ─── MCP AUTHENTICATION ──────────────────────────────────────────
|
|
3
|
+
*
|
|
4
|
+
* Simple API key authentication for MCP server.
|
|
5
|
+
* When MCP_AUTH_TOKEN is set, all MCP and API requests require
|
|
6
|
+
* Authorization: Bearer <token> header.
|
|
7
|
+
*
|
|
8
|
+
* Environment Variables:
|
|
9
|
+
* MCP_AUTH_TOKEN - API key for authentication (optional)
|
|
10
|
+
* MCP_AUTH_USERS - Comma-separated list of user:token pairs (optional)
|
|
11
|
+
* Example: "alice:key1,bob:key2"
|
|
12
|
+
*
|
|
13
|
+
* If neither is set, authentication is disabled (open access).
|
|
14
|
+
*/
|
|
15
|
+
let authUsers = [];
|
|
16
|
+
let masterToken = null;
|
|
17
|
+
/**
|
|
18
|
+
* Initialize authentication from environment variables.
|
|
19
|
+
*/
|
|
20
|
+
export function initAuth() {
|
|
21
|
+
masterToken = process.env.MCP_AUTH_TOKEN || null;
|
|
22
|
+
const usersStr = process.env.MCP_AUTH_USERS || "";
|
|
23
|
+
if (usersStr) {
|
|
24
|
+
authUsers = usersStr.split(",").map(pair => {
|
|
25
|
+
const [name, token] = pair.split(":");
|
|
26
|
+
return { name: name?.trim() || "unknown", token: token?.trim() || "" };
|
|
27
|
+
}).filter(u => u.token);
|
|
28
|
+
}
|
|
29
|
+
if (masterToken) {
|
|
30
|
+
process.stderr.write(`[AUTH] Master token configured (${masterToken.length} chars)\n`);
|
|
31
|
+
}
|
|
32
|
+
if (authUsers.length > 0) {
|
|
33
|
+
process.stderr.write(`[AUTH] ${authUsers.length} user(s) configured: ${authUsers.map(u => u.name).join(", ")}\n`);
|
|
34
|
+
}
|
|
35
|
+
if (!masterToken && authUsers.length === 0) {
|
|
36
|
+
process.stderr.write("[AUTH] No authentication configured - OPEN ACCESS\n");
|
|
37
|
+
process.stderr.write("[AUTH] Set MCP_AUTH_TOKEN=<key> or MCP_AUTH_USERS=user1:key1,user2:key2 to enable\n");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Authenticate a request using the Authorization header.
|
|
42
|
+
* Returns the authenticated user name or null if unauthorized.
|
|
43
|
+
*/
|
|
44
|
+
export function authenticate(req) {
|
|
45
|
+
if (!masterToken && authUsers.length === 0) {
|
|
46
|
+
// Auth disabled - allow all
|
|
47
|
+
return { authenticated: true, user: "anonymous" };
|
|
48
|
+
}
|
|
49
|
+
const authHeader = req.headers["authorization"];
|
|
50
|
+
if (!authHeader) {
|
|
51
|
+
return { authenticated: false, user: "" };
|
|
52
|
+
}
|
|
53
|
+
// Support both "Bearer <key>" and "<key>" formats
|
|
54
|
+
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : authHeader;
|
|
55
|
+
// Check master token first
|
|
56
|
+
if (masterToken && token === masterToken) {
|
|
57
|
+
return { authenticated: true, user: "admin" };
|
|
58
|
+
}
|
|
59
|
+
// Check user tokens
|
|
60
|
+
for (const user of authUsers) {
|
|
61
|
+
if (token === user.token) {
|
|
62
|
+
return { authenticated: true, user: user.name };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { authenticated: false, user: "" };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate authentication headers for MCP client configuration.
|
|
69
|
+
*/
|
|
70
|
+
export function getAuthHeader(user, token) {
|
|
71
|
+
return `Bearer ${token}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if authentication is enabled.
|
|
75
|
+
*/
|
|
76
|
+
export function isAuthEnabled() {
|
|
77
|
+
return !!masterToken || authUsers.length > 0;
|
|
78
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ─── AUTO-ROLLBACK + REGENERATE ───────────────────────────────────
|
|
3
|
+
*
|
|
4
|
+
* When the AI generates code that violates governance rules (e.g., `any` type,
|
|
5
|
+
* console.log, copyleft license), this module:
|
|
6
|
+
* 1. Captures the file content before the write
|
|
7
|
+
* 2. Validates the new content against governance rules
|
|
8
|
+
* 3. If violation found: blocks the write, restores original, and sends
|
|
9
|
+
* a regenerate instruction back to the AI via MCP notification
|
|
10
|
+
*
|
|
11
|
+
* Flow:
|
|
12
|
+
* [Pre-Write Snapshot] → [Validate Content] → [Violation?] ─yes→ [Rollback] → [Notify AI]
|
|
13
|
+
* └─no─→ [Allow Write]
|
|
14
|
+
*
|
|
15
|
+
* Key Innovation: Unlike simple blocking, this tells the AI WHY it was
|
|
16
|
+
* blocked and gives it specific instructions to self-correct.
|
|
17
|
+
*/
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import path from "path";
|
|
20
|
+
class SnapshotManager {
|
|
21
|
+
snapshots = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* Capture a pre-write snapshot of a file.
|
|
24
|
+
*/
|
|
25
|
+
capture(filePath, traceId) {
|
|
26
|
+
const resolvedPath = path.resolve(filePath);
|
|
27
|
+
let originalContent = null;
|
|
28
|
+
try {
|
|
29
|
+
if (fs.existsSync(resolvedPath)) {
|
|
30
|
+
originalContent = fs.readFileSync(resolvedPath, "utf8");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { /* file doesn't exist yet (new file) */ }
|
|
34
|
+
this.snapshots.set(resolvedPath, {
|
|
35
|
+
filePath: resolvedPath,
|
|
36
|
+
originalContent,
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
traceId,
|
|
39
|
+
restored: false,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get a snapshot for a file.
|
|
44
|
+
*/
|
|
45
|
+
get(filePath) {
|
|
46
|
+
return this.snapshots.get(path.resolve(filePath));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Restore a file to its pre-write state.
|
|
50
|
+
* Returns true if restored, false if no snapshot exists.
|
|
51
|
+
*/
|
|
52
|
+
restore(filePath) {
|
|
53
|
+
const resolvedPath = path.resolve(filePath);
|
|
54
|
+
const snapshot = this.snapshots.get(resolvedPath);
|
|
55
|
+
if (!snapshot || snapshot.restored)
|
|
56
|
+
return false;
|
|
57
|
+
try {
|
|
58
|
+
if (snapshot.originalContent !== null) {
|
|
59
|
+
// File existed before – restore original
|
|
60
|
+
fs.writeFileSync(resolvedPath, snapshot.originalContent, "utf8");
|
|
61
|
+
process.stderr.write(`[ROLLBACK] Restored: ${path.relative(process.cwd(), resolvedPath)}\n`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// File was new – delete it
|
|
65
|
+
if (fs.existsSync(resolvedPath)) {
|
|
66
|
+
fs.unlinkSync(resolvedPath);
|
|
67
|
+
process.stderr.write(`[ROLLBACK] Deleted (new file): ${path.relative(process.cwd(), resolvedPath)}\n`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
snapshot.restored = true;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
process.stderr.write(`[ROLLBACK] Failed to restore ${resolvedPath}: ${error}\n`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clean up old snapshots.
|
|
80
|
+
*/
|
|
81
|
+
cleanup(maxAgeMs = 300000) {
|
|
82
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
83
|
+
for (const [filePath, snapshot] of this.snapshots) {
|
|
84
|
+
if (snapshot.timestamp < cutoff) {
|
|
85
|
+
this.snapshots.delete(filePath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get all active snapshots.
|
|
91
|
+
*/
|
|
92
|
+
getAll() {
|
|
93
|
+
return Array.from(this.snapshots.values());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ─── Auto-Rollback Engine ─────────────────────────────────────────
|
|
97
|
+
export class AutoRollback {
|
|
98
|
+
static snapshots = new SnapshotManager();
|
|
99
|
+
/**
|
|
100
|
+
* Initialize the auto-rollback system.
|
|
101
|
+
* Sets up file watcher cleanup.
|
|
102
|
+
*/
|
|
103
|
+
static initialize() {
|
|
104
|
+
// Periodic cleanup of old snapshots (every 5 minutes)
|
|
105
|
+
setInterval(() => {
|
|
106
|
+
AutoRollback.snapshots.cleanup();
|
|
107
|
+
}, 300000);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Prepare for a file write by capturing the current state.
|
|
111
|
+
* Call this BEFORE the tool handler writes to the file.
|
|
112
|
+
*/
|
|
113
|
+
static prepareWrite(filePath, traceId) {
|
|
114
|
+
AutoRollback.snapshots.capture(filePath, traceId);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Validate content and rollback if violations are found.
|
|
118
|
+
* This is called after the tool handler writes the file, but
|
|
119
|
+
* BEFORE the response is returned to the AI.
|
|
120
|
+
*
|
|
121
|
+
* Returns:
|
|
122
|
+
* - null if content is clean
|
|
123
|
+
* - ViolationRecord[] if violations were found and rolled back
|
|
124
|
+
*/
|
|
125
|
+
static validateAndRollback(filePath, content, violations) {
|
|
126
|
+
if (violations.length === 0)
|
|
127
|
+
return null;
|
|
128
|
+
// Restore the original file
|
|
129
|
+
const restored = AutoRollback.snapshots.restore(filePath);
|
|
130
|
+
if (restored) {
|
|
131
|
+
process.stderr.write(`[ROLLBACK] Rolled back ${path.relative(process.cwd(), filePath)} due to ${violations.length} violation(s)\n`);
|
|
132
|
+
}
|
|
133
|
+
return violations;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build a regenerate instruction for the AI.
|
|
137
|
+
* This is what gets sent back to the AI to tell it to fix its output.
|
|
138
|
+
*/
|
|
139
|
+
static buildRegenerateInstruction(violations, toolName) {
|
|
140
|
+
const criticalViolations = violations.filter(v => v.severity === "CRITICAL");
|
|
141
|
+
const lines = [
|
|
142
|
+
"## ⛔ AI Output Blocked – Governance Violation",
|
|
143
|
+
"",
|
|
144
|
+
`The code you generated for tool "${toolName}" has been BLOCKED and ROLLED BACK.`,
|
|
145
|
+
"The file has been restored to its previous state.",
|
|
146
|
+
"",
|
|
147
|
+
];
|
|
148
|
+
if (criticalViolations.length > 0) {
|
|
149
|
+
lines.push(`### 🔴 Critical Violations (${criticalViolations.length})`);
|
|
150
|
+
lines.push("");
|
|
151
|
+
for (const v of criticalViolations) {
|
|
152
|
+
lines.push(`- **${v.rule}** (${v.filePath}:${v.line})`);
|
|
153
|
+
lines.push(` - Detail: ${v.detail}`);
|
|
154
|
+
lines.push(` - Fix: ${v.regenerateInstruction}`);
|
|
155
|
+
lines.push("");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const otherViolations = violations.filter(v => v.severity !== "CRITICAL");
|
|
159
|
+
if (otherViolations.length > 0) {
|
|
160
|
+
lines.push(`### 🟠 Other Violations (${otherViolations.length})`);
|
|
161
|
+
lines.push("");
|
|
162
|
+
for (const v of otherViolations) {
|
|
163
|
+
lines.push(`- **${v.rule}** (${v.filePath}:${v.line})`);
|
|
164
|
+
lines.push(` - Detail: ${v.detail}`);
|
|
165
|
+
lines.push(` - Fix: ${v.regenerateInstruction}`);
|
|
166
|
+
lines.push("");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
lines.push("---");
|
|
170
|
+
lines.push("> **Instruction:** Please regenerate the code for this operation.");
|
|
171
|
+
lines.push("> Address ALL violations listed above.");
|
|
172
|
+
lines.push("> Do NOT reintroduce the same violations.");
|
|
173
|
+
return lines.join("\n");
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Validate tool response content against governance rules.
|
|
177
|
+
* Used by the MCP middleware to check file write content before returning.
|
|
178
|
+
*/
|
|
179
|
+
static checkWriteContent(filePath, content, traceId) {
|
|
180
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
181
|
+
const violations = [];
|
|
182
|
+
// Only check source files
|
|
183
|
+
if (![".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".java", ".rs", ".swift", ".kt"].includes(ext)) {
|
|
184
|
+
return { allowed: true, violations: [], instruction: null };
|
|
185
|
+
}
|
|
186
|
+
const lines = content.split("\n");
|
|
187
|
+
// 1. Check for `any` type (CRITICAL)
|
|
188
|
+
if ([".ts", ".tsx"].includes(ext)) {
|
|
189
|
+
for (let i = 0; i < lines.length; i++) {
|
|
190
|
+
const anyMatch = lines[i].match(/:\s*any\b/);
|
|
191
|
+
if (anyMatch) {
|
|
192
|
+
violations.push({
|
|
193
|
+
rule: "No `any` Type",
|
|
194
|
+
severity: "CRITICAL",
|
|
195
|
+
filePath,
|
|
196
|
+
line: i + 1,
|
|
197
|
+
detail: `Line ${i + 1}: ${anyMatch[0].substring(0, 60)}`,
|
|
198
|
+
regenerateInstruction: "Replace `any` with `unknown` and add proper type guards. Or use a specific interface/type.",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// 2. Check for console.log (CRITICAL)
|
|
204
|
+
for (let i = 0; i < lines.length; i++) {
|
|
205
|
+
const consoleMatch = lines[i].match(/console\.(log|error|warn|debug)\s*\(/);
|
|
206
|
+
if (consoleMatch) {
|
|
207
|
+
// Check if file is exempt
|
|
208
|
+
if (!filePath.includes("logger.ts") && !filePath.includes("cli.ts") && !filePath.includes("compliance.ts")) {
|
|
209
|
+
violations.push({
|
|
210
|
+
rule: "No Console Log",
|
|
211
|
+
severity: "CRITICAL",
|
|
212
|
+
filePath,
|
|
213
|
+
line: i + 1,
|
|
214
|
+
detail: `Line ${i + 1}: ${consoleMatch[0].substring(0, 60)}`,
|
|
215
|
+
regenerateInstruction: "Replace with the project's logger (import from the appropriate logger module).",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// 3. Check for hardcoded secrets (HIGH)
|
|
221
|
+
const secretPatterns = [
|
|
222
|
+
{ pattern: /(['"])sk-[a-zA-Z0-9]{20,}['"]/, name: "OpenAI API Key" },
|
|
223
|
+
{ pattern: /(['"])ghp_[a-zA-Z0-9]{36,}['"]/, name: "GitHub Token" },
|
|
224
|
+
{ pattern: /(['"])AIza[0-9A-Za-z_-]{35}['"]/, name: "Google API Key" },
|
|
225
|
+
];
|
|
226
|
+
for (let i = 0; i < lines.length; i++) {
|
|
227
|
+
for (const { pattern, name } of secretPatterns) {
|
|
228
|
+
if (pattern.test(lines[i])) {
|
|
229
|
+
violations.push({
|
|
230
|
+
rule: `Hardcoded Secret: ${name}`,
|
|
231
|
+
severity: "HIGH",
|
|
232
|
+
filePath,
|
|
233
|
+
line: i + 1,
|
|
234
|
+
detail: `Potential ${name} hardcoded at line ${i + 1}`,
|
|
235
|
+
regenerateInstruction: `Use environment variables (process.env.*) instead of hardcoding ${name}.`,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// If violations found, prepare snapshot and rollback
|
|
241
|
+
if (violations.length > 0) {
|
|
242
|
+
AutoRollback.prepareWrite(filePath, traceId);
|
|
243
|
+
const rolledBack = AutoRollback.validateAndRollback(filePath, content, violations);
|
|
244
|
+
const instruction = AutoRollback.buildRegenerateInstruction(violations, "write_file");
|
|
245
|
+
return {
|
|
246
|
+
allowed: false,
|
|
247
|
+
violations: rolledBack || violations,
|
|
248
|
+
instruction,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return { allowed: true, violations: [], instruction: null };
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get snapshot stats.
|
|
255
|
+
*/
|
|
256
|
+
static getSnapshotStats() {
|
|
257
|
+
const all = AutoRollback.snapshots.getAll();
|
|
258
|
+
return {
|
|
259
|
+
total: all.length,
|
|
260
|
+
restored: all.filter(s => s.restored).length,
|
|
261
|
+
pending: all.filter(s => !s.restored).length,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
export { AutoRollback as AutoRollbackEngine };
|
|
@@ -53,7 +53,14 @@ export function verifyCorporateCompliance(content, filePath) {
|
|
|
53
53
|
}
|
|
54
54
|
// 5. Secure CLI Policy (No direct child_process)
|
|
55
55
|
if (moduleSpecifier.text === "child_process") {
|
|
56
|
-
|
|
56
|
+
const isExempt = filePath.includes("src/cli/commands/") ||
|
|
57
|
+
filePath.includes("shared/fs.ts") ||
|
|
58
|
+
filePath.includes("shared/lock.ts") ||
|
|
59
|
+
filePath.includes("quality-gate.ts") ||
|
|
60
|
+
filePath.includes("providers/shared.ts");
|
|
61
|
+
if (!isExempt) {
|
|
62
|
+
errors.push(`[ERROR] Corporate Compliance Breach: Direct 'child_process' usage is forbidden at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Use secure framework APIs.`);
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
66
|
}
|
|
@@ -75,10 +82,11 @@ export function verifyCorporateCompliance(content, filePath) {
|
|
|
75
82
|
const prop = node.expression;
|
|
76
83
|
if (ts.isIdentifier(prop.expression) && prop.expression.text === "fs") {
|
|
77
84
|
if (["writeFileSync", "appendFileSync"].includes(prop.name.text)) {
|
|
78
|
-
// Lock files are exempt
|
|
85
|
+
// Lock files, core atomic fs wrapper, and mcp configs are exempt
|
|
79
86
|
const args = node.arguments;
|
|
80
87
|
const firstArgText = args.length > 0 ? args[0].getText(sourceFile).toLowerCase() : "";
|
|
81
|
-
|
|
88
|
+
const isExemptFile = filePath.includes("shared/fs.ts") || filePath.includes("src/cli/commands/mcp.ts");
|
|
89
|
+
if (!firstArgText.includes("lock") && !isExemptFile) {
|
|
82
90
|
errors.push(`[ERROR] Corporate Compliance Breach: Raw 'fs.${prop.name.text}' mutation detected at line ${sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1}. Use atomic utilities instead.`);
|
|
83
91
|
}
|
|
84
92
|
}
|
|
@@ -118,7 +126,7 @@ export function scanProjectCompliance(targetDir = "src") {
|
|
|
118
126
|
const files = getAllFiles(targetDir);
|
|
119
127
|
for (const file of files) {
|
|
120
128
|
// Skip exempt files
|
|
121
|
-
if (file.includes("compliance") || file.includes("definitions") || file.includes("logger") || file.includes("cli/index.ts"))
|
|
129
|
+
if (file.includes("compliance") || file.includes("definitions") || file.includes("logger") || file.includes("cli/index.ts") || file.includes("shared/fs.ts") || file.includes("shared/lock.ts"))
|
|
122
130
|
continue;
|
|
123
131
|
const content = fs.readFileSync(file, "utf8");
|
|
124
132
|
try {
|