vaspera 2.14.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 +45 -0
- package/README.md +15 -2
- 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/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/store.d.ts.map +1 -1
- package/dist/certification/store.js +5 -2
- package/dist/certification/store.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 +247 -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/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
|
// ---------------------------------------------------------------------------
|
|
@@ -1909,6 +1925,181 @@ server.registerTool("certification_finalize", {
|
|
|
1909
1925
|
};
|
|
1910
1926
|
});
|
|
1911
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
|
+
// ---------------------------------------------------------------------------
|
|
1912
2103
|
// Tool: Certification Dashboard
|
|
1913
2104
|
// ---------------------------------------------------------------------------
|
|
1914
2105
|
server.registerTool("certification_dashboard", {
|
|
@@ -2092,7 +2283,7 @@ server.registerTool("autofix_apply", {
|
|
|
2092
2283
|
},
|
|
2093
2284
|
annotations: {
|
|
2094
2285
|
readOnlyHint: false,
|
|
2095
|
-
destructiveHint:
|
|
2286
|
+
destructiveHint: true,
|
|
2096
2287
|
idempotentHint: false,
|
|
2097
2288
|
openWorldHint: false,
|
|
2098
2289
|
},
|
|
@@ -2146,7 +2337,7 @@ server.registerTool("autofix_batch", {
|
|
|
2146
2337
|
},
|
|
2147
2338
|
annotations: {
|
|
2148
2339
|
readOnlyHint: false,
|
|
2149
|
-
destructiveHint:
|
|
2340
|
+
destructiveHint: true,
|
|
2150
2341
|
idempotentHint: false,
|
|
2151
2342
|
openWorldHint: false,
|
|
2152
2343
|
},
|
|
@@ -2720,7 +2911,9 @@ server.registerTool("rules_check_file", {
|
|
|
2720
2911
|
message: "No custom rules configured. Use rules_generate_config to create a sample.",
|
|
2721
2912
|
});
|
|
2722
2913
|
}
|
|
2723
|
-
|
|
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);
|
|
2724
2917
|
const content = await readFile(fullPath, "utf-8");
|
|
2725
2918
|
const matches = await checkFileAgainstRules(file_path, content, rules);
|
|
2726
2919
|
const findings = matchesToFindings(matches);
|
|
@@ -3673,9 +3866,10 @@ server.registerTool("sbom_generate", {
|
|
|
3673
3866
|
includeDevDependencies: include_dev,
|
|
3674
3867
|
includeVulnerabilities: include_vulnerabilities,
|
|
3675
3868
|
}, findings);
|
|
3676
|
-
// 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).
|
|
3677
3871
|
if (output_file) {
|
|
3678
|
-
const filePath =
|
|
3872
|
+
const filePath = await resolveContainedWritePath(project_path, output_file);
|
|
3679
3873
|
const content = output_format === "summary"
|
|
3680
3874
|
? generateSBOMSummary(sbom)
|
|
3681
3875
|
: JSON.stringify(sbom, null, 2);
|
|
@@ -4471,10 +4665,15 @@ server.registerTool("consensus_models", {
|
|
|
4471
4665
|
// ---------------------------------------------------------------------------
|
|
4472
4666
|
server.registerTool("consensus_clear", {
|
|
4473
4667
|
title: "Clear Recorded Results",
|
|
4474
|
-
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.`,
|
|
4475
4669
|
inputSchema: {
|
|
4476
4670
|
certification_id: z.string().describe("Certification ID"),
|
|
4477
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"),
|
|
4478
4677
|
},
|
|
4479
4678
|
annotations: {
|
|
4480
4679
|
readOnlyHint: false,
|
|
@@ -4482,8 +4681,15 @@ server.registerTool("consensus_clear", {
|
|
|
4482
4681
|
idempotentHint: true,
|
|
4483
4682
|
openWorldHint: false,
|
|
4484
4683
|
},
|
|
4485
|
-
}, async ({ certification_id, agent }) => {
|
|
4684
|
+
}, async ({ certification_id, agent, confirm }) => {
|
|
4486
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
|
+
}
|
|
4487
4693
|
const runner = getRunner();
|
|
4488
4694
|
runner.clearResults(certification_id, agent);
|
|
4489
4695
|
return jsonResponse({
|
|
@@ -6086,10 +6292,15 @@ server.registerTool("deploy_vercel_list", {
|
|
|
6086
6292
|
}
|
|
6087
6293
|
});
|
|
6088
6294
|
server.registerTool("deploy_vercel_promote", {
|
|
6089
|
-
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.",
|
|
6090
6296
|
inputSchema: {
|
|
6091
6297
|
project_id: z.string().describe("Vercel project ID"),
|
|
6092
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"),
|
|
6093
6304
|
},
|
|
6094
6305
|
annotations: {
|
|
6095
6306
|
readOnlyHint: false,
|
|
@@ -6097,8 +6308,15 @@ server.registerTool("deploy_vercel_promote", {
|
|
|
6097
6308
|
idempotentHint: false,
|
|
6098
6309
|
openWorldHint: true,
|
|
6099
6310
|
},
|
|
6100
|
-
}, async ({ project_id, deployment_id }) => {
|
|
6311
|
+
}, async ({ project_id, deployment_id, confirm }) => {
|
|
6101
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
|
+
}
|
|
6102
6320
|
if (!isVercelAvailable()) {
|
|
6103
6321
|
return errorResponse("VERCEL_TOKEN not configured");
|
|
6104
6322
|
}
|
|
@@ -6116,10 +6334,15 @@ server.registerTool("deploy_vercel_promote", {
|
|
|
6116
6334
|
}
|
|
6117
6335
|
});
|
|
6118
6336
|
server.registerTool("deploy_vercel_rollback", {
|
|
6119
|
-
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.",
|
|
6120
6338
|
inputSchema: {
|
|
6121
6339
|
project_id: z.string().describe("Vercel project ID"),
|
|
6122
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"),
|
|
6123
6346
|
},
|
|
6124
6347
|
annotations: {
|
|
6125
6348
|
readOnlyHint: false,
|
|
@@ -6127,8 +6350,15 @@ server.registerTool("deploy_vercel_rollback", {
|
|
|
6127
6350
|
idempotentHint: false,
|
|
6128
6351
|
openWorldHint: true,
|
|
6129
6352
|
},
|
|
6130
|
-
}, async ({ project_id, deployment_id }) => {
|
|
6353
|
+
}, async ({ project_id, deployment_id, confirm }) => {
|
|
6131
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
|
+
}
|
|
6132
6362
|
if (!isVercelAvailable()) {
|
|
6133
6363
|
return errorResponse("VERCEL_TOKEN not configured");
|
|
6134
6364
|
}
|
|
@@ -6221,7 +6451,9 @@ Use this for fast feedback on individual files.`,
|
|
|
6221
6451
|
},
|
|
6222
6452
|
}, async ({ project_path, file_path }) => {
|
|
6223
6453
|
try {
|
|
6224
|
-
|
|
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);
|
|
6225
6457
|
const result = await quickAICheck(fullPath, project_path);
|
|
6226
6458
|
return jsonResponse({
|
|
6227
6459
|
file: file_path,
|