@danielblomma/cortex-mcp 2.4.0 → 2.4.1
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 +201 -0
- package/README.md +19 -3
- package/bin/cortex.mjs +308 -75
- package/bin/daemon-control.mjs +162 -0
- package/package.json +7 -3
- package/scaffold/mcp/dist/.cortex-build-hash +1 -0
- package/scaffold/mcp/dist/cli/enterprise-setup.js +134 -0
- package/scaffold/mcp/dist/cli/govern.js +937 -0
- package/scaffold/mcp/dist/cli/query.js +409 -0
- package/scaffold/mcp/dist/cli/run.js +295 -0
- package/scaffold/mcp/dist/cli/stage.js +308 -0
- package/scaffold/mcp/dist/cli/telemetry-test.js +141 -0
- package/scaffold/mcp/dist/cli/ungoverned-detector.js +133 -0
- package/scaffold/mcp/dist/contextEntities.js +282 -0
- package/scaffold/mcp/dist/core/audit/query.js +72 -0
- package/scaffold/mcp/dist/core/audit/writer.js +28 -0
- package/scaffold/mcp/dist/core/config.js +235 -0
- package/scaffold/mcp/dist/core/enterprise-host-identity.js +193 -0
- package/scaffold/mcp/dist/core/enterprise-identity.js +18 -0
- package/scaffold/mcp/dist/core/enterprise-rotation.js +158 -0
- package/scaffold/mcp/dist/core/govern-paths.js +21 -0
- package/scaffold/mcp/dist/core/index.js +14 -0
- package/scaffold/mcp/dist/core/license.js +278 -0
- package/scaffold/mcp/dist/core/policy/enforce.js +66 -0
- package/scaffold/mcp/dist/core/policy/injection.js +172 -0
- package/scaffold/mcp/dist/core/policy/store.js +179 -0
- package/scaffold/mcp/dist/core/rbac/check.js +30 -0
- package/scaffold/mcp/dist/core/secure-endpoint.js +25 -0
- package/scaffold/mcp/dist/core/telemetry/collector.js +285 -0
- package/scaffold/mcp/dist/core/telemetry/state-dir.js +31 -0
- package/scaffold/mcp/dist/core/validators/builtins.js +632 -0
- package/scaffold/mcp/dist/core/validators/config.js +44 -0
- package/scaffold/mcp/dist/core/validators/engine.js +120 -0
- package/scaffold/mcp/dist/core/validators/evaluators/code_comments.js +236 -0
- package/scaffold/mcp/dist/core/validators/evaluators/regex.js +116 -0
- package/scaffold/mcp/dist/core/workflow/artifact-io.js +103 -0
- package/scaffold/mcp/dist/core/workflow/capabilities.js +88 -0
- package/scaffold/mcp/dist/core/workflow/default-workflows.js +102 -0
- package/scaffold/mcp/dist/core/workflow/enforcement.js +164 -0
- package/scaffold/mcp/dist/core/workflow/envelope.js +132 -0
- package/scaffold/mcp/dist/core/workflow/index.js +11 -0
- package/scaffold/mcp/dist/core/workflow/mcp-tools.js +175 -0
- package/scaffold/mcp/dist/core/workflow/resolution.js +69 -0
- package/scaffold/mcp/dist/core/workflow/run-lifecycle.js +123 -0
- package/scaffold/mcp/dist/core/workflow/schemas.js +141 -0
- package/scaffold/mcp/dist/core/workflow/synced-capability-registry.js +60 -0
- package/scaffold/mcp/dist/core/workflow/synced-registry.js +60 -0
- package/scaffold/mcp/dist/daemon/capability-sync-checker.js +242 -0
- package/scaffold/mcp/dist/daemon/client.js +130 -0
- package/scaffold/mcp/dist/daemon/egress-proxy.js +288 -0
- package/scaffold/mcp/dist/daemon/global-host-events.js +222 -0
- package/scaffold/mcp/dist/daemon/heartbeat-pusher.js +116 -0
- package/scaffold/mcp/dist/daemon/heartbeat-tracker.js +165 -0
- package/scaffold/mcp/dist/daemon/host-events-pusher.js +249 -0
- package/scaffold/mcp/dist/daemon/main.js +449 -0
- package/scaffold/mcp/dist/daemon/paths.js +36 -0
- package/scaffold/mcp/dist/daemon/project-service-registry.js +78 -0
- package/scaffold/mcp/dist/daemon/protocol.js +8 -0
- package/scaffold/mcp/dist/daemon/server.js +180 -0
- package/scaffold/mcp/dist/daemon/skill-sync-checker.js +557 -0
- package/scaffold/mcp/dist/daemon/sync-checker.js +165 -0
- package/scaffold/mcp/dist/daemon/ungoverned-scanner.js +136 -0
- package/scaffold/mcp/dist/daemon/workflow-sync-checker.js +249 -0
- package/scaffold/mcp/dist/defaults.js +6 -0
- package/scaffold/mcp/dist/embed.js +627 -0
- package/scaffold/mcp/dist/embedScheduler.js +479 -0
- package/scaffold/mcp/dist/embeddings.js +167 -0
- package/scaffold/mcp/dist/enterprise/audit/push.js +66 -0
- package/scaffold/mcp/dist/enterprise/index.js +327 -0
- package/scaffold/mcp/dist/enterprise/model/deploy.js +27 -0
- package/scaffold/mcp/dist/enterprise/policy/sync.js +129 -0
- package/scaffold/mcp/dist/enterprise/privacy/boundary.js +184 -0
- package/scaffold/mcp/dist/enterprise/reviews/changed-files.js +33 -0
- package/scaffold/mcp/dist/enterprise/reviews/pattern-context.js +202 -0
- package/scaffold/mcp/dist/enterprise/reviews/policy-selection.js +46 -0
- package/scaffold/mcp/dist/enterprise/reviews/push.js +102 -0
- package/scaffold/mcp/dist/enterprise/reviews/trust-state.js +186 -0
- package/scaffold/mcp/dist/enterprise/telemetry/sync.js +57 -0
- package/scaffold/mcp/dist/enterprise/tools/enterprise.js +826 -0
- package/scaffold/mcp/dist/enterprise/tools/harness.js +40 -0
- package/scaffold/mcp/dist/enterprise/tools/walk.js +73 -0
- package/scaffold/mcp/dist/enterprise/violations/push.js +77 -0
- package/scaffold/mcp/dist/enterprise/workflow/push.js +44 -0
- package/scaffold/mcp/dist/enterprise/workflow/state.js +329 -0
- package/scaffold/mcp/dist/frontmatter.js +33 -0
- package/scaffold/mcp/dist/graph.js +769 -0
- package/scaffold/mcp/dist/graphCsv.js +55 -0
- package/scaffold/mcp/dist/graphMetrics.js +8 -0
- package/scaffold/mcp/dist/hooks/permission-request.js +89 -0
- package/scaffold/mcp/dist/hooks/post-tool-use.js +105 -0
- package/scaffold/mcp/dist/hooks/pre-compact.js +29 -0
- package/scaffold/mcp/dist/hooks/pre-tool-use.js +78 -0
- package/scaffold/mcp/dist/hooks/session-end.js +43 -0
- package/scaffold/mcp/dist/hooks/session-start.js +41 -0
- package/scaffold/mcp/dist/hooks/shared.js +194 -0
- package/scaffold/mcp/dist/hooks/stop.js +28 -0
- package/scaffold/mcp/dist/hooks/user-prompt-submit.js +33 -0
- package/scaffold/mcp/dist/impactPresentation.js +137 -0
- package/scaffold/mcp/dist/impactRanking.js +191 -0
- package/scaffold/mcp/dist/impactResponse.js +30 -0
- package/scaffold/mcp/dist/impactResults.js +105 -0
- package/scaffold/mcp/dist/impactSeed.js +20 -0
- package/scaffold/mcp/dist/impactTraversal.js +64 -0
- package/scaffold/mcp/dist/jsonl.js +77 -0
- package/scaffold/mcp/dist/loadGraph.js +759 -0
- package/scaffold/mcp/dist/lruCache.js +38 -0
- package/scaffold/mcp/dist/paths.js +97 -0
- package/scaffold/mcp/dist/patternEvidence.js +272 -0
- package/scaffold/mcp/dist/plugin.js +81 -0
- package/scaffold/mcp/dist/presets.js +78 -0
- package/scaffold/mcp/dist/relatedResponse.js +18 -0
- package/scaffold/mcp/dist/relatedTraversal.js +78 -0
- package/scaffold/mcp/dist/rules.js +23 -0
- package/scaffold/mcp/dist/search.js +212 -0
- package/scaffold/mcp/dist/searchCore.js +457 -0
- package/scaffold/mcp/dist/searchResults.js +230 -0
- package/scaffold/mcp/dist/server.js +317 -0
- package/scaffold/mcp/dist/types.js +1 -0
- package/scaffold/mcp/package-lock.json +321 -200
- package/scaffold/mcp/package.json +15 -6
- package/scaffold/mcp/src/cli/enterprise-setup.ts +82 -8
- package/scaffold/mcp/src/cli/govern.ts +137 -52
- package/scaffold/mcp/src/cli/run.ts +53 -19
- package/scaffold/mcp/src/cli/stage.ts +8 -3
- package/scaffold/mcp/src/core/config.ts +9 -2
- package/scaffold/mcp/src/core/enterprise-host-identity.ts +259 -0
- package/scaffold/mcp/src/core/enterprise-identity.ts +20 -0
- package/scaffold/mcp/src/core/enterprise-rotation.ts +185 -0
- package/scaffold/mcp/src/core/govern-paths.ts +30 -0
- package/scaffold/mcp/src/core/license.ts +186 -17
- package/scaffold/mcp/src/core/secure-endpoint.ts +23 -0
- package/scaffold/mcp/src/core/workflow/enforcement.ts +8 -1
- package/scaffold/mcp/src/core/workflow/mcp-tools.ts +3 -0
- package/scaffold/mcp/src/core/workflow/resolution.ts +9 -1
- package/scaffold/mcp/src/core/workflow/synced-capability-registry.ts +15 -0
- package/scaffold/mcp/src/core/workflow/synced-registry.ts +15 -0
- package/scaffold/mcp/src/daemon/capability-sync-checker.ts +38 -2
- package/scaffold/mcp/src/daemon/egress-proxy.ts +14 -8
- package/scaffold/mcp/src/daemon/global-host-events.ts +288 -0
- package/scaffold/mcp/src/daemon/heartbeat-pusher.ts +4 -0
- package/scaffold/mcp/src/daemon/heartbeat-tracker.ts +2 -0
- package/scaffold/mcp/src/daemon/host-events-pusher.ts +5 -0
- package/scaffold/mcp/src/daemon/main.ts +99 -25
- package/scaffold/mcp/src/daemon/project-service-registry.ts +107 -0
- package/scaffold/mcp/src/daemon/skill-sync-checker.ts +368 -31
- package/scaffold/mcp/src/daemon/sync-checker.ts +4 -0
- package/scaffold/mcp/src/daemon/ungoverned-scanner.ts +57 -25
- package/scaffold/mcp/src/daemon/workflow-sync-checker.ts +41 -2
- package/scaffold/mcp/src/enterprise/audit/push.ts +8 -0
- package/scaffold/mcp/src/enterprise/policy/sync.ts +12 -0
- package/scaffold/mcp/src/enterprise/reviews/push.ts +9 -0
- package/scaffold/mcp/src/enterprise/reviews/trust-state.ts +3 -1
- package/scaffold/mcp/src/enterprise/telemetry/sync.ts +9 -0
- package/scaffold/mcp/src/enterprise/violations/push.ts +9 -0
- package/scaffold/mcp/src/enterprise/workflow/push.ts +7 -0
- package/scaffold/mcp/src/plugin.ts +20 -0
- package/scaffold/mcp/tests/copilot-shim.test.mjs +19 -1
- package/scaffold/mcp/tests/egress-proxy.test.mjs +37 -0
- package/scaffold/mcp/tests/enterprise-identity-sync.test.mjs +401 -0
- package/scaffold/mcp/tests/enterprise-setup.test.mjs +189 -0
- package/scaffold/mcp/tests/global-host-events.test.mjs +81 -0
- package/scaffold/mcp/tests/govern-install.test.mjs +95 -2
- package/scaffold/mcp/tests/govern-repair.test.mjs +20 -3
- package/scaffold/mcp/tests/heartbeat-tracker.test.mjs +26 -0
- package/scaffold/mcp/tests/license.test.mjs +367 -0
- package/scaffold/mcp/tests/project-service-registry.test.mjs +172 -0
- package/scaffold/mcp/tests/review-trust-contract.test.mjs +18 -0
- package/scaffold/mcp/tests/secure-enterprise-endpoint.test.mjs +46 -0
- package/scaffold/mcp/tests/skill-sync-checker.test.mjs +446 -2
- package/scaffold/mcp/tests/ungoverned-scanner.test.mjs +104 -22
- package/scaffold/mcp/tests/workflow-cli.test.mjs +14 -1
- package/scaffold/mcp/tests/workflow-synced-capabilities.test.mjs +37 -0
- package/scaffold/mcp/tests/workflow-synced-registry.test.mjs +40 -1
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
import { hostname } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { loadEnterpriseConfig } from "../core/config.js";
|
|
9
|
+
import { matchesEnterpriseHostIdentity } from "../core/enterprise-host-identity.js";
|
|
10
|
+
import {
|
|
11
|
+
enterpriseCredentialId,
|
|
12
|
+
isAllowedLicenseEndpoint,
|
|
13
|
+
} from "../core/license.js";
|
|
9
14
|
import { workflowDefinitionSchema, type WorkflowDefinition } from "../core/workflow/schemas.js";
|
|
10
15
|
import { writeHostAuditEvent } from "./ungoverned-scanner.js";
|
|
11
16
|
import { daemonDir } from "./paths.js";
|
|
@@ -55,6 +60,7 @@ type LocalWorkflowRecord = {
|
|
|
55
60
|
};
|
|
56
61
|
|
|
57
62
|
type LocalWorkflowsState = {
|
|
63
|
+
credential_id?: string;
|
|
58
64
|
workflows: Record<string, LocalWorkflowRecord>;
|
|
59
65
|
last_synced_at?: string;
|
|
60
66
|
};
|
|
@@ -79,6 +85,10 @@ export function readSyncedWorkflowsState(): LocalWorkflowsState {
|
|
|
79
85
|
try {
|
|
80
86
|
const parsed = JSON.parse(readFileSync(path, "utf8")) as LocalWorkflowsState;
|
|
81
87
|
return {
|
|
88
|
+
credential_id:
|
|
89
|
+
typeof parsed.credential_id === "string"
|
|
90
|
+
? parsed.credential_id
|
|
91
|
+
: undefined,
|
|
82
92
|
workflows: parsed.workflows ?? {},
|
|
83
93
|
last_synced_at: parsed.last_synced_at,
|
|
84
94
|
};
|
|
@@ -149,6 +159,28 @@ export async function runWorkflowSyncOnce(
|
|
|
149
159
|
await writeAudit(cwd, outcome);
|
|
150
160
|
return outcome;
|
|
151
161
|
}
|
|
162
|
+
if (!isAllowedLicenseEndpoint(baseUrl)) {
|
|
163
|
+
const outcome: WorkflowSyncOutcome = {
|
|
164
|
+
kind: "failed",
|
|
165
|
+
error: "insecure or invalid enterprise endpoint",
|
|
166
|
+
};
|
|
167
|
+
await writeAudit(cwd, outcome);
|
|
168
|
+
return outcome;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const credentialId = enterpriseCredentialId(baseUrl, apiKey);
|
|
172
|
+
if (!matchesEnterpriseHostIdentity(credentialId)) {
|
|
173
|
+
const outcome: WorkflowSyncOutcome = {
|
|
174
|
+
kind: "failed",
|
|
175
|
+
error:
|
|
176
|
+
"enterprise identity conflict: this user profile is already enrolled to another endpoint or API key",
|
|
177
|
+
};
|
|
178
|
+
await writeAudit(cwd, outcome);
|
|
179
|
+
return outcome;
|
|
180
|
+
}
|
|
181
|
+
const state = readSyncedWorkflowsState();
|
|
182
|
+
const identityChanged = state.credential_id !== credentialId;
|
|
183
|
+
state.credential_id = credentialId;
|
|
152
184
|
|
|
153
185
|
let manifest: ManifestEntry[];
|
|
154
186
|
try {
|
|
@@ -162,7 +194,6 @@ export async function runWorkflowSyncOnce(
|
|
|
162
194
|
return outcome;
|
|
163
195
|
}
|
|
164
196
|
|
|
165
|
-
const state = readSyncedWorkflowsState();
|
|
166
197
|
const remoteByName = new Map(manifest.map((e) => [e.workflow_id, e]));
|
|
167
198
|
|
|
168
199
|
const added: string[] = [];
|
|
@@ -174,7 +205,11 @@ export async function runWorkflowSyncOnce(
|
|
|
174
205
|
const isNew = !local;
|
|
175
206
|
const isChanged =
|
|
176
207
|
Boolean(local) &&
|
|
177
|
-
(
|
|
208
|
+
(
|
|
209
|
+
identityChanged ||
|
|
210
|
+
local.updated_at !== entry.updated_at ||
|
|
211
|
+
local.version !== entry.version
|
|
212
|
+
);
|
|
178
213
|
if (!isNew && !isChanged) continue;
|
|
179
214
|
|
|
180
215
|
let fetched: FetchedWorkflow;
|
|
@@ -224,6 +259,10 @@ export async function runWorkflowSyncOnce(
|
|
|
224
259
|
|
|
225
260
|
const totalChanged = added.length + changed.length + removed.length;
|
|
226
261
|
if (totalChanged === 0) {
|
|
262
|
+
if (identityChanged) {
|
|
263
|
+
state.last_synced_at = new Date().toISOString();
|
|
264
|
+
writeSyncedWorkflowsState(state);
|
|
265
|
+
}
|
|
227
266
|
const outcome: WorkflowSyncOutcome = {
|
|
228
267
|
kind: "unchanged",
|
|
229
268
|
count: manifest.length,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AuditEntry } from "../../core/audit/writer.js";
|
|
2
|
+
import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
|
|
2
3
|
import { sanitizeAuditEntryForPush } from "../privacy/boundary.js";
|
|
3
4
|
|
|
4
5
|
export type AuditPushContext = {
|
|
@@ -40,6 +41,13 @@ export async function pushAuditEvents(
|
|
|
40
41
|
if (pending.length === 0) {
|
|
41
42
|
return { success: true, count: 0 };
|
|
42
43
|
}
|
|
44
|
+
if (!isAllowedEnterpriseEndpoint(baseUrl)) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
count: 0,
|
|
48
|
+
error: "insecure or invalid enterprise endpoint",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
43
51
|
|
|
44
52
|
const auditUrl = `${baseUrl.replace(/\/$/, "")}/api/v1/audit/push`;
|
|
45
53
|
let pushedCount = 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { OrgPolicy, PolicyStore } from "../../core/policy/store.js";
|
|
3
|
+
import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
|
|
3
4
|
|
|
4
5
|
const CloudPolicySchema = z.object({
|
|
5
6
|
id: z.string().min(1).max(200),
|
|
@@ -60,6 +61,17 @@ export async function syncFromCloud(
|
|
|
60
61
|
lastSync = result;
|
|
61
62
|
return result;
|
|
62
63
|
}
|
|
64
|
+
if (!isAllowedEnterpriseEndpoint(endpoint)) {
|
|
65
|
+
const result: SyncResult = {
|
|
66
|
+
success: false,
|
|
67
|
+
synced: 0,
|
|
68
|
+
source: "cloud",
|
|
69
|
+
timestamp: new Date().toISOString(),
|
|
70
|
+
error: "insecure or invalid enterprise endpoint",
|
|
71
|
+
};
|
|
72
|
+
lastSync = result;
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
63
75
|
|
|
64
76
|
try {
|
|
65
77
|
const response = await fetch(endpoint, {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { recordReviewDeliveryStatus } from "./trust-state.js";
|
|
2
|
+
import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
|
|
2
3
|
|
|
3
4
|
export type ReviewPushItem = {
|
|
4
5
|
policy_id: string;
|
|
@@ -51,6 +52,14 @@ export async function pushReviewResults(
|
|
|
51
52
|
if (pending.length === 0) {
|
|
52
53
|
return { success: true, count: 0, attempted: false };
|
|
53
54
|
}
|
|
55
|
+
if (!isAllowedEnterpriseEndpoint(baseUrl)) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
count: 0,
|
|
59
|
+
error: "insecure or invalid enterprise endpoint",
|
|
60
|
+
attempted: false,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
54
63
|
|
|
55
64
|
const reviewsUrl = `${baseUrl.replace(/\/$/, "")}/api/v1/reviews/push`;
|
|
56
65
|
const batch = pending.splice(0, 100);
|
|
@@ -143,7 +143,9 @@ function inferWorkflow(projectRoot: string, taskId?: string): WorkflowInference
|
|
|
143
143
|
if (!workflowId) {
|
|
144
144
|
return { id: null, source: "unknown", task_id: normalizedTaskId };
|
|
145
145
|
}
|
|
146
|
-
const resolved = resolveWorkflowDefinition(workflowId
|
|
146
|
+
const resolved = resolveWorkflowDefinition(workflowId, {
|
|
147
|
+
cwd: projectRoot,
|
|
148
|
+
});
|
|
147
149
|
return {
|
|
148
150
|
id: workflowId,
|
|
149
151
|
source: resolved.source,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TelemetryMetrics } from "../../core/telemetry/collector.js";
|
|
2
|
+
import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
|
|
2
3
|
import { buildTelemetryPushPayload } from "../privacy/boundary.js";
|
|
3
4
|
|
|
4
5
|
export type PushResult = {
|
|
@@ -38,6 +39,14 @@ export async function pushMetrics(
|
|
|
38
39
|
lastPush = result;
|
|
39
40
|
return result;
|
|
40
41
|
}
|
|
42
|
+
if (!isAllowedEnterpriseEndpoint(endpoint)) {
|
|
43
|
+
const result: PushResult = {
|
|
44
|
+
success: false,
|
|
45
|
+
error: "insecure or invalid enterprise endpoint",
|
|
46
|
+
};
|
|
47
|
+
lastPush = result;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
41
50
|
|
|
42
51
|
try {
|
|
43
52
|
const response = await fetch(endpoint, {
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Uses the same endpoint/api_key as policy sync since
|
|
5
5
|
* violations require the "policy" scope.
|
|
6
6
|
*/
|
|
7
|
+
import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
|
|
8
|
+
|
|
7
9
|
type ViolationItem = {
|
|
8
10
|
rule_id: string;
|
|
9
11
|
severity: "error" | "warning" | "info";
|
|
@@ -54,6 +56,13 @@ export async function pushViolations(
|
|
|
54
56
|
if (!baseUrl || !apiKey) {
|
|
55
57
|
return { success: false, count: 0, error: "endpoint or api_key not configured" };
|
|
56
58
|
}
|
|
59
|
+
if (!isAllowedEnterpriseEndpoint(baseUrl)) {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
count: 0,
|
|
63
|
+
error: "insecure or invalid enterprise endpoint",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
57
66
|
|
|
58
67
|
const violationsUrl = `${baseUrl.replace(/\/$/, "")}/api/v1/violations/push`;
|
|
59
68
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WorkflowState } from "./state.js";
|
|
2
|
+
import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
|
|
2
3
|
|
|
3
4
|
export type WorkflowPushContext = {
|
|
4
5
|
repo?: string;
|
|
@@ -26,6 +27,12 @@ export async function pushWorkflowSnapshot(
|
|
|
26
27
|
if (!baseUrl || !apiKey) {
|
|
27
28
|
return { success: false, error: "endpoint or api_key not configured" };
|
|
28
29
|
}
|
|
30
|
+
if (!isAllowedEnterpriseEndpoint(baseUrl)) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: "insecure or invalid enterprise endpoint",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
29
36
|
|
|
30
37
|
const workflowUrl = `${baseUrl.replace(/\/$/, "")}/api/v1/workflow/push`;
|
|
31
38
|
|
|
@@ -120,6 +120,26 @@ export async function loadPlugins(server: McpServer): Promise<void> {
|
|
|
120
120
|
);
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
|
+
const [
|
|
124
|
+
{ matchesEnterpriseHostIdentity },
|
|
125
|
+
{ enterpriseCredentialId },
|
|
126
|
+
] = await Promise.all([
|
|
127
|
+
import("./core/enterprise-host-identity.js"),
|
|
128
|
+
import("./core/license.js"),
|
|
129
|
+
]);
|
|
130
|
+
if (
|
|
131
|
+
!matchesEnterpriseHostIdentity(
|
|
132
|
+
enterpriseCredentialId(
|
|
133
|
+
config.enterprise.base_url || config.enterprise.endpoint,
|
|
134
|
+
config.enterprise.api_key,
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
) {
|
|
138
|
+
process.stderr.write(
|
|
139
|
+
"[cortex] Enterprise inactive: this user profile is enrolled to another endpoint or API key\n",
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
123
143
|
|
|
124
144
|
if (typeof enterprise.register === "function") {
|
|
125
145
|
await enterprise.register(server);
|
|
@@ -80,7 +80,25 @@ test("installCopilotShim: refuses to overwrite a non-shim file at shim path", ()
|
|
|
80
80
|
searchPath: ws.searchPath,
|
|
81
81
|
});
|
|
82
82
|
assert.equal(result.ok, false);
|
|
83
|
-
assert.match(result.message, /not a cortex shim/);
|
|
83
|
+
assert.match(result.message, /not a regular cortex shim/);
|
|
84
|
+
} finally {
|
|
85
|
+
fs.rmSync(ws.dir, { recursive: true, force: true });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("installCopilotShim: refuses a dangling symlink without touching its target", () => {
|
|
90
|
+
const ws = makeWorkspace();
|
|
91
|
+
const target = path.join(ws.dir, "must-not-be-created");
|
|
92
|
+
try {
|
|
93
|
+
fs.symlinkSync(target, ws.shimPath);
|
|
94
|
+
const result = installCopilotShim({
|
|
95
|
+
shimPath: ws.shimPath,
|
|
96
|
+
searchPath: ws.searchPath,
|
|
97
|
+
});
|
|
98
|
+
assert.equal(result.ok, false);
|
|
99
|
+
assert.match(result.message, /regular cortex shim/);
|
|
100
|
+
assert.equal(fs.existsSync(target), false);
|
|
101
|
+
assert.equal(fs.lstatSync(ws.shimPath).isSymbolicLink(), true);
|
|
84
102
|
} finally {
|
|
85
103
|
fs.rmSync(ws.dir, { recursive: true, force: true });
|
|
86
104
|
}
|
|
@@ -237,3 +237,40 @@ test("egress proxy: stop() closes the server", async () => {
|
|
|
237
237
|
assert.equal(proxy.isRunning(), false);
|
|
238
238
|
fs.rmSync(cwd, { recursive: true, force: true });
|
|
239
239
|
});
|
|
240
|
+
|
|
241
|
+
test("egress proxy: an unattributed shared connection is not written into either project audit boundary", async () => {
|
|
242
|
+
const echo = await startEchoServer();
|
|
243
|
+
const projectA = makeProject();
|
|
244
|
+
const projectB = makeProject();
|
|
245
|
+
const proxy = await startEgressProxy({ port: 0, hostId: "test-host" });
|
|
246
|
+
try {
|
|
247
|
+
await new Promise((resolve, reject) => {
|
|
248
|
+
const client = net.connect(proxy.port, "127.0.0.1", () => {
|
|
249
|
+
client.write(
|
|
250
|
+
`GET http://127.0.0.1:${echo.port}/health HTTP/1.1\r\n` +
|
|
251
|
+
`Host: 127.0.0.1:${echo.port}\r\n\r\n`,
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
client.on("data", () => {
|
|
255
|
+
client.end();
|
|
256
|
+
resolve();
|
|
257
|
+
});
|
|
258
|
+
client.on("error", reject);
|
|
259
|
+
});
|
|
260
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
261
|
+
|
|
262
|
+
assert.equal(
|
|
263
|
+
fs.existsSync(path.join(projectA, ".context", "audit")),
|
|
264
|
+
false,
|
|
265
|
+
);
|
|
266
|
+
assert.equal(
|
|
267
|
+
fs.existsSync(path.join(projectB, ".context", "audit")),
|
|
268
|
+
false,
|
|
269
|
+
);
|
|
270
|
+
} finally {
|
|
271
|
+
await proxy.stop();
|
|
272
|
+
await new Promise((resolve) => echo.server.close(resolve));
|
|
273
|
+
fs.rmSync(projectA, { recursive: true, force: true });
|
|
274
|
+
fs.rmSync(projectB, { recursive: true, force: true });
|
|
275
|
+
}
|
|
276
|
+
});
|