@jstn-sdk/ma 0.1.11 → 0.1.13
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/.codex/agents/Architect.toml +0 -2
- package/.codex/agents/Auditor.toml +0 -2
- package/.codex/agents/Builder.toml +0 -2
- package/.codex/agents/Flow.toml +0 -2
- package/.codex/agents/Sage.toml +0 -2
- package/.codex/agents/Vibe.toml +0 -2
- package/.codex/hooks.json +14 -13
- package/.codex/prompts/enforcement.md +4 -0
- package/.codex/prompts/skill-contract.md +4 -0
- package/COVERAGE.md +211 -0
- package/DEMO.md +265 -0
- package/README.md +287 -37
- package/bin/ma.js +232 -71
- package/data/clone-data.ledger.json +41 -0
- package/data/clone-data.proof.json +50 -0
- package/data/clone-data.rvf +37 -0
- package/docs/README.md +7 -1
- package/docs/getting-started.md +108 -37
- package/docs/installed-sdk.md +23 -0
- package/docs/mcp-setup.md +65 -1
- package/docs/onboarding.md +18 -2
- package/docs/prompt-guidance-contract.md +17 -0
- package/docs/prompt-guidance-fragments/active-autonomy-core.md +7 -0
- package/docs/qa/release-issue-gates-0.1.13.json +644 -0
- package/docs/qa/release-readiness-0.1.13.md +116 -0
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/reference/native-engineering-patterns.md +35 -0
- package/docs/reference/native-security-playbooks.md +27 -0
- package/docs/reference/native-source-selection.md +27 -0
- package/docs/reference/native-style-and-deslop.md +20 -0
- package/docs/release-spec.md +37 -10
- package/docs/skills-publishing.md +25 -1
- package/docs/skills.md +37 -12
- package/index.js +222 -2
- package/mcp/collections.json +23 -6
- package/mcp/local/code-intel.js +113 -0
- package/mcp/local/memory.js +46 -0
- package/mcp/local/playbooks.js +168 -0
- package/mcp/local/state.js +71 -0
- package/mcp/local/team-run.js +113 -0
- package/mcp/local/trace.js +60 -0
- package/mcp/local-capabilities.json +56 -0
- package/mcp/native-playbooks.json +117 -0
- package/mcp/servers.json +34 -0
- package/package.json +8 -2
- package/plugins/meta-architect/.app.json +1 -1
- package/plugins/meta-architect/.codex-plugin/plugin.json +4 -4
- package/plugins/meta-architect/.mcp.json +1 -1
- package/plugins/meta-architect/README.md +34 -1
- package/plugins/meta-architect/obsidian/main.js +401 -0
- package/plugins/meta-architect/obsidian/manifest.json +9 -0
- package/plugins/meta-architect/obsidian/styles.css +7 -0
- package/plugins/meta-architect/skills/align/SKILL.md +24 -0
- package/plugins/meta-architect/skills/align/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/align/references/shared-language.md +24 -0
- package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
- package/plugins/meta-architect/skills/build/SKILL.md +3 -0
- package/plugins/meta-architect/skills/cleanup/SKILL.md +23 -0
- package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +18 -0
- package/plugins/meta-architect/skills/diagnose/SKILL.md +24 -0
- package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/maestro/SKILL.md +36 -3
- package/plugins/meta-architect/skills/maestro/agents/openai.yaml +2 -2
- package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +44 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +5 -1
- package/plugins/meta-architect/skills/sage/references/source-selection.md +36 -0
- package/plugins/meta-architect/skills/tdd/SKILL.md +24 -0
- package/plugins/meta-architect/skills/tdd/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +5 -1
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +30 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/scripts/active-autonomy-hook.mjs +96 -0
- package/scripts/build-linux-packages.mjs +328 -0
- package/scripts/doctor.js +36 -4
- package/scripts/install.sh +28 -0
- package/scripts/linux-package-lib.mjs +40 -0
- package/scripts/linux-package-smoke.mjs +103 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-sync.js +13 -6
- package/scripts/release-verify.js +166 -1
- package/skills/align/SKILL.md +24 -0
- package/skills/align/agents/openai.yaml +4 -0
- package/skills/align/references/shared-language.md +24 -0
- package/skills/arch/SKILL.md +2 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/cleanup/SKILL.md +23 -0
- package/skills/cleanup/agents/openai.yaml +4 -0
- package/skills/cleanup/references/style-and-deslop.md +18 -0
- package/skills/diagnose/SKILL.md +24 -0
- package/skills/diagnose/agents/openai.yaml +4 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/index.json +21 -6
- package/skills/maestro/SKILL.md +36 -3
- package/skills/maestro/agents/openai.yaml +2 -2
- package/skills/maestro/references/native-ingest-map.md +44 -0
- package/skills/sage/SKILL.md +5 -1
- package/skills/sage/references/source-selection.md +36 -0
- package/skills/tdd/SKILL.md +24 -0
- package/skills/tdd/agents/openai.yaml +4 -0
- package/skills/vet/SKILL.md +5 -1
- package/skills/vet/references/security-playbooks.md +30 -0
- package/skills/vibe/SKILL.md +2 -0
- package/src/bootstrap.js +141 -24
- package/src/build-gate.js +2 -2
- package/src/decision-log.js +12 -9
- package/src/launcher.js +4 -0
- package/src/mcp-config.js +130 -8
- package/src/mcp-live-client.js +289 -3
- package/src/paths.js +37 -1
- package/src/policy.js +1 -1
- package/src/release-issue-gates.js +190 -0
- package/src/release-state.js +30 -1
- package/src/runtime/active-autonomy-core.js +208 -0
- package/src/runtime/alignment-sentinel.js +165 -0
- package/src/runtime/architect-review.js +88 -0
- package/src/runtime/build-readiness.js +62 -0
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/context-economy-core.js +276 -0
- package/src/runtime/continuity-notes.js +79 -0
- package/src/runtime/core-source-ingest.js +379 -0
- package/src/runtime/detached-provider.js +74 -0
- package/src/runtime/environment-awareness-core.js +460 -0
- package/src/runtime/exposure-catalog.js +276 -0
- package/src/runtime/guidance-stack.js +42 -0
- package/src/runtime/helper-orchestration-core.js +307 -0
- package/src/runtime/learning-loop-core.js +238 -0
- package/src/runtime/maestro-events.js +18 -0
- package/src/runtime/maestro-manager.js +605 -0
- package/src/runtime/maestro-state.js +125 -0
- package/src/runtime/mcp-policy.js +192 -0
- package/src/runtime/obsidian-integration-core.js +851 -0
- package/src/runtime/obsidian-plugin-bridge.js +739 -0
- package/src/runtime/orchestrator.js +158 -0
- package/src/runtime/prompt-strategy-core.js +230 -0
- package/src/runtime/quorum-review.js +90 -0
- package/src/runtime/ralph-execution-core.js +217 -0
- package/src/runtime/redaction-gateway.js +174 -0
- package/src/runtime/runtime-state.js +1223 -0
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +67 -0
- package/src/runtime/skills-registry-export.js +670 -0
- package/src/runtime/startup-path.js +14 -0
- package/src/runtime/universal-plugin-broker-core.js +575 -0
- package/src/runtime/workspace-intelligence-runtime.js +674 -0
- package/src/runtime/workspace-virtualizer.js +102 -0
- package/src/runtime/workspaces.js +25 -0
- package/src/runtime-artifacts.js +407 -84
- package/src/skill-installer.js +53 -5
- package/src/skills.js +1454 -78
- package/src/state-sync.js +51 -8
- package/docs/qa/release-readiness-0.1.11.md +0 -79
- package/plugins/meta-architect/skills/meta-architect/SKILL.md +0 -32
- package/plugins/meta-architect/skills/meta-architect/agents/openai.yaml +0 -4
- package/skills/meta-architect/SKILL.md +0 -32
- package/skills/meta-architect/agents/openai.yaml +0 -4
- /package/plugins/meta-architect/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
- /package/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
3
|
+
import { getRuntimeStatePath } from "../paths.js";
|
|
4
|
+
|
|
5
|
+
const defaultDependencyGraph = {
|
|
6
|
+
description: "Topological map of gate execution dependencies.",
|
|
7
|
+
nodes: {
|
|
8
|
+
$arch: { dependencies: [] },
|
|
9
|
+
$sage: { dependencies: ["$arch"] },
|
|
10
|
+
$flow: { dependencies: ["$sage"] },
|
|
11
|
+
$vet: { dependencies: ["$sage"] },
|
|
12
|
+
$vibe: { dependencies: ["$sage"] },
|
|
13
|
+
$build: { dependencies: ["$flow", "$vet", "$vibe"] },
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const validGlobalStatuses = new Set([
|
|
18
|
+
"IDLE",
|
|
19
|
+
"READY",
|
|
20
|
+
"RUNNING",
|
|
21
|
+
"COMPLETED",
|
|
22
|
+
"DEGRADED_HEALING",
|
|
23
|
+
"LOCKED",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export function getMaestroStatePath() {
|
|
27
|
+
return getRuntimeStatePath("maestro-state.json");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createDefaultMaestroState() {
|
|
31
|
+
return {
|
|
32
|
+
schemaVersion: "0.1.0",
|
|
33
|
+
orchestration_id: null,
|
|
34
|
+
timestamp: null,
|
|
35
|
+
global_status: "IDLE",
|
|
36
|
+
configuration: {
|
|
37
|
+
auto_heal: false,
|
|
38
|
+
max_healing_attempts: 2,
|
|
39
|
+
concurrency_limit: 4,
|
|
40
|
+
},
|
|
41
|
+
dependency_graph: defaultDependencyGraph,
|
|
42
|
+
runtime_tracks: {},
|
|
43
|
+
downstream_lock_table: {},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function validateMaestroState(state) {
|
|
48
|
+
if (!state || typeof state !== "object" || Array.isArray(state)) {
|
|
49
|
+
throw new Error("Maestro state must be an object");
|
|
50
|
+
}
|
|
51
|
+
if (state.schemaVersion !== "0.1.0") {
|
|
52
|
+
throw new Error(`Unsupported maestro state schemaVersion: ${state.schemaVersion}`);
|
|
53
|
+
}
|
|
54
|
+
if (!(state.orchestration_id === null || typeof state.orchestration_id === "string")) {
|
|
55
|
+
throw new Error("maestro_state.orchestration_id must be null or a string");
|
|
56
|
+
}
|
|
57
|
+
if (!(state.timestamp === null || typeof state.timestamp === "string")) {
|
|
58
|
+
throw new Error("maestro_state.timestamp must be null or a string");
|
|
59
|
+
}
|
|
60
|
+
if (!validGlobalStatuses.has(state.global_status)) {
|
|
61
|
+
throw new Error(`Unsupported maestro_state.global_status: ${state.global_status}`);
|
|
62
|
+
}
|
|
63
|
+
if (!state.configuration || typeof state.configuration !== "object") {
|
|
64
|
+
throw new Error("maestro_state.configuration must be an object");
|
|
65
|
+
}
|
|
66
|
+
if (!state.dependency_graph || typeof state.dependency_graph !== "object") {
|
|
67
|
+
throw new Error("maestro_state.dependency_graph must be an object");
|
|
68
|
+
}
|
|
69
|
+
if (!state.runtime_tracks || typeof state.runtime_tracks !== "object") {
|
|
70
|
+
throw new Error("maestro_state.runtime_tracks must be an object");
|
|
71
|
+
}
|
|
72
|
+
if (!state.downstream_lock_table || typeof state.downstream_lock_table !== "object") {
|
|
73
|
+
throw new Error("maestro_state.downstream_lock_table must be an object");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return state;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function seedMaestroStateArtifacts() {
|
|
80
|
+
await writeFileIfMissing(
|
|
81
|
+
getMaestroStatePath(),
|
|
82
|
+
`${JSON.stringify(createDefaultMaestroState(), null, 2)}\n`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function loadMaestroState() {
|
|
87
|
+
return validateMaestroState(await readJson(getMaestroStatePath()));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function loadMaestroStateOrDefault() {
|
|
91
|
+
try {
|
|
92
|
+
return await loadMaestroState();
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
95
|
+
return createDefaultMaestroState();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function saveMaestroState(state) {
|
|
103
|
+
validateMaestroState(state);
|
|
104
|
+
await writeJson(getMaestroStatePath(), state);
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function beginMaestroOrchestration(state, { globalStatus, configuration = null } = {}) {
|
|
109
|
+
const next = structuredClone(state);
|
|
110
|
+
if (
|
|
111
|
+
next.orchestration_id === null ||
|
|
112
|
+
["IDLE", "COMPLETED", "LOCKED"].includes(next.global_status)
|
|
113
|
+
) {
|
|
114
|
+
next.orchestration_id = `ma_run_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
115
|
+
}
|
|
116
|
+
next.timestamp = new Date().toISOString();
|
|
117
|
+
next.global_status = globalStatus;
|
|
118
|
+
if (configuration) {
|
|
119
|
+
next.configuration = {
|
|
120
|
+
...next.configuration,
|
|
121
|
+
...configuration,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return next;
|
|
125
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readJson } from "../fs-utils.js";
|
|
3
|
+
import {
|
|
4
|
+
getMcpLocalCapabilitiesPath,
|
|
5
|
+
getMcpServersPath,
|
|
6
|
+
getRuntimeMcpPolicyPath,
|
|
7
|
+
} from "../paths.js";
|
|
8
|
+
|
|
9
|
+
const allowedExecutionModes = new Set(["READ_ONLY", "SANDBOX_MUTABLE"]);
|
|
10
|
+
const hardenedProfiles = new Set(["project", "deep", "hardened"]);
|
|
11
|
+
|
|
12
|
+
function createFinding({ sourcePath, severity = "high", diagnostic, recommendedAction }) {
|
|
13
|
+
return {
|
|
14
|
+
record_type: "policy:mcp_validation",
|
|
15
|
+
severity,
|
|
16
|
+
confidence: "high",
|
|
17
|
+
source_path: sourcePath,
|
|
18
|
+
diagnostic,
|
|
19
|
+
recommended_action: recommendedAction,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isSafeRelativePath(value) {
|
|
24
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (path.isAbsolute(value)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const normalized = path.normalize(value);
|
|
31
|
+
return normalized !== ".." && !normalized.startsWith(`..${path.sep}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeArray(value) {
|
|
35
|
+
return Array.isArray(value) ? value : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function validateRuntimeMcpPolicy({
|
|
39
|
+
policy,
|
|
40
|
+
localCapabilities,
|
|
41
|
+
servers,
|
|
42
|
+
activeProfile = "project",
|
|
43
|
+
sourcePath = ".ma/.mcp.json",
|
|
44
|
+
} = {}) {
|
|
45
|
+
const findings = [];
|
|
46
|
+
const supportedCapabilities = new Set(
|
|
47
|
+
normalizeArray(localCapabilities?.capabilities).map((descriptor) => descriptor.capability),
|
|
48
|
+
);
|
|
49
|
+
const approvedEndpoints = new Set(
|
|
50
|
+
normalizeArray(servers?.servers)
|
|
51
|
+
.map((server) => server.endpoint)
|
|
52
|
+
.filter(Boolean),
|
|
53
|
+
);
|
|
54
|
+
const addFinding = (finding) => findings.push(createFinding({ sourcePath, ...finding }));
|
|
55
|
+
|
|
56
|
+
if (!policy || typeof policy !== "object" || Array.isArray(policy)) {
|
|
57
|
+
addFinding({
|
|
58
|
+
diagnostic: "Runtime-local MCP policy must be a JSON object.",
|
|
59
|
+
recommendedAction: "Replace .ma/.mcp.json with an object that narrows repo-owned MCP access.",
|
|
60
|
+
});
|
|
61
|
+
return findings;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (policy.schemaVersion !== undefined && policy.schemaVersion !== "0.1.0") {
|
|
65
|
+
addFinding({
|
|
66
|
+
severity: "warning",
|
|
67
|
+
diagnostic: `Unsupported runtime-local MCP policy schemaVersion: ${policy.schemaVersion}.`,
|
|
68
|
+
recommendedAction:
|
|
69
|
+
"Use schemaVersion 0.1.0 or omit schemaVersion until the contract changes.",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const policyMode = policy.executionMode ?? policy.execution_mode ?? "READ_ONLY";
|
|
74
|
+
if (!allowedExecutionModes.has(policyMode)) {
|
|
75
|
+
addFinding({
|
|
76
|
+
diagnostic: `Runtime-local MCP policy requests unsupported execution mode '${policyMode}'.`,
|
|
77
|
+
recommendedAction:
|
|
78
|
+
"Use READ_ONLY, or SANDBOX_MUTABLE only for explicitly permitted non-hardened runs.",
|
|
79
|
+
});
|
|
80
|
+
} else if (hardenedProfiles.has(activeProfile) && policyMode !== "READ_ONLY") {
|
|
81
|
+
addFinding({
|
|
82
|
+
diagnostic: `Runtime-local MCP policy cannot use ${policyMode} while profile '${activeProfile}' is hardened.`,
|
|
83
|
+
recommendedAction:
|
|
84
|
+
"Switch the runtime-local policy to READ_ONLY for project/deep/hardened profiles.",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const capability of normalizeArray(policy.capabilities)) {
|
|
89
|
+
const capabilityName = capability.capability ?? capability.name;
|
|
90
|
+
if (!supportedCapabilities.has(capabilityName)) {
|
|
91
|
+
addFinding({
|
|
92
|
+
diagnostic: `Runtime-local MCP policy references unsupported capability '${capabilityName ?? "(missing)"}'.`,
|
|
93
|
+
recommendedAction:
|
|
94
|
+
"Keep runtime-local capability entries as a subset of mcp/local-capabilities.json.",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const modulePath = capability.module;
|
|
99
|
+
if (modulePath !== undefined && !isSafeRelativePath(modulePath)) {
|
|
100
|
+
addFinding({
|
|
101
|
+
diagnostic: `Runtime-local MCP policy capability '${capabilityName ?? "(missing)"}' uses unsafe module path '${modulePath}'.`,
|
|
102
|
+
recommendedAction:
|
|
103
|
+
"Use safe repo-relative paths only; reject absolute paths and traversal.",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const capabilityMode = capability.executionMode ?? capability.execution_mode;
|
|
108
|
+
if (capabilityMode !== undefined && !allowedExecutionModes.has(capabilityMode)) {
|
|
109
|
+
addFinding({
|
|
110
|
+
diagnostic: `Runtime-local MCP policy capability '${capabilityName ?? "(missing)"}' requests unsupported execution mode '${capabilityMode}'.`,
|
|
111
|
+
recommendedAction:
|
|
112
|
+
"Use READ_ONLY, or SANDBOX_MUTABLE only where the active profile allows it.",
|
|
113
|
+
});
|
|
114
|
+
} else if (
|
|
115
|
+
capabilityMode !== undefined &&
|
|
116
|
+
hardenedProfiles.has(activeProfile) &&
|
|
117
|
+
capabilityMode !== "READ_ONLY"
|
|
118
|
+
) {
|
|
119
|
+
addFinding({
|
|
120
|
+
diagnostic: `Runtime-local MCP policy capability '${capabilityName ?? "(missing)"}' cannot use ${capabilityMode} while profile '${activeProfile}' is hardened.`,
|
|
121
|
+
recommendedAction: "Switch capability execution to READ_ONLY for hardened profiles.",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const allowedPath of normalizeArray(policy.allowedPaths ?? policy.allowed_paths)) {
|
|
127
|
+
if (!isSafeRelativePath(allowedPath)) {
|
|
128
|
+
addFinding({
|
|
129
|
+
diagnostic: `Runtime-local MCP policy contains unsafe allowed path '${allowedPath}'.`,
|
|
130
|
+
recommendedAction: "Use safe repo-relative allowed paths only.",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const endpoint of normalizeArray(policy.evidenceSources ?? policy.evidence_sources)) {
|
|
136
|
+
if (!approvedEndpoints.has(endpoint)) {
|
|
137
|
+
addFinding({
|
|
138
|
+
diagnostic: `Runtime-local MCP policy references unapproved evidence source '${endpoint}'.`,
|
|
139
|
+
recommendedAction:
|
|
140
|
+
"Keep runtime-local evidence sources as a subset of mcp/servers.json exact GitMCP endpoints.",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const envAllowlist =
|
|
146
|
+
policy.envAllowlist ?? policy.env_allowlist ?? policy.env?.allowlist ?? policy.env?.allow;
|
|
147
|
+
if (envAllowlist !== undefined) {
|
|
148
|
+
if (!Array.isArray(envAllowlist)) {
|
|
149
|
+
addFinding({
|
|
150
|
+
diagnostic: "Runtime-local MCP policy env allowlist must be an array.",
|
|
151
|
+
recommendedAction: "Use an explicit array of environment variable names.",
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
for (const entry of envAllowlist) {
|
|
155
|
+
if (
|
|
156
|
+
typeof entry !== "string" ||
|
|
157
|
+
entry.trim() === "" ||
|
|
158
|
+
entry === "*" ||
|
|
159
|
+
entry.includes("*")
|
|
160
|
+
) {
|
|
161
|
+
addFinding({
|
|
162
|
+
diagnostic: `Runtime-local MCP policy env allowlist contains unsafe entry '${entry}'.`,
|
|
163
|
+
recommendedAction:
|
|
164
|
+
"List exact environment variable names only; wildcard env access is not allowed.",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function validateRuntimeMcpPolicyFile({ activeProfile = "project" } = {}) {
|
|
175
|
+
let policy;
|
|
176
|
+
try {
|
|
177
|
+
policy = await readJson(getRuntimeMcpPolicyPath());
|
|
178
|
+
} catch (error) {
|
|
179
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return validateRuntimeMcpPolicy({
|
|
186
|
+
policy,
|
|
187
|
+
localCapabilities: await readJson(getMcpLocalCapabilitiesPath()),
|
|
188
|
+
servers: await readJson(getMcpServersPath()),
|
|
189
|
+
activeProfile,
|
|
190
|
+
sourcePath: getRuntimeMcpPolicyPath(),
|
|
191
|
+
});
|
|
192
|
+
}
|