mergen-server 1.4.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/LICENSE +57 -0
- package/README.md +96 -0
- package/dist/__stubs__/calibration.js +471 -0
- package/dist/__stubs__/causal.js +601 -0
- package/dist/__stubs__/closed-source.js +150 -0
- package/dist/__stubs__/license.js +35 -0
- package/dist/__stubs__/plans.js +197 -0
- package/dist/app.js +467 -0
- package/dist/cli.js +250 -0
- package/dist/commands/agent-identity.js +79 -0
- package/dist/commands/gate.js +107 -0
- package/dist/commands/github.js +632 -0
- package/dist/commands/incident.js +644 -0
- package/dist/commands/policy.js +710 -0
- package/dist/commands/scan.js +243 -0
- package/dist/commands/setup.js +1218 -0
- package/dist/commands/shared.js +109 -0
- package/dist/commands/team.js +809 -0
- package/dist/datadog/blame-attribution.js +228 -0
- package/dist/datadog/client.js +159 -0
- package/dist/datadog/compactor.js +169 -0
- package/dist/datadog/fingerprinter.js +34 -0
- package/dist/datadog/incident-state.js +21 -0
- package/dist/datadog/line-matcher.js +56 -0
- package/dist/datadog/memory-store.js +399 -0
- package/dist/datadog/otel-trace.js +67 -0
- package/dist/index.js +505 -0
- package/dist/intelligence/action-risk.js +132 -0
- package/dist/intelligence/activity-feed.js +40 -0
- package/dist/intelligence/agent-identity.js +115 -0
- package/dist/intelligence/agent-pipeline.js +329 -0
- package/dist/intelligence/agent-profiles.js +81 -0
- package/dist/intelligence/ai-commit.js +16 -0
- package/dist/intelligence/approval-analytics.js +42 -0
- package/dist/intelligence/approval-events.js +5 -0
- package/dist/intelligence/arch-boundaries.js +94 -0
- package/dist/intelligence/arch-graph.js +117 -0
- package/dist/intelligence/autonomy.js +397 -0
- package/dist/intelligence/azure-broker.js +57 -0
- package/dist/intelligence/azure-proxy-sessions.js +49 -0
- package/dist/intelligence/baseline.js +1 -0
- package/dist/intelligence/behavior-baseline.js +134 -0
- package/dist/intelligence/billing.js +1 -0
- package/dist/intelligence/blast-radius.js +224 -0
- package/dist/intelligence/bypass.js +157 -0
- package/dist/intelligence/calibration-classifier.js +77 -0
- package/dist/intelligence/calibration-git-sync.js +74 -0
- package/dist/intelligence/calibration.js +1 -0
- package/dist/intelligence/cascade-detector.js +58 -0
- package/dist/intelligence/case-study-generator.js +126 -0
- package/dist/intelligence/causal-graph.js +126 -0
- package/dist/intelligence/causal.js +1 -0
- package/dist/intelligence/change-risk.js +102 -0
- package/dist/intelligence/compliance-report.js +134 -0
- package/dist/intelligence/confidence-report.js +85 -0
- package/dist/intelligence/corpus-to-policy.js +140 -0
- package/dist/intelligence/credential-broker.js +112 -0
- package/dist/intelligence/credential-misuse.js +39 -0
- package/dist/intelligence/debug-sessions.js +108 -0
- package/dist/intelligence/decision-escalation.js +36 -0
- package/dist/intelligence/decision-memory.js +47 -0
- package/dist/intelligence/decision-similarity.js +85 -0
- package/dist/intelligence/default-safety-tests.js +53 -0
- package/dist/intelligence/degradation-watcher.js +108 -0
- package/dist/intelligence/detector-plugins.js +70 -0
- package/dist/intelligence/detectors.js +1 -0
- package/dist/intelligence/device-auth.js +138 -0
- package/dist/intelligence/diff-size.js +42 -0
- package/dist/intelligence/ebpf-verify.js +45 -0
- package/dist/intelligence/enterprise-policy-engine.js +1122 -0
- package/dist/intelligence/error-fingerprint.js +1 -0
- package/dist/intelligence/execution-gate.js +100 -0
- package/dist/intelligence/execution-mode.js +22 -0
- package/dist/intelligence/gate-analytics.js +256 -0
- package/dist/intelligence/gate-decision.js +64 -0
- package/dist/intelligence/gcp-broker.js +50 -0
- package/dist/intelligence/git-adr-sync.js +207 -0
- package/dist/intelligence/hitl-hold.js +460 -0
- package/dist/intelligence/hypothesis-history.js +93 -0
- package/dist/intelligence/idp-identity.js +83 -0
- package/dist/intelligence/impl-critic.js +216 -0
- package/dist/intelligence/incident-autopilot.js +622 -0
- package/dist/intelligence/incident-replay.js +162 -0
- package/dist/intelligence/incident-result-cache.js +33 -0
- package/dist/intelligence/incident-similarity.js +58 -0
- package/dist/intelligence/infra-detectors.js +312 -0
- package/dist/intelligence/license.js +185 -0
- package/dist/intelligence/llm-spokesperson.js +62 -0
- package/dist/intelligence/mcp-prompts.js +320 -0
- package/dist/intelligence/mcp-resources.js +171 -0
- package/dist/intelligence/normalize.js +108 -0
- package/dist/intelligence/notifications.js +83 -0
- package/dist/intelligence/oidc-issuer.js +87 -0
- package/dist/intelligence/override-corpus.js +597 -0
- package/dist/intelligence/planning-gate.js +84 -0
- package/dist/intelligence/plans.js +223 -0
- package/dist/intelligence/platt-scaling.js +164 -0
- package/dist/intelligence/policy-proposals.js +84 -0
- package/dist/intelligence/policy-suggester.js +161 -0
- package/dist/intelligence/policy-sync.js +103 -0
- package/dist/intelligence/policy-test-runner.js +35 -0
- package/dist/intelligence/postmortem-parser.js +181 -0
- package/dist/intelligence/postmortem-retrieval.js +224 -0
- package/dist/intelligence/postmortem-store.js +596 -0
- package/dist/intelligence/pr-commenter.js +81 -0
- package/dist/intelligence/pr-policy-gate.js +38 -0
- package/dist/intelligence/pr-shadow-analyzer.js +229 -0
- package/dist/intelligence/process-tree.js +108 -0
- package/dist/intelligence/prompts.js +1 -0
- package/dist/intelligence/replay.js +38 -0
- package/dist/intelligence/repro-steps.js +1 -0
- package/dist/intelligence/resource-extractors.js +0 -0
- package/dist/intelligence/resource-file-context.js +138 -0
- package/dist/intelligence/risk-score.js +22 -0
- package/dist/intelligence/rollback.js +137 -0
- package/dist/intelligence/runbook-updater.js +143 -0
- package/dist/intelligence/sandbox.js +194 -0
- package/dist/intelligence/script-trust.js +104 -0
- package/dist/intelligence/session-metrics.js +67 -0
- package/dist/intelligence/session-threat-tracker.js +231 -0
- package/dist/intelligence/shadow-digest-cron.js +81 -0
- package/dist/intelligence/shadow-log.js +284 -0
- package/dist/intelligence/shell-ast.js +145 -0
- package/dist/intelligence/siem-forward.js +104 -0
- package/dist/intelligence/slack-digest.js +151 -0
- package/dist/intelligence/slack-override-loop.js +217 -0
- package/dist/intelligence/slack-routing.js +67 -0
- package/dist/intelligence/slack.js +900 -0
- package/dist/intelligence/sql-ast.js +31 -0
- package/dist/intelligence/team.js +1 -0
- package/dist/intelligence/telemetry.js +76 -0
- package/dist/intelligence/threshold-optimizer.js +75 -0
- package/dist/intelligence/token-budget.js +66 -0
- package/dist/intelligence/tool-guard.js +708 -0
- package/dist/intelligence/tool-manifest 2.js +74 -0
- package/dist/intelligence/tool-manifest.js +125 -0
- package/dist/intelligence/tools-analysis.js +857 -0
- package/dist/intelligence/tools-arch.js +152 -0
- package/dist/intelligence/tools-autonomy.js +495 -0
- package/dist/intelligence/tools-blast-radius.js +140 -0
- package/dist/intelligence/tools-browser.js +431 -0
- package/dist/intelligence/tools-change-timeline.js +154 -0
- package/dist/intelligence/tools-credentials.js +117 -0
- package/dist/intelligence/tools-datadog.js +233 -0
- package/dist/intelligence/tools-debug-sessions.js +209 -0
- package/dist/intelligence/tools-discovery.js +284 -0
- package/dist/intelligence/tools-infra.js +612 -0
- package/dist/intelligence/tools-intent.js +128 -0
- package/dist/intelligence/tools-memory.js +514 -0
- package/dist/intelligence/tools-runbook.js +951 -0
- package/dist/intelligence/tools-sessions.js +107 -0
- package/dist/intelligence/tools-state.js +232 -0
- package/dist/intelligence/tools-utility.js +430 -0
- package/dist/intelligence/tools-validate.js +215 -0
- package/dist/intelligence/tools.js +1 -0
- package/dist/intelligence/trace-context.js +11 -0
- package/dist/intelligence/unclassified-clusters.js +87 -0
- package/dist/intelligence/usage.js +195 -0
- package/dist/routes/active-authors.js +63 -0
- package/dist/routes/activity-feed.js +21 -0
- package/dist/routes/adr.js +117 -0
- package/dist/routes/agent-activity.js +184 -0
- package/dist/routes/agent-blunders.js +163 -0
- package/dist/routes/agents.js +175 -0
- package/dist/routes/api-keys.js +65 -0
- package/dist/routes/arch.js +75 -0
- package/dist/routes/audit-export.js +93 -0
- package/dist/routes/billing-dashboard.js +158 -0
- package/dist/routes/billing-outcome.js +77 -0
- package/dist/routes/calibration.js +305 -0
- package/dist/routes/ci-gate-history.js +20 -0
- package/dist/routes/ci-gate.js +232 -0
- package/dist/routes/ci.js +293 -0
- package/dist/routes/compliance-report.js +24 -0
- package/dist/routes/confidence.js +30 -0
- package/dist/routes/credentials.js +119 -0
- package/dist/routes/dashboard.js +1407 -0
- package/dist/routes/decision-memory.js +42 -0
- package/dist/routes/demo.js +844 -0
- package/dist/routes/device-auth-stub.js +173 -0
- package/dist/routes/explain-why.js +39 -0
- package/dist/routes/gate-analytics.js +213 -0
- package/dist/routes/gate.js +28 -0
- package/dist/routes/github-webhook.js +364 -0
- package/dist/routes/habituation.js +30 -0
- package/dist/routes/health-integrations.js +243 -0
- package/dist/routes/heartbeats.js +45 -0
- package/dist/routes/hitl.js +364 -0
- package/dist/routes/impact-report.js +645 -0
- package/dist/routes/incident-webhook.js +63 -0
- package/dist/routes/incidents.js +366 -0
- package/dist/routes/layers.js +62 -0
- package/dist/routes/license.js +187 -0
- package/dist/routes/oidc-issuer.js +30 -0
- package/dist/routes/onboarding.js +170 -0
- package/dist/routes/otel.js +67 -0
- package/dist/routes/otlp-receiver.js +179 -0
- package/dist/routes/overrides.js +241 -0
- package/dist/routes/pagerduty.js +258 -0
- package/dist/routes/policies.js +809 -0
- package/dist/routes/policy-nl.js +169 -0
- package/dist/routes/postmortem.js +102 -0
- package/dist/routes/pr-shadow.js +47 -0
- package/dist/routes/rbac.js +61 -0
- package/dist/routes/replay.js +17 -0
- package/dist/routes/risk-report.js +164 -0
- package/dist/routes/runbooks.js +125 -0
- package/dist/routes/safety-test.js +174 -0
- package/dist/routes/sdk.js +222 -0
- package/dist/routes/sensor.js +819 -0
- package/dist/routes/sentry.js +140 -0
- package/dist/routes/sessions.js +43 -0
- package/dist/routes/setup-ui.js +594 -0
- package/dist/routes/shadow-report.js +107 -0
- package/dist/routes/slack-routing.js +171 -0
- package/dist/routes/team-usage.js +71 -0
- package/dist/routes/telemetry.js +28 -0
- package/dist/routes/tenants.js +199 -0
- package/dist/routes/tickets.js +167 -0
- package/dist/routes/validate.js +13 -0
- package/dist/routes/war-room.js +113 -0
- package/dist/scripts/check-arch.js +22 -0
- package/dist/seeds/community-corpus.js +176 -0
- package/dist/seeds/corpus.js +1224 -0
- package/dist/sensor/action-ledger.js +327 -0
- package/dist/sensor/adr-store.js +140 -0
- package/dist/sensor/agent-blunder-store.js +370 -0
- package/dist/sensor/agent-context-store.js +225 -0
- package/dist/sensor/agent-memory-store.js +231 -0
- package/dist/sensor/audit-fetch.js +23 -0
- package/dist/sensor/audit-log.js +273 -0
- package/dist/sensor/buffer-schemas.js +225 -0
- package/dist/sensor/buffer.js +599 -0
- package/dist/sensor/bypass-tracker.js +93 -0
- package/dist/sensor/ci-gate-history.js +70 -0
- package/dist/sensor/cloud-auth.js +183 -0
- package/dist/sensor/commit-context-store.js +235 -0
- package/dist/sensor/docker-log-stream.js +164 -0
- package/dist/sensor/docker-monitor.js +122 -0
- package/dist/sensor/extended-buffer.js +46 -0
- package/dist/sensor/feedback-token.js +41 -0
- package/dist/sensor/file-lock.js +6 -0
- package/dist/sensor/fs-watcher.js +106 -0
- package/dist/sensor/gate-heartbeat.js +122 -0
- package/dist/sensor/git-suspect.js +136 -0
- package/dist/sensor/habituation-store.js +77 -0
- package/dist/sensor/heartbeat-monitor.js +133 -0
- package/dist/sensor/incident-store.js +340 -0
- package/dist/sensor/infra-normalizer.js +513 -0
- package/dist/sensor/ingest.js +339 -0
- package/dist/sensor/jest-reporter.js +52 -0
- package/dist/sensor/k8s-events.js +132 -0
- package/dist/sensor/layer2-store.js +111 -0
- package/dist/sensor/layer3-store.js +230 -0
- package/dist/sensor/layer4-store.js +147 -0
- package/dist/sensor/logger.js +13 -0
- package/dist/sensor/otel-exporter.js +161 -0
- package/dist/sensor/paths.js +71 -0
- package/dist/sensor/policy-history.js +147 -0
- package/dist/sensor/pr-shadow-store.js +83 -0
- package/dist/sensor/process-watcher.js +162 -0
- package/dist/sensor/rbac.js +92 -0
- package/dist/sensor/redact.js +114 -0
- package/dist/sensor/redis-store.js +191 -0
- package/dist/sensor/route-reachability.js +64 -0
- package/dist/sensor/security-utils.js +18 -0
- package/dist/sensor/service-graph.js +170 -0
- package/dist/sensor/service-topology.js +227 -0
- package/dist/sensor/session-history.js +74 -0
- package/dist/sensor/session-persist.js +38 -0
- package/dist/sensor/shadow-promote.js +122 -0
- package/dist/sensor/sourcemap.js +281 -0
- package/dist/sensor/sqlite-store.js +205 -0
- package/dist/sensor/sso.js +164 -0
- package/dist/sensor/vitest-reporter.js +65 -0
- package/dist/sensor/watcher.js +48 -0
- package/dist/storage/interfaces.js +0 -0
- package/dist/storage/pg/pg-action-ledger.js +138 -0
- package/dist/storage/pg/pg-approval-store.js +107 -0
- package/dist/storage/pg/pg-blunder-store.js +193 -0
- package/dist/storage/pg/pg-ci-gate-history.js +83 -0
- package/dist/storage/pg/pg-client.js +24 -0
- package/dist/storage/pg/pg-event-store.js +63 -0
- package/dist/storage/pg/pg-incident-store.js +190 -0
- package/dist/storage/pg/pg-migrations.js +26 -0
- package/dist/storage/pg/pg-override-corpus.js +447 -0
- package/dist/storage/pg/pg-shadow-log.js +129 -0
- package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
- package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
- package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
- package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
- package/dist/storage/sqlite/sqlite-event-store.js +29 -0
- package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
- package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
- package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
- package/dist/storage/store-factory.js +55 -0
- package/dist/storage/store-registry.js +31 -0
- package/dist/update-checker.js +107 -0
- package/dist/workers/autopilot-worker.js +28 -0
- package/dist/workers/notification-worker.js +28 -0
- package/dist/workers/queues.js +58 -0
- package/dist/workers/validation-worker.js +24 -0
- package/dist/workers/worker-registry.js +18 -0
- package/package.json +123 -0
- package/sdk/mergen-inject.js +260 -0
- package/sdk/node.js +313 -0
- package/sdk/vite-plugin.ts +57 -0
- package/sdk/webpack-plugin.js +106 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { store } from "../sensor/buffer.js";
|
|
2
|
+
import { buildCausalChain } from "./causal.js";
|
|
3
|
+
const MAX_HISTORY = 50;
|
|
4
|
+
let _entries = [];
|
|
5
|
+
let _pendingRebuild = Promise.resolve();
|
|
6
|
+
function flushPendingRebuild() {
|
|
7
|
+
return _pendingRebuild;
|
|
8
|
+
}
|
|
9
|
+
function _triggerKey(entry) {
|
|
10
|
+
return entry.triggerMessage;
|
|
11
|
+
}
|
|
12
|
+
async function _build(reason) {
|
|
13
|
+
const logs = store.getLogs(50);
|
|
14
|
+
const network = store.getNetwork(50);
|
|
15
|
+
const context = store.getContext(10);
|
|
16
|
+
const errors = logs.filter((e) => e.level === "error");
|
|
17
|
+
if (reason === "error" || reason === "default" || !reason) {
|
|
18
|
+
if (errors.length === 0) return null;
|
|
19
|
+
const latest = errors[errors.length - 1];
|
|
20
|
+
const triggerMsg = latest.args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
21
|
+
const chain2 = await buildCausalChain(logs, network, context);
|
|
22
|
+
return {
|
|
23
|
+
topHypothesis: chain2.hypotheses[0] ?? null,
|
|
24
|
+
chain: chain2,
|
|
25
|
+
builtAt: Date.now(),
|
|
26
|
+
builtAtIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27
|
+
triggerMessage: triggerMsg,
|
|
28
|
+
reason: reason || "error"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (reason === "pageload") {
|
|
32
|
+
if (network.length === 0 && logs.length === 0) return null;
|
|
33
|
+
const chain2 = await buildCausalChain(logs, network, context);
|
|
34
|
+
return {
|
|
35
|
+
topHypothesis: chain2.hypotheses[0] ?? null,
|
|
36
|
+
chain: chain2,
|
|
37
|
+
builtAt: Date.now(),
|
|
38
|
+
builtAtIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39
|
+
triggerMessage: "Page loaded \u2014 baseline captured",
|
|
40
|
+
reason: "pageload"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (errors.length === 0 && network.length === 0) return null;
|
|
44
|
+
const chain = await buildCausalChain(logs, network, context);
|
|
45
|
+
return {
|
|
46
|
+
topHypothesis: chain.hypotheses[0] ?? null,
|
|
47
|
+
chain,
|
|
48
|
+
builtAt: Date.now(),
|
|
49
|
+
builtAtIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
50
|
+
triggerMessage: errors[errors.length - 1] ? errors[errors.length - 1].args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") : "Periodic baseline",
|
|
51
|
+
reason
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const hypothesisHistory = {
|
|
55
|
+
size() {
|
|
56
|
+
return _entries.length;
|
|
57
|
+
},
|
|
58
|
+
latest() {
|
|
59
|
+
return _entries[_entries.length - 1] ?? null;
|
|
60
|
+
},
|
|
61
|
+
list(limit) {
|
|
62
|
+
const rows = [..._entries].reverse().slice(0, limit ?? _entries.length);
|
|
63
|
+
return rows.map(({ chain: _chain, ...rest }) => rest);
|
|
64
|
+
},
|
|
65
|
+
clear() {
|
|
66
|
+
_entries = [];
|
|
67
|
+
},
|
|
68
|
+
add(entry) {
|
|
69
|
+
_entries.push(entry);
|
|
70
|
+
if (_entries.length > MAX_HISTORY) _entries.shift();
|
|
71
|
+
},
|
|
72
|
+
notifyError(_pid, _err) {
|
|
73
|
+
const p = new Promise((resolve) => setTimeout(resolve, 0)).then(() => this._rebuildNowForTesting("error")).catch(() => {
|
|
74
|
+
});
|
|
75
|
+
_pendingRebuild = _pendingRebuild.then(() => p);
|
|
76
|
+
},
|
|
77
|
+
async _rebuildNowForTesting(reason = "error") {
|
|
78
|
+
const entry = await _build(reason);
|
|
79
|
+
if (!entry) return;
|
|
80
|
+
const key = _triggerKey(entry);
|
|
81
|
+
const existingIdx = _entries.findIndex((e) => _triggerKey(e) === key);
|
|
82
|
+
if (existingIdx >= 0) {
|
|
83
|
+
_entries[existingIdx] = entry;
|
|
84
|
+
} else {
|
|
85
|
+
_entries.push(entry);
|
|
86
|
+
if (_entries.length > MAX_HISTORY) _entries.shift();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
export {
|
|
91
|
+
flushPendingRebuild,
|
|
92
|
+
hypothesisHistory
|
|
93
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2
|
+
let _oktaJwks = null;
|
|
3
|
+
let _entraJwks = null;
|
|
4
|
+
function _oktaJwksUrl(domain) {
|
|
5
|
+
return new URL(`https://${domain}/oauth2/v1/keys`);
|
|
6
|
+
}
|
|
7
|
+
function _entraJwksUrl(tenantId) {
|
|
8
|
+
const tid = tenantId.includes(".") ? tenantId : tenantId;
|
|
9
|
+
return new URL(`https://login.microsoftonline.com/${tid}/discovery/v2.0/keys`);
|
|
10
|
+
}
|
|
11
|
+
function _entraIssuer(tenantId) {
|
|
12
|
+
return `https://login.microsoftonline.com/${tenantId}/v2.0`;
|
|
13
|
+
}
|
|
14
|
+
function _oktaIssuer(domain) {
|
|
15
|
+
return `https://${domain}`;
|
|
16
|
+
}
|
|
17
|
+
function _extractBearer(req) {
|
|
18
|
+
const auth = req.headers.authorization;
|
|
19
|
+
if (!auth || !auth.startsWith("Bearer ")) return null;
|
|
20
|
+
return auth.slice(7).trim() || null;
|
|
21
|
+
}
|
|
22
|
+
async function _validateOkta(token, domain) {
|
|
23
|
+
try {
|
|
24
|
+
if (!_oktaJwks) _oktaJwks = createRemoteJWKSet(_oktaJwksUrl(domain));
|
|
25
|
+
const { payload } = await jwtVerify(token, _oktaJwks, {
|
|
26
|
+
issuer: _oktaIssuer(domain)
|
|
27
|
+
});
|
|
28
|
+
return payload.email ?? payload.sub ?? null;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function _validateEntra(token, tenantId) {
|
|
34
|
+
try {
|
|
35
|
+
if (!_entraJwks) _entraJwks = createRemoteJWKSet(_entraJwksUrl(tenantId));
|
|
36
|
+
const { payload } = await jwtVerify(token, _entraJwks, {
|
|
37
|
+
issuer: _entraIssuer(tenantId)
|
|
38
|
+
});
|
|
39
|
+
return payload["preferred_username"] ?? payload.email ?? payload.sub ?? null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function idpIdentityMiddleware() {
|
|
45
|
+
const oktaDomain = process.env.MERGEN_OKTA_DOMAIN?.trim() || null;
|
|
46
|
+
const entraId = process.env.MERGEN_ENTRA_TENANT_ID?.trim() || null;
|
|
47
|
+
if (!oktaDomain && !entraId) {
|
|
48
|
+
return (_req, _res, next) => next();
|
|
49
|
+
}
|
|
50
|
+
return async (req, _res, next) => {
|
|
51
|
+
try {
|
|
52
|
+
const token = _extractBearer(req);
|
|
53
|
+
if (token) {
|
|
54
|
+
let userId = null;
|
|
55
|
+
if (oktaDomain) userId = await _validateOkta(token, oktaDomain);
|
|
56
|
+
if (!userId && entraId) userId = await _validateEntra(token, entraId);
|
|
57
|
+
if (userId) req.mergenUserId = userId;
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
next();
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function resolveRequestIdentity(authHeader) {
|
|
65
|
+
if (!authHeader?.startsWith("Bearer ")) return null;
|
|
66
|
+
const token = authHeader.slice(7).trim();
|
|
67
|
+
if (!token) return null;
|
|
68
|
+
const oktaDomain = process.env.MERGEN_OKTA_DOMAIN?.trim() || null;
|
|
69
|
+
const entraId = process.env.MERGEN_ENTRA_TENANT_ID?.trim() || null;
|
|
70
|
+
if (oktaDomain) {
|
|
71
|
+
const id = await _validateOkta(token, oktaDomain);
|
|
72
|
+
if (id) return id;
|
|
73
|
+
}
|
|
74
|
+
if (entraId) {
|
|
75
|
+
const id = await _validateEntra(token, entraId);
|
|
76
|
+
if (id) return id;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
idpIdentityMiddleware,
|
|
82
|
+
resolveRequestIdentity
|
|
83
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { checkBoundaries } from "./arch-boundaries.js";
|
|
4
|
+
import { confidenceStore } from "./confidence-report.js";
|
|
5
|
+
const SECURITY_PATTERNS = [
|
|
6
|
+
{
|
|
7
|
+
re: /catch\s*\([^)]*\)\s*\{\s*\}/,
|
|
8
|
+
message: "Empty catch block silently swallows errors",
|
|
9
|
+
suggestion: "Log the error to the Mergen logger or rethrow. Never use bare catch {}.",
|
|
10
|
+
severity: "error"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
re: /catch\s*\([^)]*\)\s*\{\s*\/\//,
|
|
14
|
+
message: "Catch block contains only a comment \u2014 error is still silently dropped",
|
|
15
|
+
suggestion: "Log via logger.warn/error or rethrow the exception.",
|
|
16
|
+
severity: "warning"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
re: /['"`][A-Za-z0-9+/]{20,}={0,2}['"`]/,
|
|
20
|
+
message: "Possible hardcoded credential or token",
|
|
21
|
+
suggestion: "Use environment variables. Check this is not an accidentally committed secret.",
|
|
22
|
+
severity: "warning"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
re: /eval\s*\(/,
|
|
26
|
+
message: "eval() is forbidden \u2014 arbitrary code execution risk",
|
|
27
|
+
suggestion: "Remove eval(). Use explicit logic or JSON.parse for data.",
|
|
28
|
+
severity: "error"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
re: /innerHTML\s*=/,
|
|
32
|
+
message: "innerHTML assignment is an XSS vector",
|
|
33
|
+
suggestion: "Use textContent or a sanitiser library.",
|
|
34
|
+
severity: "warning"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
re: /exec\s*\(\s*`[^`]*\$\{/,
|
|
38
|
+
message: "Shell command contains template literal interpolation \u2014 command injection risk",
|
|
39
|
+
suggestion: "Use execFile() with an array of arguments instead of string interpolation.",
|
|
40
|
+
severity: "error"
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
function scanSecurityPatterns(source, file, relFile) {
|
|
44
|
+
const findings = [];
|
|
45
|
+
const lines = source.split("\n");
|
|
46
|
+
for (let i = 0; i < lines.length; i++) {
|
|
47
|
+
const line = lines[i];
|
|
48
|
+
for (const p of SECURITY_PATTERNS) {
|
|
49
|
+
if (p.re.test(line)) {
|
|
50
|
+
findings.push({
|
|
51
|
+
severity: p.severity,
|
|
52
|
+
category: "security",
|
|
53
|
+
file: relFile,
|
|
54
|
+
line: i + 1,
|
|
55
|
+
message: p.message,
|
|
56
|
+
suggestion: p.suggestion
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return findings;
|
|
62
|
+
}
|
|
63
|
+
const HARDCODED_RETURN_RE = /return\s+(true|false|0|1|null|\[\]|\{\})\s*;/g;
|
|
64
|
+
const MOCK_BYPASS_RE = /mockReturnValue\(|mockResolvedValue\(/;
|
|
65
|
+
function scanHardcodedReturns(source, relFile) {
|
|
66
|
+
if (relFile.endsWith(".test.ts")) return [];
|
|
67
|
+
const findings = [];
|
|
68
|
+
const lines = source.split("\n");
|
|
69
|
+
for (let i = 0; i < lines.length; i++) {
|
|
70
|
+
HARDCODED_RETURN_RE.lastIndex = 0;
|
|
71
|
+
if (HARDCODED_RETURN_RE.test(lines[i]) && !lines[i].trim().startsWith("//")) {
|
|
72
|
+
const context = lines.slice(Math.max(0, i - 5), i).join("\n");
|
|
73
|
+
if (/function|=>|\basync\b/.test(context)) {
|
|
74
|
+
findings.push({
|
|
75
|
+
severity: "warning",
|
|
76
|
+
category: "hardcoded-return",
|
|
77
|
+
file: relFile,
|
|
78
|
+
line: i + 1,
|
|
79
|
+
message: "Function returns a hardcoded literal \u2014 may be an incomplete implementation",
|
|
80
|
+
suggestion: "Verify this is intentional, not a stub left after test-driving."
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return findings;
|
|
86
|
+
}
|
|
87
|
+
function checkAdrViolations(source, relFile) {
|
|
88
|
+
const findings = [];
|
|
89
|
+
if (/MERGEN_BUFFER_SIZE|bufferSize|capacity/.test(source)) {
|
|
90
|
+
const m = source.match(/(?:bufferSize|capacity|MERGEN_BUFFER_SIZE)\D*(\d{5,})/);
|
|
91
|
+
if (m && Number(m[1]) > 1e4) {
|
|
92
|
+
findings.push({
|
|
93
|
+
severity: "warning",
|
|
94
|
+
category: "adr-violation",
|
|
95
|
+
file: relFile,
|
|
96
|
+
message: `ADR-001: ring buffer capacity set to ${m[1]} \u2014 this may cause OOM. The default is 2 000.`,
|
|
97
|
+
suggestion: "Review ADR-001 before increasing the buffer cap. Use SQLite history for older events."
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (/res\.json\(|res\.send\(/.test(source) && relFile.includes("intelligence/")) {
|
|
102
|
+
findings.push({
|
|
103
|
+
severity: "warning",
|
|
104
|
+
category: "adr-violation",
|
|
105
|
+
file: relFile,
|
|
106
|
+
message: "ADR-002: intelligence/* file appears to use Express res.json() \u2014 tools must return MCP result objects, not HTTP responses.",
|
|
107
|
+
suggestion: 'Return { content: [{ type: "text", text: "..." }] } from MCP tool handlers. Route handling belongs in routes/*.'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (/0\.0\.0\.0/.test(source) && !source.includes("CLOUD_MODE") && !source.includes("MERGEN_HOST")) {
|
|
111
|
+
findings.push({
|
|
112
|
+
severity: "warning",
|
|
113
|
+
category: "adr-violation",
|
|
114
|
+
file: relFile,
|
|
115
|
+
message: "ADR-004: binding to 0.0.0.0 without CLOUD_MODE or MERGEN_HOST guard.",
|
|
116
|
+
suggestion: "Wrap in a CLOUD_MODE or MERGEN_HOST check. Default binding must remain 127.0.0.1."
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return findings;
|
|
120
|
+
}
|
|
121
|
+
function checkMissingWorkflow(files) {
|
|
122
|
+
const findings = [];
|
|
123
|
+
const hasNonTestFiles = files.some((f) => !f.endsWith(".test.ts") && !f.endsWith(".d.ts"));
|
|
124
|
+
if (!hasNonTestFiles) return findings;
|
|
125
|
+
const recent = confidenceStore.list(5);
|
|
126
|
+
if (recent.length === 0) {
|
|
127
|
+
findings.push({
|
|
128
|
+
severity: "warning",
|
|
129
|
+
category: "missing-workflow",
|
|
130
|
+
file: "(session)",
|
|
131
|
+
message: "No confidence report filed before this implementation.",
|
|
132
|
+
suggestion: "Call report_confidence before making changes to declare assumptions and unknowns."
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return findings;
|
|
136
|
+
}
|
|
137
|
+
function critiqueImplementation(opts) {
|
|
138
|
+
const { files, srcDir } = opts;
|
|
139
|
+
const allFindings = [];
|
|
140
|
+
for (const file of files) {
|
|
141
|
+
if (!file.endsWith(".ts") || file.endsWith(".d.ts")) continue;
|
|
142
|
+
const result = checkBoundaries({ srcDir, singleFile: file });
|
|
143
|
+
for (const v of result.violations) {
|
|
144
|
+
allFindings.push({
|
|
145
|
+
severity: "error",
|
|
146
|
+
category: "boundary-violation",
|
|
147
|
+
file: v.relativeFile,
|
|
148
|
+
message: `Imports \`${v.relativeImport}\` \u2014 ${v.fromZone} cannot import ${v.importedZone}`,
|
|
149
|
+
suggestion: v.rule.rationale
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
for (const file of files) {
|
|
154
|
+
if (!file.endsWith(".ts") || file.endsWith(".d.ts")) continue;
|
|
155
|
+
let source;
|
|
156
|
+
try {
|
|
157
|
+
source = fs.readFileSync(file, "utf8");
|
|
158
|
+
} catch {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const relFile = path.relative(srcDir, file);
|
|
162
|
+
allFindings.push(...scanSecurityPatterns(source, file, relFile));
|
|
163
|
+
allFindings.push(...scanHardcodedReturns(source, relFile));
|
|
164
|
+
allFindings.push(...checkAdrViolations(source, relFile));
|
|
165
|
+
}
|
|
166
|
+
allFindings.push(...checkMissingWorkflow(files));
|
|
167
|
+
const errors = allFindings.filter((f) => f.severity === "error").length;
|
|
168
|
+
const warnings = allFindings.filter((f) => f.severity === "warning").length;
|
|
169
|
+
const passed = errors === 0;
|
|
170
|
+
const summary = passed ? `Critique passed \u2014 ${warnings} warning(s), 0 errors across ${files.length} file(s).` : `Critique failed \u2014 ${errors} error(s) and ${warnings} warning(s) across ${files.length} file(s). Fix errors before merging.`;
|
|
171
|
+
return { files, findings: allFindings, errors, warnings, passed, summary };
|
|
172
|
+
}
|
|
173
|
+
function formatCritiqueReport(r) {
|
|
174
|
+
const icon = r.passed ? "\u2705" : "\u274C";
|
|
175
|
+
const lines = [
|
|
176
|
+
`## Post-Implementation Critique: ${r.passed ? "PASS" : "FAIL"} ${icon}`,
|
|
177
|
+
"",
|
|
178
|
+
r.summary,
|
|
179
|
+
""
|
|
180
|
+
];
|
|
181
|
+
if (r.findings.length === 0) {
|
|
182
|
+
lines.push("No findings. Implementation looks clean.");
|
|
183
|
+
return lines.join("\n");
|
|
184
|
+
}
|
|
185
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
186
|
+
for (const f of r.findings) {
|
|
187
|
+
const arr = byCategory.get(f.category) ?? [];
|
|
188
|
+
arr.push(f);
|
|
189
|
+
byCategory.set(f.category, arr);
|
|
190
|
+
}
|
|
191
|
+
const categoryLabels = {
|
|
192
|
+
"boundary-violation": "Architectural Boundary Violations",
|
|
193
|
+
"adr-violation": "ADR Violations",
|
|
194
|
+
"missing-workflow": "Missing Workflow Steps",
|
|
195
|
+
"security": "Security Issues",
|
|
196
|
+
"test-erasure": "Test Erasure",
|
|
197
|
+
"hardcoded-return": "Hard-Coded Returns",
|
|
198
|
+
"duplication": "Duplication Signals"
|
|
199
|
+
};
|
|
200
|
+
for (const [cat, findings] of byCategory) {
|
|
201
|
+
lines.push(`### ${categoryLabels[cat] ?? cat}`, "");
|
|
202
|
+
for (const f of findings) {
|
|
203
|
+
const icon2 = f.severity === "error" ? "\u{1F534}" : f.severity === "warning" ? "\u{1F7E1}" : "\u{1F535}";
|
|
204
|
+
const loc = f.line ? `\`${f.file}:${f.line}\`` : `\`${f.file}\``;
|
|
205
|
+
lines.push(`${icon2} **${f.severity.toUpperCase()}** ${loc}`);
|
|
206
|
+
lines.push(` ${f.message}`);
|
|
207
|
+
lines.push(` > ${f.suggestion}`);
|
|
208
|
+
lines.push("");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return lines.join("\n");
|
|
212
|
+
}
|
|
213
|
+
export {
|
|
214
|
+
critiqueImplementation,
|
|
215
|
+
formatCritiqueReport
|
|
216
|
+
};
|