@lannguyensi/harness 0.25.1 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/cli/apply/apply.d.ts +11 -0
- package/dist/cli/apply/apply.js +11 -0
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/event-input.d.ts +28 -0
- package/dist/cli/event-input.js +72 -0
- package/dist/cli/event-input.js.map +1 -0
- package/dist/cli/explain-action.d.ts +20 -0
- package/dist/cli/explain-action.js +27 -0
- package/dist/cli/explain-action.js.map +1 -0
- package/dist/cli/index.js +76 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/interactive.js +10 -3
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.js +1 -1
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-post-tool-use.js +1 -1
- package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.d.ts +8 -0
- package/dist/cli/pack/hook-pre-tool-use.js +3 -1
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-track-active-claim.js +1 -1
- package/dist/cli/pack/hook-track-active-claim.js.map +1 -1
- package/dist/cli/{pack/pause-check.d.ts → pause-check.d.ts} +1 -1
- package/dist/cli/{pack/pause-check.js → pause-check.js} +14 -11
- package/dist/cli/pause-check.js.map +1 -0
- package/dist/cli/policy/intercept.d.ts +5 -0
- package/dist/cli/policy/intercept.js +21 -0
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/resolve-env.d.ts +32 -0
- package/dist/cli/resolve-env.js +47 -0
- package/dist/cli/resolve-env.js.map +1 -0
- package/dist/cli/test-risk.d.ts +26 -0
- package/dist/cli/test-risk.js +34 -0
- package/dist/cli/test-risk.js.map +1 -0
- package/dist/runtime/action-envelope.d.ts +64 -0
- package/dist/runtime/action-envelope.js +46 -0
- package/dist/runtime/action-envelope.js.map +1 -0
- package/dist/runtime/environment-resolver.d.ts +36 -0
- package/dist/runtime/environment-resolver.js +138 -0
- package/dist/runtime/environment-resolver.js.map +1 -0
- package/dist/runtime/home-dir.js +1 -1
- package/dist/runtime/home-dir.js.map +1 -1
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/kube-context.d.ts +16 -0
- package/dist/runtime/kube-context.js +63 -0
- package/dist/runtime/kube-context.js.map +1 -0
- package/dist/runtime/risk-classifier.d.ts +38 -0
- package/dist/runtime/risk-classifier.js +121 -0
- package/dist/runtime/risk-classifier.js.map +1 -0
- package/dist/schema/environments.d.ts +215 -0
- package/dist/schema/environments.js +101 -0
- package/dist/schema/environments.js.map +1 -0
- package/dist/schema/index.d.ts +408 -0
- package/dist/schema/index.js +8 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/policies.d.ts +139 -0
- package/dist/schema/policies.js +39 -0
- package/dist/schema/policies.js.map +1 -1
- package/dist/schema/risk.d.ts +131 -0
- package/dist/schema/risk.js +87 -0
- package/dist/schema/risk.js.map +1 -0
- package/package.json +1 -1
- package/dist/cli/pack/pause-check.js.map +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Phase 7 #4 — Context Resolver.
|
|
2
|
+
//
|
|
3
|
+
// Classifies an Action Envelope's target environment by matching the
|
|
4
|
+
// manifest's `environments.resolvers[]` signals. The Risk Gate stage
|
|
5
|
+
// that reads the `environments:` schema vocabulary shipped in
|
|
6
|
+
// Phase 7 #1.
|
|
7
|
+
//
|
|
8
|
+
// STATUS: invoked by `harness resolve-env` (Phase 7 #4). NOT yet
|
|
9
|
+
// consumed by `harness policy intercept` — wiring the runtime through
|
|
10
|
+
// the resolver is Phase 7 #5. See docs/risk-gate.md and docs/ROADMAP.md.
|
|
11
|
+
//
|
|
12
|
+
// "Unknown is not safe": when no resolver matches, the environment is
|
|
13
|
+
// `unknown` — not a safe default. The Phase 7 #5 policy evaluator must
|
|
14
|
+
// treat `unknown` as risk-bearing. `MatchableEnvironmentSchema` already
|
|
15
|
+
// admits `unknown` so a `policy.when.environment.name` clause can gate
|
|
16
|
+
// on it.
|
|
17
|
+
//
|
|
18
|
+
// Design source: lava-ice-logs/2026-04-30/harness-risk-gate-extension.md
|
|
19
|
+
// (design phase C).
|
|
20
|
+
// Most-dangerous-wins precedence: when resolvers disagree, the earlier
|
|
21
|
+
// entry here is the resolved environment. `unknown` is the implicit
|
|
22
|
+
// fallback and is not a resolver-assertable value.
|
|
23
|
+
const ENV_PRECEDENCE = [
|
|
24
|
+
"production",
|
|
25
|
+
"staging",
|
|
26
|
+
"dev",
|
|
27
|
+
"local",
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Convert a `*`-glob to an anchored RegExp. Only `*` is special (the
|
|
31
|
+
* Phase 7 #1 signal patterns are simple, e.g. `main`, `release/*`); all
|
|
32
|
+
* other regex metacharacters are escaped literally.
|
|
33
|
+
*/
|
|
34
|
+
function globToRegExp(glob) {
|
|
35
|
+
const escaped = glob
|
|
36
|
+
.replace(/[.+^${}()|[\]\\?]/g, "\\$&")
|
|
37
|
+
.replace(/\*/g, ".*");
|
|
38
|
+
return new RegExp(`^${escaped}$`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Match one resolver's signals against the envelope + ambient inputs.
|
|
42
|
+
* Signals are OR-ed: a resolver fires when ANY signal matches. Returns
|
|
43
|
+
* the matched signal descriptors (empty when the resolver did not fire).
|
|
44
|
+
*
|
|
45
|
+
* Per-signal-kind semantics: `branch_patterns` and
|
|
46
|
+
* `kube_namespace_patterns` are globs, `kube_context_patterns` are
|
|
47
|
+
* regexes, `env_var_patterns` are substrings of the variable's value.
|
|
48
|
+
*/
|
|
49
|
+
function matchResolver(resolver, envelope, inputs) {
|
|
50
|
+
const matched = [];
|
|
51
|
+
const sig = resolver.signals;
|
|
52
|
+
const branch = envelope.session.branch;
|
|
53
|
+
if (sig.branch_patterns !== undefined && branch !== "") {
|
|
54
|
+
for (const p of sig.branch_patterns) {
|
|
55
|
+
if (globToRegExp(p).test(branch)) {
|
|
56
|
+
matched.push(`branch:${branch} ~ ${p}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (sig.env_var_patterns !== undefined) {
|
|
61
|
+
for (const evp of sig.env_var_patterns) {
|
|
62
|
+
const value = inputs.env[evp.var];
|
|
63
|
+
if (typeof value !== "string")
|
|
64
|
+
continue;
|
|
65
|
+
for (const p of evp.patterns) {
|
|
66
|
+
if (value.includes(p)) {
|
|
67
|
+
matched.push(`env:${evp.var} contains "${p}"`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (sig.kube_context_patterns !== undefined && inputs.kubeContext !== "") {
|
|
73
|
+
for (const p of sig.kube_context_patterns) {
|
|
74
|
+
let re;
|
|
75
|
+
try {
|
|
76
|
+
re = new RegExp(p);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (re.test(inputs.kubeContext)) {
|
|
82
|
+
matched.push(`kube-context:${inputs.kubeContext} ~ /${p}/`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (sig.kube_namespace_patterns !== undefined &&
|
|
87
|
+
inputs.kubeNamespace !== "") {
|
|
88
|
+
for (const p of sig.kube_namespace_patterns) {
|
|
89
|
+
if (globToRegExp(p).test(inputs.kubeNamespace)) {
|
|
90
|
+
matched.push(`kube-namespace:${inputs.kubeNamespace} ~ ${p}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return matched;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the target environment of an Action Envelope.
|
|
98
|
+
*
|
|
99
|
+
* Pure: envelope + resolvers + ambient inputs in, resolution out, no
|
|
100
|
+
* I/O. When resolvers disagree, the most-dangerous environment wins
|
|
101
|
+
* (`ENV_PRECEDENCE`). Signals from every fired resolver that asserts the
|
|
102
|
+
* winning environment are unioned for explainability. When nothing
|
|
103
|
+
* matches, the result is `unknown` — deliberately not a safe default.
|
|
104
|
+
*/
|
|
105
|
+
export function resolveEnvironment(envelope, resolvers, inputs) {
|
|
106
|
+
const fired = [];
|
|
107
|
+
for (const resolver of resolvers) {
|
|
108
|
+
const signals = matchResolver(resolver, envelope, inputs);
|
|
109
|
+
if (signals.length > 0)
|
|
110
|
+
fired.push({ resolver, signals });
|
|
111
|
+
}
|
|
112
|
+
if (fired.length === 0) {
|
|
113
|
+
return { name: "unknown", confidence: "low", signals: [], resolver: null };
|
|
114
|
+
}
|
|
115
|
+
let best = fired[0];
|
|
116
|
+
for (const f of fired) {
|
|
117
|
+
if (ENV_PRECEDENCE.indexOf(f.resolver.environment) <
|
|
118
|
+
ENV_PRECEDENCE.indexOf(best.resolver.environment)) {
|
|
119
|
+
best = f;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Union the signals of every fired resolver that asserts the winning
|
|
123
|
+
// environment, so the explanation is complete when several resolvers
|
|
124
|
+
// agree. `resolver` names the highest-precedence (first-found) one.
|
|
125
|
+
const winningEnv = best.resolver.environment;
|
|
126
|
+
const signals = [
|
|
127
|
+
...new Set(fired
|
|
128
|
+
.filter((f) => f.resolver.environment === winningEnv)
|
|
129
|
+
.flatMap((f) => f.signals)),
|
|
130
|
+
].sort();
|
|
131
|
+
return {
|
|
132
|
+
name: winningEnv,
|
|
133
|
+
confidence: signals.length >= 2 ? "high" : "medium",
|
|
134
|
+
signals,
|
|
135
|
+
resolver: best.resolver.name,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=environment-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-resolver.js","sourceRoot":"","sources":["../../src/runtime/environment-resolver.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/home-dir.js
CHANGED
|
@@ -63,7 +63,7 @@ export function resolveHomeDir(opts = {}) {
|
|
|
63
63
|
if (legacyHasHarnessState(legacyPath)) {
|
|
64
64
|
if (!warnedLegacy) {
|
|
65
65
|
const stderr = opts.stderr ?? process.stderr;
|
|
66
|
-
stderr.write(`harness: state still under legacy ${legacyPath}/; run \`harness migrate-home\` to move it to ${newPath}/. The legacy fallback
|
|
66
|
+
stderr.write(`harness: state still under legacy ${legacyPath}/; run \`harness migrate-home\` to move it to ${newPath}/. The legacy fallback will be removed in a future release.\n`);
|
|
67
67
|
warnedLegacy = true;
|
|
68
68
|
}
|
|
69
69
|
return { path: legacyPath, source: "legacy" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home-dir.js","sourceRoot":"","sources":["../../src/runtime/home-dir.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,kEAAkE;AAClE,gEAAgE;AAChE,+DAA+D;AAC/D,gEAAgE;AAChE,+DAA+D;AAC/D,+DAA+D;AAC/D,oBAAoB;AACpB,EAAE;AACF,yBAAyB;AACzB,qEAAqE;AACrE,mDAAmD;AACnD,2CAA2C;AAC3C,oEAAoE;AACpE,kEAAkE;AAClE,yDAAyD;AACzD,wDAAwD;AACxD,EAAE;AACF,uEAAuE;AACvE,qEAAqE;AACrE,oEAAoE;AACpE,qEAAqE;AACrE,aAAa;AACb,EAAE;AACF,oEAAoE;AACpE,0DAA0D;AAC1D,+DAA+D;AAC/D,iEAAiE;AACjE,iEAAiE;AACjE,mCAAmC;AAEnC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAC/C,MAAM,CAAC,MAAM,2BAA2B,GAAG,SAAS,CAAC;AACrD,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAE/C;;;;;GAKG;AACH,IAAI,YAAY,GAAG,KAAK,CAAC;AAiCzB,MAAM,UAAU,cAAc,CAAC,OAA8B,EAAE;IAC7D,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;IAEpE,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;IAED,iEAAiE;IACjE,+DAA+D;IAC/D,mEAAmE;IACnE,iDAAiD;IACjD,IAAI,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;YAC7C,MAAM,CAAC,KAAK,CACV,qCAAqC,UAAU,iDAAiD,OAAO,
|
|
1
|
+
{"version":3,"file":"home-dir.js","sourceRoot":"","sources":["../../src/runtime/home-dir.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -3,4 +3,8 @@ export { recordPolicyDecision, payloadFromDecision, encodeLedgerContent, decodeL
|
|
|
3
3
|
export { resolveSessionId } from "./session-id.js";
|
|
4
4
|
export { buildAgentFacingBlock, formatAgentFacingMessage, renderAgentFacing, type AgentFacingBlock, } from "./agent-facing.js";
|
|
5
5
|
export { resolveGitContext, type GitRepoContext } from "./git-context.js";
|
|
6
|
+
export { buildActionEnvelope, type ActionEnvelope, type ActionEnvelopeRuntime, type ActionEnvelopeSession, type EnvelopeContext, } from "./action-envelope.js";
|
|
7
|
+
export { classifyRisk, type RiskConfidence, type RiskProfile, } from "./risk-classifier.js";
|
|
8
|
+
export { resolveKubeContext, type KubeContext, type ResolveKubeContextOptions, } from "./kube-context.js";
|
|
9
|
+
export { resolveEnvironment, type EnvironmentConfidence, type EnvironmentResolution, type SignalInputs, } from "./environment-resolver.js";
|
|
6
10
|
export { addLedgerFact, type AddLedgerFactOptions, type AddLedgerFactResult, } from "./ledger-add.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -3,5 +3,9 @@ export { recordPolicyDecision, payloadFromDecision, encodeLedgerContent, decodeL
|
|
|
3
3
|
export { resolveSessionId } from "./session-id.js";
|
|
4
4
|
export { buildAgentFacingBlock, formatAgentFacingMessage, renderAgentFacing, } from "./agent-facing.js";
|
|
5
5
|
export { resolveGitContext } from "./git-context.js";
|
|
6
|
+
export { buildActionEnvelope, } from "./action-envelope.js";
|
|
7
|
+
export { classifyRisk, } from "./risk-classifier.js";
|
|
8
|
+
export { resolveKubeContext, } from "./kube-context.js";
|
|
9
|
+
export { resolveEnvironment, } from "./environment-resolver.js";
|
|
6
10
|
export { addLedgerFact, } from "./ledger-add.js";
|
|
7
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAQV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,aAAa,GAGd,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAQV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,mBAAmB,GAKpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,YAAY,GAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,GAGnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,GAInB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,aAAa,GAGd,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface KubeContext {
|
|
2
|
+
/** Current context name, or "" when unresolved. */
|
|
3
|
+
context: string;
|
|
4
|
+
/** Namespace of the current context, or "" when unresolved. */
|
|
5
|
+
namespace: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ResolveKubeContextOptions {
|
|
8
|
+
/** Override the kubeconfig path (tests). Defaults to `~/.kube/config`. */
|
|
9
|
+
kubeconfigPath?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve `{ context, namespace }` from `~/.kube/config`. Returns empty
|
|
13
|
+
* strings when the file is absent, unparseable, or declares no
|
|
14
|
+
* `current-context`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveKubeContext(opts?: ResolveKubeContextOptions): KubeContext;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Resolves the current Kubernetes context + namespace from the
|
|
2
|
+
// standard `~/.kube/config`, for the Phase 7 #4 Context Resolver's
|
|
3
|
+
// `kube_context_patterns` / `kube_namespace_patterns` signals.
|
|
4
|
+
//
|
|
5
|
+
// Like `git-context.ts`, this is a deliberate filesystem approximation:
|
|
6
|
+
// it reads `~/.kube/config` directly and does NOT consult `$KUBECONFIG`
|
|
7
|
+
// file lists or in-cluster service-account state. For classifying a
|
|
8
|
+
// target environment those exotic setups are out of the MVP's scope.
|
|
9
|
+
// Every failure path returns empty strings, never throws — callers
|
|
10
|
+
// treat "" as "unknown".
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as os from "node:os";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { parse as parseYaml } from "yaml";
|
|
15
|
+
const EMPTY = { context: "", namespace: "" };
|
|
16
|
+
/**
|
|
17
|
+
* Resolve `{ context, namespace }` from `~/.kube/config`. Returns empty
|
|
18
|
+
* strings when the file is absent, unparseable, or declares no
|
|
19
|
+
* `current-context`.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveKubeContext(opts = {}) {
|
|
22
|
+
const configPath = opts.kubeconfigPath ?? path.join(os.homedir(), ".kube", "config");
|
|
23
|
+
let raw;
|
|
24
|
+
try {
|
|
25
|
+
raw = fs.readFileSync(configPath, "utf8");
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return EMPTY;
|
|
29
|
+
}
|
|
30
|
+
let doc;
|
|
31
|
+
try {
|
|
32
|
+
doc = parseYaml(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return EMPTY;
|
|
36
|
+
}
|
|
37
|
+
if (typeof doc !== "object" || doc === null)
|
|
38
|
+
return EMPTY;
|
|
39
|
+
const config = doc;
|
|
40
|
+
const context = typeof config["current-context"] === "string"
|
|
41
|
+
? config["current-context"]
|
|
42
|
+
: "";
|
|
43
|
+
if (context === "")
|
|
44
|
+
return EMPTY;
|
|
45
|
+
let namespace = "";
|
|
46
|
+
if (Array.isArray(config.contexts)) {
|
|
47
|
+
for (const entry of config.contexts) {
|
|
48
|
+
if (typeof entry !== "object" || entry === null)
|
|
49
|
+
continue;
|
|
50
|
+
const e = entry;
|
|
51
|
+
if (e.name !== context)
|
|
52
|
+
continue;
|
|
53
|
+
if (typeof e.context === "object" && e.context !== null) {
|
|
54
|
+
const ns = e.context.namespace;
|
|
55
|
+
if (typeof ns === "string")
|
|
56
|
+
namespace = ns;
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { context, namespace };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=kube-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kube-context.js","sourceRoot":"","sources":["../../src/runtime/kube-context.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,mEAAmE;AACnE,+DAA+D;AAC/D,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,oEAAoE;AACpE,qEAAqE;AACrE,mEAAmE;AACnE,yBAAyB;AAEzB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAS1C,MAAM,KAAK,GAAgB,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAO1D;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAkC,EAAE;IAEpC,MAAM,UAAU,GACd,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAEpE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAE1D,MAAM,MAAM,GAAG,GAA0D,CAAC;IAC1E,MAAM,OAAO,GACX,OAAO,MAAM,CAAC,iBAAiB,CAAC,KAAK,QAAQ;QAC3C,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC;IACT,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAEjC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;gBAAE,SAAS;YAC1D,MAAM,CAAC,GAAG,KAA8C,CAAC;YACzD,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;gBAAE,SAAS;YACjC,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBACxD,MAAM,EAAE,GAAI,CAAC,CAAC,OAAmC,CAAC,SAAS,CAAC;gBAC5D,IAAI,OAAO,EAAE,KAAK,QAAQ;oBAAE,SAAS,GAAG,EAAE,CAAC;YAC7C,CAAC;YACD,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RiskCategory, RiskClassifier, RiskSeverity } from "../schema/index.js";
|
|
2
|
+
import type { ActionEnvelope } from "./action-envelope.js";
|
|
3
|
+
export type RiskConfidence = "high" | "low";
|
|
4
|
+
export interface RiskProfile {
|
|
5
|
+
/** Did any classifier pattern match the action? */
|
|
6
|
+
classified: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Highest matched severity, or `null` when unclassified. `null` is
|
|
9
|
+
* NOT "low" — see the module header on "unknown is not safe".
|
|
10
|
+
*/
|
|
11
|
+
severity: RiskSeverity | null;
|
|
12
|
+
/** Union of every matched pattern's categories, sorted and deduplicated. */
|
|
13
|
+
categories: RiskCategory[];
|
|
14
|
+
/**
|
|
15
|
+
* `false` when a matched category marks the action irreversible,
|
|
16
|
+
* `true` when classified and nothing marks it irreversible, `null`
|
|
17
|
+
* when unclassified (reversibility is unknown, not assumed).
|
|
18
|
+
*/
|
|
19
|
+
reversible: boolean | null;
|
|
20
|
+
/**
|
|
21
|
+
* `high` for any deterministic rule match, `low` when unclassified.
|
|
22
|
+
* A regex classifier has no real probability; the field is a
|
|
23
|
+
* placeholder for the v2 LLM-assisted classifier, where a graded
|
|
24
|
+
* confidence becomes meaningful.
|
|
25
|
+
*/
|
|
26
|
+
confidence: RiskConfidence;
|
|
27
|
+
/** One human-readable line per matched pattern, or the no-match note. */
|
|
28
|
+
reasons: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Classify an Action Envelope against the manifest's risk classifiers.
|
|
32
|
+
*
|
|
33
|
+
* Pure: envelope + classifiers in, profile out, no I/O. Multiple
|
|
34
|
+
* matching patterns compose — highest severity wins, categories union,
|
|
35
|
+
* one `reasons` line per hit. An envelope no pattern matches yields the
|
|
36
|
+
* honest unclassified profile (`classified: false`, `severity: null`).
|
|
37
|
+
*/
|
|
38
|
+
export declare function classifyRisk(envelope: ActionEnvelope, classifiers: readonly RiskClassifier[]): RiskProfile;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Phase 7 #3 — Risk Classifier.
|
|
2
|
+
//
|
|
3
|
+
// Assigns an Action Envelope a risk profile by regex-matching the
|
|
4
|
+
// manifest's `risk.classifiers[]` against the action. The first Risk
|
|
5
|
+
// Gate stage that reads the `risk:` schema vocabulary shipped in
|
|
6
|
+
// Phase 7 #1.
|
|
7
|
+
//
|
|
8
|
+
// STATUS: invoked by `harness test-risk` (Phase 7 #3). NOT yet consumed
|
|
9
|
+
// by `harness policy intercept` — wiring the runtime through the
|
|
10
|
+
// classifier is Phase 7 #5. See docs/risk-gate.md and docs/ROADMAP.md.
|
|
11
|
+
//
|
|
12
|
+
// "Unknown is not safe": an envelope no pattern matches yields a
|
|
13
|
+
// profile with `classified: false` and `severity: null`, deliberately
|
|
14
|
+
// NOT a low/zero-risk profile. The Phase 7 #5 policy evaluator must
|
|
15
|
+
// treat an unclassified action as risk-bearing rather than allow it by
|
|
16
|
+
// default; this module's job is only to report the unclassified state
|
|
17
|
+
// honestly.
|
|
18
|
+
//
|
|
19
|
+
// Design source: lava-ice-logs/2026-04-30/harness-risk-gate-extension.md
|
|
20
|
+
// (design phase B).
|
|
21
|
+
import { RiskSeveritySchema } from "../schema/index.js";
|
|
22
|
+
import { expandToolNameAliases, extractShellCommand } from "./tool-name-aliases.js";
|
|
23
|
+
// Ordered severity scale: a value's index here is the comparison key
|
|
24
|
+
// for "highest matched severity wins". Sourced from the schema enum so
|
|
25
|
+
// a future reordering there flows through unchanged.
|
|
26
|
+
const SEVERITY_ORDER = RiskSeveritySchema.options;
|
|
27
|
+
// Categories that mean the action does not cleanly undo itself. When a
|
|
28
|
+
// matched pattern carries any of these the profile is `reversible:
|
|
29
|
+
// false`. `destructive` and `data_loss` are included alongside the
|
|
30
|
+
// explicit `irreversible_action`: a regex classifier cannot prove an
|
|
31
|
+
// action is safely undoable, and the Risk Gate exists to err toward
|
|
32
|
+
// caution. A genuinely destructive-but-reversible action simply should
|
|
33
|
+
// not be tagged `destructive` by its classifier author.
|
|
34
|
+
const IRREVERSIBLE_CATEGORIES = new Set(["irreversible_action", "data_loss", "destructive"]);
|
|
35
|
+
/**
|
|
36
|
+
* The string a classifier's patterns are regex-matched against. For a
|
|
37
|
+
* shell-class tool (or any tool whose input carries a `command` / `cmd`
|
|
38
|
+
* field) it is that command. For other tools it is the serialized raw
|
|
39
|
+
* input — blunt, but it keeps non-shell classifiers usable in the MVP.
|
|
40
|
+
*/
|
|
41
|
+
function subjectFor(envelope) {
|
|
42
|
+
const command = extractShellCommand({ raw_input: envelope.raw_input });
|
|
43
|
+
if (command !== null)
|
|
44
|
+
return command;
|
|
45
|
+
const raw = envelope.raw_input;
|
|
46
|
+
if (raw === null || raw === undefined)
|
|
47
|
+
return "";
|
|
48
|
+
if (typeof raw === "string")
|
|
49
|
+
return raw;
|
|
50
|
+
try {
|
|
51
|
+
return JSON.stringify(raw);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** A classifier applies when its `tool` is an alias of the envelope's tool. */
|
|
58
|
+
function classifierApplies(classifier, envelope) {
|
|
59
|
+
return expandToolNameAliases(envelope.tool).includes(classifier.tool);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Classify an Action Envelope against the manifest's risk classifiers.
|
|
63
|
+
*
|
|
64
|
+
* Pure: envelope + classifiers in, profile out, no I/O. Multiple
|
|
65
|
+
* matching patterns compose — highest severity wins, categories union,
|
|
66
|
+
* one `reasons` line per hit. An envelope no pattern matches yields the
|
|
67
|
+
* honest unclassified profile (`classified: false`, `severity: null`).
|
|
68
|
+
*/
|
|
69
|
+
export function classifyRisk(envelope, classifiers) {
|
|
70
|
+
const applicable = classifiers.filter((c) => classifierApplies(c, envelope));
|
|
71
|
+
const subject = subjectFor(envelope);
|
|
72
|
+
const categories = new Set();
|
|
73
|
+
const reasons = [];
|
|
74
|
+
let severityIdx = -1;
|
|
75
|
+
for (const classifier of applicable) {
|
|
76
|
+
for (const pat of classifier.patterns) {
|
|
77
|
+
let re;
|
|
78
|
+
try {
|
|
79
|
+
re = new RegExp(pat.pattern);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// The schema regex-validates patterns at parse time; this guard
|
|
83
|
+
// only covers a manifest that bypassed `harness validate`.
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (!re.test(subject))
|
|
87
|
+
continue;
|
|
88
|
+
for (const cat of pat.categories)
|
|
89
|
+
categories.add(cat);
|
|
90
|
+
const idx = SEVERITY_ORDER.indexOf(pat.severity);
|
|
91
|
+
if (idx > severityIdx)
|
|
92
|
+
severityIdx = idx;
|
|
93
|
+
reasons.push(`classifier "${classifier.name}" pattern /${pat.pattern}/ matched: ` +
|
|
94
|
+
`severity ${pat.severity}, categories [${pat.categories.join(", ")}]`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (severityIdx === -1) {
|
|
98
|
+
return {
|
|
99
|
+
classified: false,
|
|
100
|
+
severity: null,
|
|
101
|
+
categories: [],
|
|
102
|
+
reversible: null,
|
|
103
|
+
confidence: "low",
|
|
104
|
+
reasons: [
|
|
105
|
+
applicable.length === 0
|
|
106
|
+
? `no risk classifier is declared for tool "${envelope.tool}"`
|
|
107
|
+
: `no classifier pattern matched the action for tool "${envelope.tool}"`,
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const sortedCategories = [...categories].sort();
|
|
112
|
+
return {
|
|
113
|
+
classified: true,
|
|
114
|
+
severity: SEVERITY_ORDER[severityIdx],
|
|
115
|
+
categories: sortedCategories,
|
|
116
|
+
reversible: !sortedCategories.some((c) => IRREVERSIBLE_CATEGORIES.has(c)),
|
|
117
|
+
confidence: "high",
|
|
118
|
+
reasons,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=risk-classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk-classifier.js","sourceRoot":"","sources":["../../src/runtime/risk-classifier.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,kEAAkE;AAClE,qEAAqE;AACrE,iEAAiE;AACjE,cAAc;AACd,EAAE;AACF,wEAAwE;AACxE,iEAAiE;AACjE,uEAAuE;AACvE,EAAE;AACF,iEAAiE;AACjE,sEAAsE;AACtE,oEAAoE;AACpE,uEAAuE;AACvE,sEAAsE;AACtE,YAAY;AACZ,EAAE;AACF,yEAAyE;AACzE,oBAAoB;AAOpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEpF,qEAAqE;AACrE,uEAAuE;AACvE,qDAAqD;AACrD,MAAM,cAAc,GAA4B,kBAAkB,CAAC,OAAO,CAAC;AAE3E,uEAAuE;AACvE,mEAAmE;AACnE,mEAAmE;AACnE,qEAAqE;AACrE,oEAAoE;AACpE,uEAAuE;AACvE,wDAAwD;AACxD,MAAM,uBAAuB,GAA8B,IAAI,GAAG,CAChE,CAAC,qBAAqB,EAAE,WAAW,EAAE,aAAa,CAAC,CACpD,CAAC;AA+BF;;;;;GAKG;AACH,SAAS,UAAU,CAAC,QAAwB;IAC1C,MAAM,OAAO,GAAG,mBAAmB,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACvE,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;IAC/B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACjD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,iBAAiB,CACxB,UAA0B,EAC1B,QAAwB;IAExB,OAAO,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAwB,EACxB,WAAsC;IAEtC,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAErC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgB,CAAC;IAC3C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;IAErB,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;QACpC,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,EAAU,CAAC;YACf,IAAI,CAAC;gBACH,EAAE,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,gEAAgE;gBAChE,2DAA2D;gBAC3D,SAAS;YACX,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,SAAS;YAChC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU;gBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtD,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACjD,IAAI,GAAG,GAAG,WAAW;gBAAE,WAAW,GAAG,GAAG,CAAC;YACzC,OAAO,CAAC,IAAI,CACV,eAAe,UAAU,CAAC,IAAI,cAAc,GAAG,CAAC,OAAO,aAAa;gBAClE,YAAY,GAAG,CAAC,QAAQ,iBAAiB,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACxE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,KAAK;YACjB,OAAO,EAAE;gBACP,UAAU,CAAC,MAAM,KAAK,CAAC;oBACrB,CAAC,CAAC,4CAA4C,QAAQ,CAAC,IAAI,GAAG;oBAC9D,CAAC,CAAC,sDAAsD,QAAQ,CAAC,IAAI,GAAG;aAC3E;SACF,CAAC;IACJ,CAAC;IAED,MAAM,gBAAgB,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,OAAO;QACL,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,cAAc,CAAC,WAAW,CAAE;QACtC,UAAU,EAAE,gBAAgB;QAC5B,UAAU,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACzE,UAAU,EAAE,MAAM;QAClB,OAAO;KACR,CAAC;AACJ,CAAC"}
|