@danielblomma/cortex-mcp 2.4.0 → 2.4.2

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.
Files changed (214) hide show
  1. package/CHANGELOG.md +226 -0
  2. package/README.md +24 -3
  3. package/bin/cli/arguments.mjs +60 -0
  4. package/bin/cli/context-passthrough.mjs +129 -0
  5. package/bin/cli/daemon.mjs +157 -0
  6. package/bin/cli/enterprise.mjs +516 -0
  7. package/bin/cli/help.mjs +88 -0
  8. package/bin/cli/hooks.mjs +199 -0
  9. package/bin/cli/mcp-command.mjs +87 -0
  10. package/bin/cli/paths.mjs +14 -0
  11. package/bin/cli/process.mjs +49 -0
  12. package/bin/cli/project-commands.mjs +237 -0
  13. package/bin/cli/project-runtime.mjs +34 -0
  14. package/bin/cli/query-command.mjs +18 -0
  15. package/bin/cli/router.mjs +66 -0
  16. package/bin/cli/run-command.mjs +44 -0
  17. package/bin/cli/scaffold-ownership.mjs +936 -0
  18. package/bin/cli/scaffold.mjs +687 -0
  19. package/bin/cli/stage-command.mjs +9 -0
  20. package/bin/cli/telemetry-command.mjs +18 -0
  21. package/bin/cli/trusted-runtime.mjs +18 -0
  22. package/bin/cortex.mjs +12 -1607
  23. package/bin/daemon-control.mjs +162 -0
  24. package/mcp-registry-submission.json +1 -1
  25. package/package.json +8 -3
  26. package/scaffold/mcp/dist/.cortex-build-hash +1 -0
  27. package/scaffold/mcp/dist/cli/enterprise-setup.js +134 -0
  28. package/scaffold/mcp/dist/cli/govern.js +937 -0
  29. package/scaffold/mcp/dist/cli/query.js +409 -0
  30. package/scaffold/mcp/dist/cli/run.js +295 -0
  31. package/scaffold/mcp/dist/cli/stage.js +308 -0
  32. package/scaffold/mcp/dist/cli/telemetry-test.js +141 -0
  33. package/scaffold/mcp/dist/cli/ungoverned-detector.js +133 -0
  34. package/scaffold/mcp/dist/contextEntities.js +282 -0
  35. package/scaffold/mcp/dist/core/audit/query.js +72 -0
  36. package/scaffold/mcp/dist/core/audit/writer.js +28 -0
  37. package/scaffold/mcp/dist/core/config.js +235 -0
  38. package/scaffold/mcp/dist/core/enterprise-host-identity.js +193 -0
  39. package/scaffold/mcp/dist/core/enterprise-identity.js +18 -0
  40. package/scaffold/mcp/dist/core/enterprise-rotation.js +158 -0
  41. package/scaffold/mcp/dist/core/govern-paths.js +21 -0
  42. package/scaffold/mcp/dist/core/index.js +14 -0
  43. package/scaffold/mcp/dist/core/license.js +278 -0
  44. package/scaffold/mcp/dist/core/policy/enforce.js +66 -0
  45. package/scaffold/mcp/dist/core/policy/injection.js +172 -0
  46. package/scaffold/mcp/dist/core/policy/store.js +179 -0
  47. package/scaffold/mcp/dist/core/rbac/check.js +30 -0
  48. package/scaffold/mcp/dist/core/secure-endpoint.js +25 -0
  49. package/scaffold/mcp/dist/core/telemetry/collector.js +285 -0
  50. package/scaffold/mcp/dist/core/telemetry/state-dir.js +31 -0
  51. package/scaffold/mcp/dist/core/validators/builtins.js +632 -0
  52. package/scaffold/mcp/dist/core/validators/config.js +44 -0
  53. package/scaffold/mcp/dist/core/validators/engine.js +120 -0
  54. package/scaffold/mcp/dist/core/validators/evaluators/code_comments.js +236 -0
  55. package/scaffold/mcp/dist/core/validators/evaluators/regex.js +116 -0
  56. package/scaffold/mcp/dist/core/workflow/artifact-io.js +103 -0
  57. package/scaffold/mcp/dist/core/workflow/capabilities.js +88 -0
  58. package/scaffold/mcp/dist/core/workflow/default-workflows.js +102 -0
  59. package/scaffold/mcp/dist/core/workflow/enforcement.js +164 -0
  60. package/scaffold/mcp/dist/core/workflow/envelope.js +132 -0
  61. package/scaffold/mcp/dist/core/workflow/index.js +11 -0
  62. package/scaffold/mcp/dist/core/workflow/mcp-tools.js +175 -0
  63. package/scaffold/mcp/dist/core/workflow/resolution.js +69 -0
  64. package/scaffold/mcp/dist/core/workflow/run-lifecycle.js +123 -0
  65. package/scaffold/mcp/dist/core/workflow/schemas.js +141 -0
  66. package/scaffold/mcp/dist/core/workflow/synced-capability-registry.js +60 -0
  67. package/scaffold/mcp/dist/core/workflow/synced-registry.js +60 -0
  68. package/scaffold/mcp/dist/daemon/capability-sync-checker.js +242 -0
  69. package/scaffold/mcp/dist/daemon/client.js +130 -0
  70. package/scaffold/mcp/dist/daemon/egress-proxy.js +288 -0
  71. package/scaffold/mcp/dist/daemon/global-host-events.js +222 -0
  72. package/scaffold/mcp/dist/daemon/heartbeat-pusher.js +116 -0
  73. package/scaffold/mcp/dist/daemon/heartbeat-tracker.js +165 -0
  74. package/scaffold/mcp/dist/daemon/host-events-pusher.js +249 -0
  75. package/scaffold/mcp/dist/daemon/main.js +449 -0
  76. package/scaffold/mcp/dist/daemon/paths.js +36 -0
  77. package/scaffold/mcp/dist/daemon/project-service-registry.js +78 -0
  78. package/scaffold/mcp/dist/daemon/protocol.js +8 -0
  79. package/scaffold/mcp/dist/daemon/server.js +180 -0
  80. package/scaffold/mcp/dist/daemon/skill-sync-checker.js +557 -0
  81. package/scaffold/mcp/dist/daemon/sync-checker.js +165 -0
  82. package/scaffold/mcp/dist/daemon/ungoverned-scanner.js +136 -0
  83. package/scaffold/mcp/dist/daemon/workflow-sync-checker.js +249 -0
  84. package/scaffold/mcp/dist/defaults.js +6 -0
  85. package/scaffold/mcp/dist/embed.js +627 -0
  86. package/scaffold/mcp/dist/embedScheduler.js +479 -0
  87. package/scaffold/mcp/dist/embeddings.js +167 -0
  88. package/scaffold/mcp/dist/enterprise/audit/push.js +66 -0
  89. package/scaffold/mcp/dist/enterprise/index.js +327 -0
  90. package/scaffold/mcp/dist/enterprise/model/deploy.js +27 -0
  91. package/scaffold/mcp/dist/enterprise/policy/sync.js +129 -0
  92. package/scaffold/mcp/dist/enterprise/privacy/boundary.js +184 -0
  93. package/scaffold/mcp/dist/enterprise/reviews/changed-files.js +33 -0
  94. package/scaffold/mcp/dist/enterprise/reviews/pattern-context.js +202 -0
  95. package/scaffold/mcp/dist/enterprise/reviews/policy-selection.js +46 -0
  96. package/scaffold/mcp/dist/enterprise/reviews/push.js +102 -0
  97. package/scaffold/mcp/dist/enterprise/reviews/trust-state.js +186 -0
  98. package/scaffold/mcp/dist/enterprise/telemetry/sync.js +57 -0
  99. package/scaffold/mcp/dist/enterprise/tools/enterprise.js +826 -0
  100. package/scaffold/mcp/dist/enterprise/tools/harness.js +40 -0
  101. package/scaffold/mcp/dist/enterprise/tools/walk.js +73 -0
  102. package/scaffold/mcp/dist/enterprise/violations/push.js +77 -0
  103. package/scaffold/mcp/dist/enterprise/workflow/push.js +44 -0
  104. package/scaffold/mcp/dist/enterprise/workflow/state.js +329 -0
  105. package/scaffold/mcp/dist/frontmatter.js +33 -0
  106. package/scaffold/mcp/dist/graph.js +769 -0
  107. package/scaffold/mcp/dist/graphCsv.js +55 -0
  108. package/scaffold/mcp/dist/graphMetrics.js +8 -0
  109. package/scaffold/mcp/dist/hooks/permission-request.js +89 -0
  110. package/scaffold/mcp/dist/hooks/post-tool-use.js +105 -0
  111. package/scaffold/mcp/dist/hooks/pre-compact.js +29 -0
  112. package/scaffold/mcp/dist/hooks/pre-tool-use.js +78 -0
  113. package/scaffold/mcp/dist/hooks/session-end.js +43 -0
  114. package/scaffold/mcp/dist/hooks/session-start.js +41 -0
  115. package/scaffold/mcp/dist/hooks/shared.js +194 -0
  116. package/scaffold/mcp/dist/hooks/stop.js +28 -0
  117. package/scaffold/mcp/dist/hooks/user-prompt-submit.js +33 -0
  118. package/scaffold/mcp/dist/impactPresentation.js +137 -0
  119. package/scaffold/mcp/dist/impactRanking.js +191 -0
  120. package/scaffold/mcp/dist/impactResponse.js +30 -0
  121. package/scaffold/mcp/dist/impactResults.js +105 -0
  122. package/scaffold/mcp/dist/impactSeed.js +20 -0
  123. package/scaffold/mcp/dist/impactTraversal.js +64 -0
  124. package/scaffold/mcp/dist/jsonl.js +77 -0
  125. package/scaffold/mcp/dist/loadGraph.js +759 -0
  126. package/scaffold/mcp/dist/lruCache.js +38 -0
  127. package/scaffold/mcp/dist/paths.js +97 -0
  128. package/scaffold/mcp/dist/patternEvidence.js +272 -0
  129. package/scaffold/mcp/dist/plugin.js +81 -0
  130. package/scaffold/mcp/dist/presets.js +78 -0
  131. package/scaffold/mcp/dist/relatedResponse.js +18 -0
  132. package/scaffold/mcp/dist/relatedTraversal.js +78 -0
  133. package/scaffold/mcp/dist/rules.js +23 -0
  134. package/scaffold/mcp/dist/search.js +212 -0
  135. package/scaffold/mcp/dist/searchCore.js +457 -0
  136. package/scaffold/mcp/dist/searchResults.js +230 -0
  137. package/scaffold/mcp/dist/server.js +317 -0
  138. package/scaffold/mcp/dist/types.js +1 -0
  139. package/scaffold/mcp/package-lock.json +321 -200
  140. package/scaffold/mcp/package.json +15 -6
  141. package/scaffold/mcp/src/cli/enterprise-setup.ts +82 -8
  142. package/scaffold/mcp/src/cli/govern.ts +137 -52
  143. package/scaffold/mcp/src/cli/run.ts +53 -19
  144. package/scaffold/mcp/src/cli/stage.ts +8 -3
  145. package/scaffold/mcp/src/core/config.ts +9 -2
  146. package/scaffold/mcp/src/core/enterprise-host-identity.ts +259 -0
  147. package/scaffold/mcp/src/core/enterprise-identity.ts +20 -0
  148. package/scaffold/mcp/src/core/enterprise-rotation.ts +185 -0
  149. package/scaffold/mcp/src/core/govern-paths.ts +30 -0
  150. package/scaffold/mcp/src/core/license.ts +186 -17
  151. package/scaffold/mcp/src/core/secure-endpoint.ts +23 -0
  152. package/scaffold/mcp/src/core/workflow/enforcement.ts +8 -1
  153. package/scaffold/mcp/src/core/workflow/mcp-tools.ts +3 -0
  154. package/scaffold/mcp/src/core/workflow/resolution.ts +9 -1
  155. package/scaffold/mcp/src/core/workflow/synced-capability-registry.ts +15 -0
  156. package/scaffold/mcp/src/core/workflow/synced-registry.ts +15 -0
  157. package/scaffold/mcp/src/daemon/capability-sync-checker.ts +38 -2
  158. package/scaffold/mcp/src/daemon/egress-proxy.ts +14 -8
  159. package/scaffold/mcp/src/daemon/global-host-events.ts +288 -0
  160. package/scaffold/mcp/src/daemon/heartbeat-pusher.ts +4 -0
  161. package/scaffold/mcp/src/daemon/heartbeat-tracker.ts +2 -0
  162. package/scaffold/mcp/src/daemon/host-events-pusher.ts +5 -0
  163. package/scaffold/mcp/src/daemon/main.ts +99 -25
  164. package/scaffold/mcp/src/daemon/project-service-registry.ts +107 -0
  165. package/scaffold/mcp/src/daemon/skill-sync-checker.ts +368 -31
  166. package/scaffold/mcp/src/daemon/sync-checker.ts +4 -0
  167. package/scaffold/mcp/src/daemon/ungoverned-scanner.ts +57 -25
  168. package/scaffold/mcp/src/daemon/workflow-sync-checker.ts +41 -2
  169. package/scaffold/mcp/src/enterprise/audit/push.ts +8 -0
  170. package/scaffold/mcp/src/enterprise/policy/sync.ts +12 -0
  171. package/scaffold/mcp/src/enterprise/reviews/push.ts +9 -0
  172. package/scaffold/mcp/src/enterprise/reviews/trust-state.ts +3 -1
  173. package/scaffold/mcp/src/enterprise/telemetry/sync.ts +9 -0
  174. package/scaffold/mcp/src/enterprise/violations/push.ts +9 -0
  175. package/scaffold/mcp/src/enterprise/workflow/push.ts +7 -0
  176. package/scaffold/mcp/src/plugin.ts +20 -0
  177. package/scaffold/mcp/tests/copilot-shim.test.mjs +19 -1
  178. package/scaffold/mcp/tests/egress-proxy.test.mjs +37 -0
  179. package/scaffold/mcp/tests/enterprise-identity-sync.test.mjs +401 -0
  180. package/scaffold/mcp/tests/enterprise-setup.test.mjs +189 -0
  181. package/scaffold/mcp/tests/global-host-events.test.mjs +81 -0
  182. package/scaffold/mcp/tests/govern-install.test.mjs +95 -2
  183. package/scaffold/mcp/tests/govern-repair.test.mjs +20 -3
  184. package/scaffold/mcp/tests/heartbeat-tracker.test.mjs +26 -0
  185. package/scaffold/mcp/tests/license.test.mjs +367 -0
  186. package/scaffold/mcp/tests/project-service-registry.test.mjs +172 -0
  187. package/scaffold/mcp/tests/review-trust-contract.test.mjs +18 -0
  188. package/scaffold/mcp/tests/secure-enterprise-endpoint.test.mjs +46 -0
  189. package/scaffold/mcp/tests/skill-sync-checker.test.mjs +446 -2
  190. package/scaffold/mcp/tests/ungoverned-scanner.test.mjs +104 -22
  191. package/scaffold/mcp/tests/workflow-cli.test.mjs +14 -1
  192. package/scaffold/mcp/tests/workflow-synced-capabilities.test.mjs +37 -0
  193. package/scaffold/mcp/tests/workflow-synced-registry.test.mjs +40 -1
  194. package/scaffold/ownership/baseline-v2.4.1.json +22 -0
  195. package/scaffold/ownership/current.json +4 -0
  196. package/scaffold/ownership/v1.json +456 -0
  197. package/scaffold/scripts/ingest-parsers.mjs +1 -387
  198. package/scaffold/scripts/ingest-worker.mjs +4 -1
  199. package/scaffold/scripts/ingest.mjs +5 -3899
  200. package/scaffold/scripts/lib/ingest/arguments.mjs +78 -0
  201. package/scaffold/scripts/lib/ingest/chunks.mjs +212 -0
  202. package/scaffold/scripts/lib/ingest/config.mjs +120 -0
  203. package/scaffold/scripts/lib/ingest/constants.mjs +222 -0
  204. package/scaffold/scripts/lib/ingest/files.mjs +387 -0
  205. package/scaffold/scripts/lib/ingest/incremental-state.mjs +131 -0
  206. package/scaffold/scripts/lib/ingest/io.mjs +78 -0
  207. package/scaffold/scripts/lib/ingest/main.mjs +76 -0
  208. package/scaffold/scripts/lib/ingest/parser-composition.mjs +140 -0
  209. package/scaffold/scripts/lib/ingest/parser-registry.mjs +387 -0
  210. package/scaffold/scripts/lib/ingest/pipeline-stages.mjs +1625 -0
  211. package/scaffold/scripts/lib/ingest/projects.mjs +272 -0
  212. package/scaffold/scripts/lib/ingest/relations.mjs +860 -0
  213. package/scaffold/scripts/lib/ingest/runtime-paths.mjs +11 -0
  214. package/scaffold/scripts/lib/ingest/workers.mjs +264 -0
@@ -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
- (local.updated_at !== entry.updated_at || local.version !== entry.version);
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
+ });