vaspera 2.13.0 → 2.15.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/CHANGELOG.md +78 -0
- package/README.md +15 -2
- package/dist/__tests__/antagonist-integration.test.d.ts +6 -0
- package/dist/__tests__/antagonist-integration.test.d.ts.map +1 -0
- package/dist/__tests__/antagonist-integration.test.js +239 -0
- package/dist/__tests__/antagonist-integration.test.js.map +1 -0
- package/dist/__tests__/certification/agent-certificate-e2e.test.d.ts +2 -0
- package/dist/__tests__/certification/agent-certificate-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/certification/agent-certificate-e2e.test.js +90 -0
- package/dist/__tests__/certification/agent-certificate-e2e.test.js.map +1 -0
- package/dist/__tests__/certification/agent-certificate-map.test.d.ts +2 -0
- package/dist/__tests__/certification/agent-certificate-map.test.d.ts.map +1 -0
- package/dist/__tests__/certification/agent-certificate-map.test.js +107 -0
- package/dist/__tests__/certification/agent-certificate-map.test.js.map +1 -0
- package/dist/__tests__/certification/agent-certificate.test.d.ts +2 -0
- package/dist/__tests__/certification/agent-certificate.test.d.ts.map +1 -0
- package/dist/__tests__/certification/agent-certificate.test.js +78 -0
- package/dist/__tests__/certification/agent-certificate.test.js.map +1 -0
- package/dist/__tests__/certification/verify-endpoint.test.d.ts +2 -0
- package/dist/__tests__/certification/verify-endpoint.test.d.ts.map +1 -0
- package/dist/__tests__/certification/verify-endpoint.test.js +81 -0
- package/dist/__tests__/certification/verify-endpoint.test.js.map +1 -0
- package/dist/__tests__/compliance/ai-frameworks.test.d.ts +2 -0
- package/dist/__tests__/compliance/ai-frameworks.test.d.ts.map +1 -0
- package/dist/__tests__/compliance/ai-frameworks.test.js +87 -0
- package/dist/__tests__/compliance/ai-frameworks.test.js.map +1 -0
- package/dist/__tests__/eval/llm-analyzer.test.d.ts +2 -0
- package/dist/__tests__/eval/llm-analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/eval/llm-analyzer.test.js +93 -0
- package/dist/__tests__/eval/llm-analyzer.test.js.map +1 -0
- package/dist/__tests__/eval/redteam-harness.test.d.ts +2 -0
- package/dist/__tests__/eval/redteam-harness.test.d.ts.map +1 -0
- package/dist/__tests__/eval/redteam-harness.test.js +136 -0
- package/dist/__tests__/eval/redteam-harness.test.js.map +1 -0
- package/dist/__tests__/evidence/evidence.test.d.ts +2 -0
- package/dist/__tests__/evidence/evidence.test.d.ts.map +1 -0
- package/dist/__tests__/evidence/evidence.test.js +240 -0
- package/dist/__tests__/evidence/evidence.test.js.map +1 -0
- package/dist/__tests__/history/decisions.test.d.ts +2 -0
- package/dist/__tests__/history/decisions.test.d.ts.map +1 -0
- package/dist/__tests__/history/decisions.test.js +54 -0
- package/dist/__tests__/history/decisions.test.js.map +1 -0
- package/dist/__tests__/http-auth.test.d.ts +2 -0
- package/dist/__tests__/http-auth.test.d.ts.map +1 -0
- package/dist/__tests__/http-auth.test.js +55 -0
- package/dist/__tests__/http-auth.test.js.map +1 -0
- package/dist/__tests__/http-policy.test.d.ts +2 -0
- package/dist/__tests__/http-policy.test.d.ts.map +1 -0
- package/dist/__tests__/http-policy.test.js +69 -0
- package/dist/__tests__/http-policy.test.js.map +1 -0
- package/dist/__tests__/http-server-transport.test.d.ts +2 -0
- package/dist/__tests__/http-server-transport.test.d.ts.map +1 -0
- package/dist/__tests__/http-server-transport.test.js +132 -0
- package/dist/__tests__/http-server-transport.test.js.map +1 -0
- package/dist/__tests__/integration/destructive-guards.test.d.ts +2 -0
- package/dist/__tests__/integration/destructive-guards.test.d.ts.map +1 -0
- package/dist/__tests__/integration/destructive-guards.test.js +49 -0
- package/dist/__tests__/integration/destructive-guards.test.js.map +1 -0
- package/dist/__tests__/logger-redaction.test.d.ts +2 -0
- package/dist/__tests__/logger-redaction.test.d.ts.map +1 -0
- package/dist/__tests__/logger-redaction.test.js +74 -0
- package/dist/__tests__/logger-redaction.test.js.map +1 -0
- package/dist/__tests__/manifest-schema.test.d.ts +2 -0
- package/dist/__tests__/manifest-schema.test.d.ts.map +1 -0
- package/dist/__tests__/manifest-schema.test.js +43 -0
- package/dist/__tests__/manifest-schema.test.js.map +1 -0
- package/dist/__tests__/scanners/builtin-rules.test.d.ts +2 -0
- package/dist/__tests__/scanners/builtin-rules.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/builtin-rules.test.js +51 -0
- package/dist/__tests__/scanners/builtin-rules.test.js.map +1 -0
- package/dist/__tests__/scanners/runtime/golden-path-runner.test.js +13 -1
- package/dist/__tests__/scanners/runtime/golden-path-runner.test.js.map +1 -1
- package/dist/__tests__/tool-guard.test.d.ts +2 -0
- package/dist/__tests__/tool-guard.test.d.ts.map +1 -0
- package/dist/__tests__/tool-guard.test.js +97 -0
- package/dist/__tests__/tool-guard.test.js.map +1 -0
- package/dist/__tests__/util/contained-file.test.d.ts +2 -0
- package/dist/__tests__/util/contained-file.test.d.ts.map +1 -0
- package/dist/__tests__/util/contained-file.test.js +78 -0
- package/dist/__tests__/util/contained-file.test.js.map +1 -0
- package/dist/__tests__/util/subprocess.test.d.ts +2 -0
- package/dist/__tests__/util/subprocess.test.d.ts.map +1 -0
- package/dist/__tests__/util/subprocess.test.js +48 -0
- package/dist/__tests__/util/subprocess.test.js.map +1 -0
- package/dist/action/diff-mode.d.ts.map +1 -1
- package/dist/action/diff-mode.js +31 -12
- package/dist/action/diff-mode.js.map +1 -1
- package/dist/agents/antagonist/challenger.d.ts +46 -0
- package/dist/agents/antagonist/challenger.d.ts.map +1 -0
- package/dist/agents/antagonist/challenger.js +257 -0
- package/dist/agents/antagonist/challenger.js.map +1 -0
- package/dist/agents/antagonist/index.d.ts +31 -0
- package/dist/agents/antagonist/index.d.ts.map +1 -0
- package/dist/agents/antagonist/index.js +175 -0
- package/dist/agents/antagonist/index.js.map +1 -0
- package/dist/agents/antagonist/prioritizer.d.ts +27 -0
- package/dist/agents/antagonist/prioritizer.d.ts.map +1 -0
- package/dist/agents/antagonist/prioritizer.js +181 -0
- package/dist/agents/antagonist/prioritizer.js.map +1 -0
- package/dist/agents/antagonist/prompts.d.ts +12 -0
- package/dist/agents/antagonist/prompts.d.ts.map +1 -0
- package/dist/agents/antagonist/prompts.js +155 -0
- package/dist/agents/antagonist/prompts.js.map +1 -0
- package/dist/agents/antagonist/synthesizer.d.ts +34 -0
- package/dist/agents/antagonist/synthesizer.d.ts.map +1 -0
- package/dist/agents/antagonist/synthesizer.js +451 -0
- package/dist/agents/antagonist/synthesizer.js.map +1 -0
- package/dist/agents/antagonist/types.d.ts +145 -0
- package/dist/agents/antagonist/types.d.ts.map +1 -0
- package/dist/agents/antagonist/types.js +63 -0
- package/dist/agents/antagonist/types.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/certification/agent-certificate-map.d.ts +51 -0
- package/dist/certification/agent-certificate-map.d.ts.map +1 -0
- package/dist/certification/agent-certificate-map.js +265 -0
- package/dist/certification/agent-certificate-map.js.map +1 -0
- package/dist/certification/agent-certificate-sample.d.ts +25 -0
- package/dist/certification/agent-certificate-sample.d.ts.map +1 -0
- package/dist/certification/agent-certificate-sample.js +207 -0
- package/dist/certification/agent-certificate-sample.js.map +1 -0
- package/dist/certification/agent-certificate.d.ts +1981 -0
- package/dist/certification/agent-certificate.d.ts.map +1 -0
- package/dist/certification/agent-certificate.js +309 -0
- package/dist/certification/agent-certificate.js.map +1 -0
- package/dist/certification/autofix.d.ts.map +1 -1
- package/dist/certification/autofix.js +5 -3
- package/dist/certification/autofix.js.map +1 -1
- package/dist/certification/consensus.test.js +2 -0
- package/dist/certification/consensus.test.js.map +1 -1
- package/dist/certification/store.d.ts.map +1 -1
- package/dist/certification/store.js +11 -3
- package/dist/certification/store.js.map +1 -1
- package/dist/certification/types.d.ts +1 -1
- package/dist/certification/types.d.ts.map +1 -1
- package/dist/certification/types.js +2 -0
- package/dist/certification/types.js.map +1 -1
- package/dist/certification/verify-endpoint.d.ts +48 -0
- package/dist/certification/verify-endpoint.d.ts.map +1 -0
- package/dist/certification/verify-endpoint.js +79 -0
- package/dist/certification/verify-endpoint.js.map +1 -0
- package/dist/compliance/index.d.ts +2 -0
- package/dist/compliance/index.d.ts.map +1 -1
- package/dist/compliance/index.js +4 -0
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/iso42001.d.ts +21 -0
- package/dist/compliance/iso42001.d.ts.map +1 -0
- package/dist/compliance/iso42001.js +160 -0
- package/dist/compliance/iso42001.js.map +1 -0
- package/dist/compliance/mapper.d.ts.map +1 -1
- package/dist/compliance/mapper.js +12 -0
- package/dist/compliance/mapper.js.map +1 -1
- package/dist/compliance/nist-ai-rmf.d.ts +20 -0
- package/dist/compliance/nist-ai-rmf.d.ts.map +1 -0
- package/dist/compliance/nist-ai-rmf.js +140 -0
- package/dist/compliance/nist-ai-rmf.js.map +1 -0
- package/dist/config/flags.d.ts +4 -4
- package/dist/eval/fixtures.d.ts.map +1 -1
- package/dist/eval/fixtures.js +161 -119
- package/dist/eval/fixtures.js.map +1 -1
- package/dist/eval/fixtures.test.js +4 -2
- package/dist/eval/fixtures.test.js.map +1 -1
- package/dist/eval/llm-analyzer.d.ts +40 -0
- package/dist/eval/llm-analyzer.d.ts.map +1 -0
- package/dist/eval/llm-analyzer.js +154 -0
- package/dist/eval/llm-analyzer.js.map +1 -0
- package/dist/eval/redteam-harness.d.ts +95 -0
- package/dist/eval/redteam-harness.d.ts.map +1 -0
- package/dist/eval/redteam-harness.js +137 -0
- package/dist/eval/redteam-harness.js.map +1 -0
- package/dist/evidence/collector.d.ts.map +1 -1
- package/dist/evidence/collector.js +21 -1
- package/dist/evidence/collector.js.map +1 -1
- package/dist/evidence/store.d.ts.map +1 -1
- package/dist/evidence/store.js +29 -5
- package/dist/evidence/store.js.map +1 -1
- package/dist/evidence/types.d.ts +16 -9
- package/dist/evidence/types.d.ts.map +1 -1
- package/dist/history/decisions.d.ts +63 -0
- package/dist/history/decisions.d.ts.map +1 -0
- package/dist/history/decisions.js +60 -0
- package/dist/history/decisions.js.map +1 -0
- package/dist/history/index.d.ts +2 -0
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +2 -0
- package/dist/history/index.js.map +1 -1
- package/dist/history/types.d.ts +34 -5
- package/dist/history/types.d.ts.map +1 -1
- package/dist/history/types.js +2 -0
- package/dist/history/types.js.map +1 -1
- package/dist/http-auth.d.ts +22 -0
- package/dist/http-auth.d.ts.map +1 -0
- package/dist/http-auth.js +58 -0
- package/dist/http-auth.js.map +1 -0
- package/dist/http-policy.d.ts +30 -0
- package/dist/http-policy.d.ts.map +1 -0
- package/dist/http-policy.js +54 -0
- package/dist/http-policy.js.map +1 -0
- package/dist/http-server.js +195 -12
- package/dist/http-server.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -15
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +56 -2
- package/dist/logger.js.map +1 -1
- package/dist/plugins/types.d.ts +2 -2
- package/dist/sbom/provenance.test.js +2 -2
- package/dist/sbom/provenance.test.js.map +1 -1
- package/dist/sbom/signing.d.ts.map +1 -1
- package/dist/sbom/signing.js +5 -3
- package/dist/sbom/signing.js.map +1 -1
- package/dist/scanners/agent/prompt-injection-fuzzer.d.ts.map +1 -1
- package/dist/scanners/agent/prompt-injection-fuzzer.js +26 -0
- package/dist/scanners/agent/prompt-injection-fuzzer.js.map +1 -1
- package/dist/scanners/agent/types.d.ts +10 -10
- package/dist/scanners/bandit.d.ts.map +1 -1
- package/dist/scanners/bandit.js +35 -29
- package/dist/scanners/bandit.js.map +1 -1
- package/dist/scanners/binary-analysis.d.ts.map +1 -1
- package/dist/scanners/binary-analysis.js +24 -49
- package/dist/scanners/binary-analysis.js.map +1 -1
- package/dist/scanners/brakeman.d.ts.map +1 -1
- package/dist/scanners/brakeman.js +19 -33
- package/dist/scanners/brakeman.js.map +1 -1
- package/dist/scanners/builtin-rules.d.ts +24 -0
- package/dist/scanners/builtin-rules.d.ts.map +1 -0
- package/dist/scanners/builtin-rules.js +175 -0
- package/dist/scanners/builtin-rules.js.map +1 -0
- package/dist/scanners/dast.d.ts.map +1 -1
- package/dist/scanners/dast.js +24 -34
- package/dist/scanners/dast.js.map +1 -1
- package/dist/scanners/deploy/types.d.ts +6 -6
- package/dist/scanners/eslint.d.ts.map +1 -1
- package/dist/scanners/eslint.js +15 -24
- package/dist/scanners/eslint.js.map +1 -1
- package/dist/scanners/gosec.d.ts.map +1 -1
- package/dist/scanners/gosec.js +14 -62
- package/dist/scanners/gosec.js.map +1 -1
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +38 -7
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/memory-safety.d.ts.map +1 -1
- package/dist/scanners/memory-safety.js +27 -28
- package/dist/scanners/memory-safety.js.map +1 -1
- package/dist/scanners/openapi.d.ts.map +1 -1
- package/dist/scanners/openapi.js +14 -22
- package/dist/scanners/openapi.js.map +1 -1
- package/dist/scanners/race-condition.d.ts.map +1 -1
- package/dist/scanners/race-condition.js +17 -16
- package/dist/scanners/race-condition.js.map +1 -1
- package/dist/scanners/runtime/types.d.ts +4 -4
- package/dist/scanners/rust.d.ts.map +1 -1
- package/dist/scanners/rust.js +38 -37
- package/dist/scanners/rust.js.map +1 -1
- package/dist/scanners/scale/types.d.ts +16 -16
- package/dist/scanners/secrets.d.ts.map +1 -1
- package/dist/scanners/secrets.js +66 -78
- package/dist/scanners/secrets.js.map +1 -1
- package/dist/scanners/semgrep.d.ts +2 -0
- package/dist/scanners/semgrep.d.ts.map +1 -1
- package/dist/scanners/semgrep.js +12 -0
- package/dist/scanners/semgrep.js.map +1 -1
- package/dist/scanners/terraform.d.ts.map +1 -1
- package/dist/scanners/terraform.js +47 -40
- package/dist/scanners/terraform.js.map +1 -1
- package/dist/scanners/trivy.d.ts.map +1 -1
- package/dist/scanners/trivy.js +38 -30
- package/dist/scanners/trivy.js.map +1 -1
- package/dist/tool-guard.d.ts +40 -0
- package/dist/tool-guard.d.ts.map +1 -0
- package/dist/tool-guard.js +55 -0
- package/dist/tool-guard.js.map +1 -0
- package/dist/util/index.d.ts +2 -1
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +2 -1
- package/dist/util/index.js.map +1 -1
- package/dist/util/paths.d.ts +20 -3
- package/dist/util/paths.d.ts.map +1 -1
- package/dist/util/paths.js +84 -4
- package/dist/util/paths.js.map +1 -1
- package/dist/util/subprocess.d.ts +51 -0
- package/dist/util/subprocess.d.ts.map +1 -0
- package/dist/util/subprocess.js +77 -0
- package/dist/util/subprocess.js.map +1 -0
- package/package.json +12 -2
- package/dist/eval/fixtures/healthcare/audit-gaps.d.ts +0 -28
- package/dist/eval/fixtures/healthcare/audit-gaps.d.ts.map +0 -1
- package/dist/eval/fixtures/healthcare/audit-gaps.js +0 -90
- package/dist/eval/fixtures/healthcare/audit-gaps.js.map +0 -1
- package/dist/eval/fixtures/healthcare/consent-bypass.d.ts +0 -31
- package/dist/eval/fixtures/healthcare/consent-bypass.d.ts.map +0 -1
- package/dist/eval/fixtures/healthcare/consent-bypass.js +0 -61
- package/dist/eval/fixtures/healthcare/consent-bypass.js.map +0 -1
- package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts +0 -24
- package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts.map +0 -1
- package/dist/eval/fixtures/healthcare/phi-in-logs.js +0 -41
- package/dist/eval/fixtures/healthcare/phi-in-logs.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,10 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { readdir, readFile, writeFile, mkdir, stat, access } from "fs/promises";
|
|
6
|
-
import {
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
import { join, basename, dirname } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
7
10
|
import { COMMANDS, listCommands, getCommand } from "./commands/index.js";
|
|
8
11
|
import { logger, createChildLogger } from "./logger.js";
|
|
9
12
|
import { generateCertificationId, initializeCertification, getCertification, startAgent, getAgentFindings, submitFinding, completeAgent, addCrossVerification, addRedTeamChallenge, saveConsensus, finalizeCertification, getLatestCertification, isCertificationValid, calculateConsensus, canFinalize, writeCertificationArtifacts, updateCertificationMetadata,
|
|
@@ -43,6 +46,7 @@ runSingleFrameworkAssessment, runComplianceAssessment, generateComplianceSummary
|
|
|
43
46
|
import { collectEvidence, storeEvidenceBundle, formatEvidenceBundleAsMarkdown, } from "./evidence/index.js";
|
|
44
47
|
import { verifyHistoryIntegrity, formatVerificationResultAsMarkdown, } from "./history/verify.js";
|
|
45
48
|
import { exportAuditTrail, } from "./history/store.js";
|
|
49
|
+
import { recordDecision, getDecisionProvenance } from "./history/decisions.js";
|
|
46
50
|
// SIEM Integration
|
|
47
51
|
import { createSIEMClient, getSIEMRegistry, createFindingEvent, } from "./integrations/siem/index.js";
|
|
48
52
|
// SBOM, Provenance, and Sigstore Signing (uses @sigstore/sign for real signing)
|
|
@@ -52,7 +56,11 @@ import { getTracker, formatCost, formatTokens, estimateCost, getSupportedModels,
|
|
|
52
56
|
// Multi-model consensus
|
|
53
57
|
import { getRunner, DEFAULT_MODELS, formatProvider, } from "./multimodel/index.js";
|
|
54
58
|
// Path validation utilities
|
|
55
|
-
import { validateProjectPath, PathValidationError } from "./util/paths.js";
|
|
59
|
+
import { validateProjectPath, PathValidationError, resolveContainedFile, resolveContainedWritePath, } from "./util/paths.js";
|
|
60
|
+
import { parseJson, tryParseJson } from "./util/json.js";
|
|
61
|
+
import { applyProjectPathGuard } from "./tool-guard.js";
|
|
62
|
+
import { finalizeCertificate, verifyCertificate, CertificateError, } from "./certification/agent-certificate.js";
|
|
63
|
+
import { certificationToCertificateBody, baselineCertificateBody, } from "./certification/agent-certificate-map.js";
|
|
56
64
|
// Telemetry and scan registry
|
|
57
65
|
import { trackCertificationStarted, trackCertificationCompleted, trackScannerRun, } from "./telemetry/usage.js";
|
|
58
66
|
import { getRegistry } from "./telemetry/registry.js";
|
|
@@ -159,10 +167,18 @@ function parseAuditCounts(content) {
|
|
|
159
167
|
// ---------------------------------------------------------------------------
|
|
160
168
|
// Server
|
|
161
169
|
// ---------------------------------------------------------------------------
|
|
170
|
+
const PKG_PATH = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
171
|
+
const PKG_VERSION = parseJson(readFileSync(PKG_PATH, "utf-8"), "package.json").version;
|
|
162
172
|
const server = new McpServer({
|
|
163
173
|
name: "vaspera-hardening-mcp-server",
|
|
164
|
-
version:
|
|
174
|
+
version: PKG_VERSION,
|
|
165
175
|
});
|
|
176
|
+
// Every tool with a project_path input gets validateProjectPath() by
|
|
177
|
+
// construction (CONSTITUTION rule 3). Set VASPERA_PATH_BOUNDARY to also
|
|
178
|
+
// confine all scans to a workspace root.
|
|
179
|
+
applyProjectPathGuard(server, process.env.VASPERA_PATH_BOUNDARY
|
|
180
|
+
? { basePath: process.env.VASPERA_PATH_BOUNDARY }
|
|
181
|
+
: {});
|
|
166
182
|
// ---------------------------------------------------------------------------
|
|
167
183
|
// Tool: List Projects
|
|
168
184
|
// ---------------------------------------------------------------------------
|
|
@@ -1587,6 +1603,170 @@ server.registerTool("redteam_challenge", {
|
|
|
1587
1603
|
};
|
|
1588
1604
|
});
|
|
1589
1605
|
// ---------------------------------------------------------------------------
|
|
1606
|
+
// Tool: Antagonist Synthesize
|
|
1607
|
+
// ---------------------------------------------------------------------------
|
|
1608
|
+
server.registerTool("antagonist_synthesize", {
|
|
1609
|
+
title: "Synthesize Attack Narratives",
|
|
1610
|
+
description: `Run antagonist analysis after all agents complete. Synthesizes findings into attack narratives and challenges assumptions. Two modes: "synthesis" (attack narratives), "challenger" (internal critic), or "both".`,
|
|
1611
|
+
inputSchema: {
|
|
1612
|
+
project_path: z.string().describe("Absolute path to the project root"),
|
|
1613
|
+
certification_id: z.string().describe("Certification ID"),
|
|
1614
|
+
mode: z.enum(["synthesis", "challenger", "both"]).optional().default("both").describe("Analysis mode"),
|
|
1615
|
+
include_prioritization: z.boolean().optional().default(true).describe("Include remediation prioritization"),
|
|
1616
|
+
use_llm: z.boolean().optional().default(true).describe("Use LLM for enhanced analysis"),
|
|
1617
|
+
},
|
|
1618
|
+
annotations: {
|
|
1619
|
+
readOnlyHint: false,
|
|
1620
|
+
destructiveHint: false,
|
|
1621
|
+
idempotentHint: true,
|
|
1622
|
+
openWorldHint: false,
|
|
1623
|
+
},
|
|
1624
|
+
}, async ({ project_path, certification_id, mode = "both", include_prioritization = true, use_llm = true }) => {
|
|
1625
|
+
const { runAntagonistAnalysis } = await import("./agents/antagonist/index.js");
|
|
1626
|
+
const { analyzeExploitChains } = await import("./agents/exploit-chain.js");
|
|
1627
|
+
const certification = await getCertification(project_path, certification_id);
|
|
1628
|
+
if (!certification) {
|
|
1629
|
+
return errorResponse(`Certification ${certification_id} not found`);
|
|
1630
|
+
}
|
|
1631
|
+
const ready = await allAgentsCompleted(project_path, certification_id);
|
|
1632
|
+
if (!ready) {
|
|
1633
|
+
return errorResponse("Not all agents have completed. Run antagonist after all agents finish.");
|
|
1634
|
+
}
|
|
1635
|
+
const allFindings = [];
|
|
1636
|
+
const agentSummaries = {};
|
|
1637
|
+
for (const [agentName, agentData] of Object.entries(certification.agents)) {
|
|
1638
|
+
if (agentData) {
|
|
1639
|
+
allFindings.push(...agentData.findings);
|
|
1640
|
+
agentSummaries[agentName] = {
|
|
1641
|
+
completed: agentData.status === "completed",
|
|
1642
|
+
findingCount: agentData.findings.length,
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
const chainResult = await analyzeExploitChains(allFindings);
|
|
1647
|
+
const result = await runAntagonistAnalysis({
|
|
1648
|
+
projectPath: project_path,
|
|
1649
|
+
certificationId: certification_id,
|
|
1650
|
+
findings: allFindings,
|
|
1651
|
+
exploitChains: chainResult.chains,
|
|
1652
|
+
exfilPaths: [],
|
|
1653
|
+
agentSummaries: agentSummaries,
|
|
1654
|
+
}, {
|
|
1655
|
+
mode,
|
|
1656
|
+
includePrioritization: include_prioritization,
|
|
1657
|
+
useLlm: use_llm,
|
|
1658
|
+
});
|
|
1659
|
+
return jsonResponse({
|
|
1660
|
+
success: result.success,
|
|
1661
|
+
analysisId: result.analysisId,
|
|
1662
|
+
narrativesFound: result.attackNarratives.length,
|
|
1663
|
+
challengesFound: result.challengerAssessments.length,
|
|
1664
|
+
coverageScore: result.gapAnalysis.coverageScore,
|
|
1665
|
+
summary: result.summary,
|
|
1666
|
+
attackNarratives: result.attackNarratives.slice(0, 5).map((n) => ({
|
|
1667
|
+
name: n.name,
|
|
1668
|
+
likelihood: n.likelihood,
|
|
1669
|
+
difficulty: n.difficulty,
|
|
1670
|
+
impact: n.impact,
|
|
1671
|
+
findingCount: n.findingIds.length,
|
|
1672
|
+
narrative: n.narrative.slice(0, 300),
|
|
1673
|
+
})),
|
|
1674
|
+
challenges: result.challengerAssessments.slice(0, 5).map((c) => ({
|
|
1675
|
+
type: c.type,
|
|
1676
|
+
targetAgent: c.targetAgent,
|
|
1677
|
+
challenge: c.challenge,
|
|
1678
|
+
severity: c.severity,
|
|
1679
|
+
})),
|
|
1680
|
+
prioritization: result.prioritization.slice(0, 5).map((p) => ({
|
|
1681
|
+
order: p.order,
|
|
1682
|
+
findingId: p.findingId,
|
|
1683
|
+
reason: p.reason,
|
|
1684
|
+
effort: p.effort,
|
|
1685
|
+
impact: p.impact,
|
|
1686
|
+
})),
|
|
1687
|
+
gapAnalysis: {
|
|
1688
|
+
coverageScore: result.gapAnalysis.coverageScore,
|
|
1689
|
+
untestedVectors: result.gapAnalysis.untestedAttackVectors.slice(0, 5),
|
|
1690
|
+
recommendations: result.gapAnalysis.recommendations.slice(0, 3),
|
|
1691
|
+
},
|
|
1692
|
+
duration: result.duration,
|
|
1693
|
+
tokensUsed: result.tokensUsed,
|
|
1694
|
+
});
|
|
1695
|
+
});
|
|
1696
|
+
// ---------------------------------------------------------------------------
|
|
1697
|
+
// Tool: Antagonist Challenge
|
|
1698
|
+
// ---------------------------------------------------------------------------
|
|
1699
|
+
server.registerTool("antagonist_challenge", {
|
|
1700
|
+
title: "Challenge Finding",
|
|
1701
|
+
description: `Challenge a specific finding from another agent. Used to flag potential false positives, missing context, or wrong severity.`,
|
|
1702
|
+
inputSchema: {
|
|
1703
|
+
project_path: z.string().describe("Absolute path to the project root"),
|
|
1704
|
+
certification_id: z.string().describe("Certification ID"),
|
|
1705
|
+
target_finding_id: z.string().describe("Finding ID to challenge"),
|
|
1706
|
+
challenge_type: z.enum(["false_positive", "missing_context", "wrong_severity", "wrong_assumption"]).describe("Type of challenge"),
|
|
1707
|
+
evidence: z.string().describe("Evidence supporting the challenge"),
|
|
1708
|
+
},
|
|
1709
|
+
annotations: {
|
|
1710
|
+
readOnlyHint: false,
|
|
1711
|
+
destructiveHint: false,
|
|
1712
|
+
idempotentHint: false,
|
|
1713
|
+
openWorldHint: false,
|
|
1714
|
+
},
|
|
1715
|
+
}, async ({ project_path, certification_id, target_finding_id, challenge_type, evidence }) => {
|
|
1716
|
+
const certification = await getCertification(project_path, certification_id);
|
|
1717
|
+
if (!certification) {
|
|
1718
|
+
return errorResponse(`Certification ${certification_id} not found`);
|
|
1719
|
+
}
|
|
1720
|
+
let targetFinding = null;
|
|
1721
|
+
let targetAgent = null;
|
|
1722
|
+
for (const [agentName, agentData] of Object.entries(certification.agents)) {
|
|
1723
|
+
if (agentData) {
|
|
1724
|
+
const found = agentData.findings.find((f) => f.id === target_finding_id);
|
|
1725
|
+
if (found) {
|
|
1726
|
+
targetFinding = found;
|
|
1727
|
+
targetAgent = agentName;
|
|
1728
|
+
break;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
if (!targetFinding || !targetAgent) {
|
|
1733
|
+
return errorResponse(`Finding ${target_finding_id} not found`);
|
|
1734
|
+
}
|
|
1735
|
+
const challengeTypeMap = {
|
|
1736
|
+
false_positive: "false_positive_likely",
|
|
1737
|
+
missing_context: "insufficient_evidence",
|
|
1738
|
+
wrong_severity: "wrong_severity",
|
|
1739
|
+
wrong_assumption: "wrong_assumption",
|
|
1740
|
+
};
|
|
1741
|
+
const assessment = {
|
|
1742
|
+
id: `chal-manual-${Date.now().toString(36)}`,
|
|
1743
|
+
type: challengeTypeMap[challenge_type] || challenge_type,
|
|
1744
|
+
targetAgent,
|
|
1745
|
+
targetFindingId: target_finding_id,
|
|
1746
|
+
challenge: `Manual challenge: ${challenge_type}`,
|
|
1747
|
+
evidence,
|
|
1748
|
+
suggestedAction: challenge_type === "false_positive"
|
|
1749
|
+
? "Review and potentially dismiss this finding"
|
|
1750
|
+
: challenge_type === "wrong_severity"
|
|
1751
|
+
? "Re-evaluate severity level"
|
|
1752
|
+
: "Provide additional context or evidence",
|
|
1753
|
+
severity: targetFinding.severity,
|
|
1754
|
+
confidence: 90,
|
|
1755
|
+
};
|
|
1756
|
+
await addCrossVerification(project_path, certification_id, {
|
|
1757
|
+
finding_id: target_finding_id,
|
|
1758
|
+
verifying_agent: "antagonist",
|
|
1759
|
+
verdict: "disputed",
|
|
1760
|
+
evidence: `[${challenge_type}] ${evidence}`,
|
|
1761
|
+
});
|
|
1762
|
+
return jsonResponse({
|
|
1763
|
+
success: true,
|
|
1764
|
+
challenge: assessment,
|
|
1765
|
+
findingDisputed: target_finding_id,
|
|
1766
|
+
message: `Finding ${target_finding_id} has been challenged and marked as disputed.`,
|
|
1767
|
+
});
|
|
1768
|
+
});
|
|
1769
|
+
// ---------------------------------------------------------------------------
|
|
1590
1770
|
// Tool: Calculate Consensus
|
|
1591
1771
|
// ---------------------------------------------------------------------------
|
|
1592
1772
|
server.registerTool("certification_consensus", {
|
|
@@ -1745,6 +1925,181 @@ server.registerTool("certification_finalize", {
|
|
|
1745
1925
|
};
|
|
1746
1926
|
});
|
|
1747
1927
|
// ---------------------------------------------------------------------------
|
|
1928
|
+
// Tool: Generate Agent Certificate
|
|
1929
|
+
// ---------------------------------------------------------------------------
|
|
1930
|
+
server.registerTool("agent_certificate_generate", {
|
|
1931
|
+
title: "Generate Agent Certificate",
|
|
1932
|
+
description: `Produce a versioned, tamper-evident agent certificate (v1) across six dimensions (security, scalability, quality, explainability, compliance, AI-BOM). If certification_id is provided, the real certification run is mapped into the certificate; dimensions not covered are marked not_assessed rather than fabricated. The certificate carries a sha256 content digest and is Sigstore-signed when an OIDC identity is available (CI), otherwise unsigned-but-digested.`,
|
|
1933
|
+
inputSchema: {
|
|
1934
|
+
project_path: z.string().describe("Absolute path to the project root"),
|
|
1935
|
+
certification_id: z
|
|
1936
|
+
.string()
|
|
1937
|
+
.optional()
|
|
1938
|
+
.describe("Map an existing certification run into the certificate"),
|
|
1939
|
+
subject_name: z.string().optional().describe("Override the certified subject name"),
|
|
1940
|
+
subject_kind: z
|
|
1941
|
+
.enum(["agent", "mcp-server", "codebase"])
|
|
1942
|
+
.optional()
|
|
1943
|
+
.describe("Kind of subject being certified (default: codebase)"),
|
|
1944
|
+
compliance_frameworks: z
|
|
1945
|
+
.array(z.enum([
|
|
1946
|
+
"ISO-42001",
|
|
1947
|
+
"NIST-AI-RMF",
|
|
1948
|
+
"OWASP-LLM",
|
|
1949
|
+
"SOC2",
|
|
1950
|
+
"ISO27001",
|
|
1951
|
+
"HIPAA",
|
|
1952
|
+
"GDPR",
|
|
1953
|
+
"PCI-DSS",
|
|
1954
|
+
"NIST-800-53",
|
|
1955
|
+
]))
|
|
1956
|
+
.optional()
|
|
1957
|
+
.describe("Frameworks to evaluate for the compliance dimension (requires certification_id)"),
|
|
1958
|
+
validity_days: z
|
|
1959
|
+
.number()
|
|
1960
|
+
.int()
|
|
1961
|
+
.positive()
|
|
1962
|
+
.max(3650)
|
|
1963
|
+
.optional()
|
|
1964
|
+
.describe("Certificate validity window in days (default 90)"),
|
|
1965
|
+
sign: z
|
|
1966
|
+
.boolean()
|
|
1967
|
+
.optional()
|
|
1968
|
+
.describe("Attempt Sigstore signing (default true; falls back to unsigned)"),
|
|
1969
|
+
},
|
|
1970
|
+
annotations: {
|
|
1971
|
+
readOnlyHint: true,
|
|
1972
|
+
destructiveHint: false,
|
|
1973
|
+
idempotentHint: false,
|
|
1974
|
+
openWorldHint: false,
|
|
1975
|
+
},
|
|
1976
|
+
}, async ({ project_path, certification_id, subject_name, subject_kind, compliance_frameworks, validity_days, sign, }) => {
|
|
1977
|
+
const now = new Date();
|
|
1978
|
+
const expires = new Date(now.getTime() + (validity_days ?? 90) * 86400000);
|
|
1979
|
+
const certificateId = `vac_${randomUUID()}`;
|
|
1980
|
+
let body;
|
|
1981
|
+
if (certification_id) {
|
|
1982
|
+
const cert = await getCertification(project_path, certification_id);
|
|
1983
|
+
if (!cert) {
|
|
1984
|
+
return errorResponse(`Certification ${certification_id} not found`);
|
|
1985
|
+
}
|
|
1986
|
+
// Anchor the certificate to the tamper-evident decision chain.
|
|
1987
|
+
const provenance = await getDecisionProvenance(project_path);
|
|
1988
|
+
body = certificationToCertificateBody(cert, {
|
|
1989
|
+
toolVersion: PKG_VERSION,
|
|
1990
|
+
issuedAt: now.toISOString(),
|
|
1991
|
+
expiresAt: expires.toISOString(),
|
|
1992
|
+
certificateId,
|
|
1993
|
+
complianceFrameworks: compliance_frameworks,
|
|
1994
|
+
provenance,
|
|
1995
|
+
});
|
|
1996
|
+
if (subject_name)
|
|
1997
|
+
body.subject.name = subject_name;
|
|
1998
|
+
if (subject_kind)
|
|
1999
|
+
body.subject.kind = subject_kind;
|
|
2000
|
+
}
|
|
2001
|
+
else {
|
|
2002
|
+
// No certification supplied — baseline skeleton with all dimensions
|
|
2003
|
+
// not_assessed (honest, never fabricated).
|
|
2004
|
+
body = baselineCertificateBody({
|
|
2005
|
+
toolVersion: PKG_VERSION,
|
|
2006
|
+
issuedAt: now.toISOString(),
|
|
2007
|
+
expiresAt: expires.toISOString(),
|
|
2008
|
+
certificateId,
|
|
2009
|
+
subjectName: subject_name ?? basename(project_path),
|
|
2010
|
+
subjectKind: subject_kind ?? "codebase",
|
|
2011
|
+
identifier: project_path,
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
try {
|
|
2015
|
+
const certificate = await finalizeCertificate(body, { sign: sign ?? true });
|
|
2016
|
+
return jsonResponse({ certificate });
|
|
2017
|
+
}
|
|
2018
|
+
catch (error) {
|
|
2019
|
+
if (error instanceof CertificateError) {
|
|
2020
|
+
return errorResponse(`Certificate generation failed: ${error.message}`);
|
|
2021
|
+
}
|
|
2022
|
+
throw error;
|
|
2023
|
+
}
|
|
2024
|
+
});
|
|
2025
|
+
// ---------------------------------------------------------------------------
|
|
2026
|
+
// Tool: Verify Agent Certificate
|
|
2027
|
+
// ---------------------------------------------------------------------------
|
|
2028
|
+
server.registerTool("agent_certificate_verify", {
|
|
2029
|
+
title: "Verify Agent Certificate",
|
|
2030
|
+
description: `Independently verify an agent certificate without trusting the issuer: re-validate the schema, recompute the content digest from the canonical body, and check the Sigstore signature if present. Returns which checks passed and any errors.`,
|
|
2031
|
+
inputSchema: {
|
|
2032
|
+
certificate_json: z
|
|
2033
|
+
.string()
|
|
2034
|
+
.describe("The agent certificate as a JSON string"),
|
|
2035
|
+
},
|
|
2036
|
+
annotations: {
|
|
2037
|
+
readOnlyHint: true,
|
|
2038
|
+
destructiveHint: false,
|
|
2039
|
+
idempotentHint: true,
|
|
2040
|
+
openWorldHint: false,
|
|
2041
|
+
},
|
|
2042
|
+
}, async ({ certificate_json }) => {
|
|
2043
|
+
const parsed = tryParseJson(certificate_json, "certificate_json");
|
|
2044
|
+
if (parsed === undefined) {
|
|
2045
|
+
return errorResponse("certificate_json is not valid JSON");
|
|
2046
|
+
}
|
|
2047
|
+
const result = await verifyCertificate(parsed);
|
|
2048
|
+
return jsonResponse({ ...result });
|
|
2049
|
+
});
|
|
2050
|
+
// ---------------------------------------------------------------------------
|
|
2051
|
+
// Tool: Record AI Decision (provenance)
|
|
2052
|
+
// ---------------------------------------------------------------------------
|
|
2053
|
+
server.registerTool("decision_record", {
|
|
2054
|
+
title: "Record AI Decision (Provenance)",
|
|
2055
|
+
description: `Append a tamper-evident record of an AI decision to the project's hash-chained audit trail, for explainability and traceability. Raw input/prompt/output are stored as sha256 digests (+ optional summary), so the chain proves what was decided without retaining secrets. Verify the chain with verify_audit_trail.`,
|
|
2056
|
+
inputSchema: {
|
|
2057
|
+
project_path: z.string().describe("Absolute path to the project root"),
|
|
2058
|
+
decision_type: z
|
|
2059
|
+
.string()
|
|
2060
|
+
.describe("Kind of decision (tool_call, classification, generation, refusal, …)"),
|
|
2061
|
+
model: z.string().describe("Model that produced the decision"),
|
|
2062
|
+
model_version: z.string().optional(),
|
|
2063
|
+
input: z.string().describe("Input/context that led to the decision (digested, not stored raw)"),
|
|
2064
|
+
prompt: z.string().optional().describe("Prompt, if applicable (digested)"),
|
|
2065
|
+
output: z.string().describe("The output/decision (digested)"),
|
|
2066
|
+
tools_invoked: z.array(z.string()).optional(),
|
|
2067
|
+
summary: z.string().optional().describe("Short human-readable summary"),
|
|
2068
|
+
rationale: z.string().optional().describe("Rationale / explanation"),
|
|
2069
|
+
confidence: z.number().min(0).max(100).optional(),
|
|
2070
|
+
certification_id: z.string().optional(),
|
|
2071
|
+
sign: z.boolean().optional().describe("Sigstore-sign the entry (requires OIDC)"),
|
|
2072
|
+
},
|
|
2073
|
+
annotations: {
|
|
2074
|
+
readOnlyHint: false,
|
|
2075
|
+
destructiveHint: false,
|
|
2076
|
+
idempotentHint: false,
|
|
2077
|
+
openWorldHint: false,
|
|
2078
|
+
},
|
|
2079
|
+
}, async ({ project_path, decision_type, model, model_version, input, prompt, output, tools_invoked, summary, rationale, confidence, certification_id, sign, }) => {
|
|
2080
|
+
const entry = await recordDecision(project_path, {
|
|
2081
|
+
decisionType: decision_type,
|
|
2082
|
+
model,
|
|
2083
|
+
modelVersion: model_version,
|
|
2084
|
+
input,
|
|
2085
|
+
prompt,
|
|
2086
|
+
output,
|
|
2087
|
+
toolsInvoked: tools_invoked,
|
|
2088
|
+
summary,
|
|
2089
|
+
rationale,
|
|
2090
|
+
confidence,
|
|
2091
|
+
certificationId: certification_id,
|
|
2092
|
+
}, { sign: sign ?? false });
|
|
2093
|
+
return jsonResponse({
|
|
2094
|
+
recorded: true,
|
|
2095
|
+
id: entry.id,
|
|
2096
|
+
hash: entry.integrity?.hash,
|
|
2097
|
+
previousHash: entry.integrity?.previousHash,
|
|
2098
|
+
inputDigest: entry.inputDigest,
|
|
2099
|
+
outputDigest: entry.outputDigest,
|
|
2100
|
+
});
|
|
2101
|
+
});
|
|
2102
|
+
// ---------------------------------------------------------------------------
|
|
1748
2103
|
// Tool: Certification Dashboard
|
|
1749
2104
|
// ---------------------------------------------------------------------------
|
|
1750
2105
|
server.registerTool("certification_dashboard", {
|
|
@@ -1928,7 +2283,7 @@ server.registerTool("autofix_apply", {
|
|
|
1928
2283
|
},
|
|
1929
2284
|
annotations: {
|
|
1930
2285
|
readOnlyHint: false,
|
|
1931
|
-
destructiveHint:
|
|
2286
|
+
destructiveHint: true,
|
|
1932
2287
|
idempotentHint: false,
|
|
1933
2288
|
openWorldHint: false,
|
|
1934
2289
|
},
|
|
@@ -1982,7 +2337,7 @@ server.registerTool("autofix_batch", {
|
|
|
1982
2337
|
},
|
|
1983
2338
|
annotations: {
|
|
1984
2339
|
readOnlyHint: false,
|
|
1985
|
-
destructiveHint:
|
|
2340
|
+
destructiveHint: true,
|
|
1986
2341
|
idempotentHint: false,
|
|
1987
2342
|
openWorldHint: false,
|
|
1988
2343
|
},
|
|
@@ -2556,7 +2911,9 @@ server.registerTool("rules_check_file", {
|
|
|
2556
2911
|
message: "No custom rules configured. Use rules_generate_config to create a sample.",
|
|
2557
2912
|
});
|
|
2558
2913
|
}
|
|
2559
|
-
|
|
2914
|
+
// file_path is untrusted — contain it within the (already validated)
|
|
2915
|
+
// project tree before reading.
|
|
2916
|
+
const fullPath = await resolveContainedFile(project_path, file_path);
|
|
2560
2917
|
const content = await readFile(fullPath, "utf-8");
|
|
2561
2918
|
const matches = await checkFileAgainstRules(file_path, content, rules);
|
|
2562
2919
|
const findings = matchesToFindings(matches);
|
|
@@ -3509,9 +3866,10 @@ server.registerTool("sbom_generate", {
|
|
|
3509
3866
|
includeDevDependencies: include_dev,
|
|
3510
3867
|
includeVulnerabilities: include_vulnerabilities,
|
|
3511
3868
|
}, findings);
|
|
3512
|
-
// Write to file if requested
|
|
3869
|
+
// Write to file if requested. output_file is untrusted — contain
|
|
3870
|
+
// it within the project tree (no absolute-path escape hatch).
|
|
3513
3871
|
if (output_file) {
|
|
3514
|
-
const filePath =
|
|
3872
|
+
const filePath = await resolveContainedWritePath(project_path, output_file);
|
|
3515
3873
|
const content = output_format === "summary"
|
|
3516
3874
|
? generateSBOMSummary(sbom)
|
|
3517
3875
|
: JSON.stringify(sbom, null, 2);
|
|
@@ -4307,10 +4665,15 @@ server.registerTool("consensus_models", {
|
|
|
4307
4665
|
// ---------------------------------------------------------------------------
|
|
4308
4666
|
server.registerTool("consensus_clear", {
|
|
4309
4667
|
title: "Clear Recorded Results",
|
|
4310
|
-
description: `Clear previously recorded findings for a certification. Use before re-recording results for fresh consensus calculation.`,
|
|
4668
|
+
description: `Clear previously recorded findings for a certification. Use before re-recording results for fresh consensus calculation. Fail-closed: pass confirm: true to actually clear; otherwise returns a no-op preview.`,
|
|
4311
4669
|
inputSchema: {
|
|
4312
4670
|
certification_id: z.string().describe("Certification ID"),
|
|
4313
4671
|
agent: z.enum(["security", "reliability", "typesafety", "performance", "quality", "redteam"]).optional().describe("Specific agent to clear, or all if not specified"),
|
|
4672
|
+
confirm: z
|
|
4673
|
+
.boolean()
|
|
4674
|
+
.optional()
|
|
4675
|
+
.default(false)
|
|
4676
|
+
.describe("Must be true to actually clear recorded results; otherwise a no-op preview is returned"),
|
|
4314
4677
|
},
|
|
4315
4678
|
annotations: {
|
|
4316
4679
|
readOnlyHint: false,
|
|
@@ -4318,8 +4681,15 @@ server.registerTool("consensus_clear", {
|
|
|
4318
4681
|
idempotentHint: true,
|
|
4319
4682
|
openWorldHint: false,
|
|
4320
4683
|
},
|
|
4321
|
-
}, async ({ certification_id, agent }) => {
|
|
4684
|
+
}, async ({ certification_id, agent, confirm }) => {
|
|
4322
4685
|
try {
|
|
4686
|
+
if (!confirm) {
|
|
4687
|
+
return jsonResponse({
|
|
4688
|
+
preview: true,
|
|
4689
|
+
wouldClear: { certificationId: certification_id, agent: agent || "all" },
|
|
4690
|
+
message: `No-op preview: would clear recorded results for ${certification_id} (${agent || "all"} agents). Re-run with confirm: true to apply.`,
|
|
4691
|
+
});
|
|
4692
|
+
}
|
|
4323
4693
|
const runner = getRunner();
|
|
4324
4694
|
runner.clearResults(certification_id, agent);
|
|
4325
4695
|
return jsonResponse({
|
|
@@ -5922,10 +6292,15 @@ server.registerTool("deploy_vercel_list", {
|
|
|
5922
6292
|
}
|
|
5923
6293
|
});
|
|
5924
6294
|
server.registerTool("deploy_vercel_promote", {
|
|
5925
|
-
description: "Promote a Vercel preview deployment to production.",
|
|
6295
|
+
description: "Promote a Vercel preview deployment to production. Fail-closed: pass confirm: true to actually promote; otherwise returns a no-op preview.",
|
|
5926
6296
|
inputSchema: {
|
|
5927
6297
|
project_id: z.string().describe("Vercel project ID"),
|
|
5928
6298
|
deployment_id: z.string().describe("Deployment ID to promote"),
|
|
6299
|
+
confirm: z
|
|
6300
|
+
.boolean()
|
|
6301
|
+
.optional()
|
|
6302
|
+
.default(false)
|
|
6303
|
+
.describe("Must be true to actually promote to production; otherwise a no-op preview is returned"),
|
|
5929
6304
|
},
|
|
5930
6305
|
annotations: {
|
|
5931
6306
|
readOnlyHint: false,
|
|
@@ -5933,8 +6308,15 @@ server.registerTool("deploy_vercel_promote", {
|
|
|
5933
6308
|
idempotentHint: false,
|
|
5934
6309
|
openWorldHint: true,
|
|
5935
6310
|
},
|
|
5936
|
-
}, async ({ project_id, deployment_id }) => {
|
|
6311
|
+
}, async ({ project_id, deployment_id, confirm }) => {
|
|
5937
6312
|
try {
|
|
6313
|
+
if (!confirm) {
|
|
6314
|
+
return jsonResponse({
|
|
6315
|
+
preview: true,
|
|
6316
|
+
wouldPromote: { project_id, deployment_id },
|
|
6317
|
+
message: `No-op preview: would promote deployment ${deployment_id} to production. Re-run with confirm: true to apply.`,
|
|
6318
|
+
});
|
|
6319
|
+
}
|
|
5938
6320
|
if (!isVercelAvailable()) {
|
|
5939
6321
|
return errorResponse("VERCEL_TOKEN not configured");
|
|
5940
6322
|
}
|
|
@@ -5952,10 +6334,15 @@ server.registerTool("deploy_vercel_promote", {
|
|
|
5952
6334
|
}
|
|
5953
6335
|
});
|
|
5954
6336
|
server.registerTool("deploy_vercel_rollback", {
|
|
5955
|
-
description: "Rollback to a previous Vercel deployment.",
|
|
6337
|
+
description: "Rollback to a previous Vercel deployment. Fail-closed: pass confirm: true to actually roll back; otherwise returns a no-op preview.",
|
|
5956
6338
|
inputSchema: {
|
|
5957
6339
|
project_id: z.string().describe("Vercel project ID"),
|
|
5958
6340
|
deployment_id: z.string().describe("Deployment ID to rollback to"),
|
|
6341
|
+
confirm: z
|
|
6342
|
+
.boolean()
|
|
6343
|
+
.optional()
|
|
6344
|
+
.default(false)
|
|
6345
|
+
.describe("Must be true to actually roll back production; otherwise a no-op preview is returned"),
|
|
5959
6346
|
},
|
|
5960
6347
|
annotations: {
|
|
5961
6348
|
readOnlyHint: false,
|
|
@@ -5963,8 +6350,15 @@ server.registerTool("deploy_vercel_rollback", {
|
|
|
5963
6350
|
idempotentHint: false,
|
|
5964
6351
|
openWorldHint: true,
|
|
5965
6352
|
},
|
|
5966
|
-
}, async ({ project_id, deployment_id }) => {
|
|
6353
|
+
}, async ({ project_id, deployment_id, confirm }) => {
|
|
5967
6354
|
try {
|
|
6355
|
+
if (!confirm) {
|
|
6356
|
+
return jsonResponse({
|
|
6357
|
+
preview: true,
|
|
6358
|
+
wouldRollbackTo: { project_id, deployment_id },
|
|
6359
|
+
message: `No-op preview: would roll back ${project_id} to deployment ${deployment_id}. Re-run with confirm: true to apply.`,
|
|
6360
|
+
});
|
|
6361
|
+
}
|
|
5968
6362
|
if (!isVercelAvailable()) {
|
|
5969
6363
|
return errorResponse("VERCEL_TOKEN not configured");
|
|
5970
6364
|
}
|
|
@@ -6057,7 +6451,9 @@ Use this for fast feedback on individual files.`,
|
|
|
6057
6451
|
},
|
|
6058
6452
|
}, async ({ project_path, file_path }) => {
|
|
6059
6453
|
try {
|
|
6060
|
-
|
|
6454
|
+
// file_path is untrusted — contain it within the (already
|
|
6455
|
+
// validated) project tree (no absolute-path escape hatch).
|
|
6456
|
+
const fullPath = await resolveContainedFile(project_path, file_path);
|
|
6061
6457
|
const result = await quickAICheck(fullPath, project_path);
|
|
6062
6458
|
return jsonResponse({
|
|
6063
6459
|
file: file_path,
|