@lannguyensi/harness 0.62.0 → 0.64.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 +38 -0
- package/README.md +2 -2
- package/dist/cli/approve/understanding.d.ts +10 -1
- package/dist/cli/approve/understanding.js +19 -13
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/delegate/index.js +2 -1
- package/dist/cli/delegate/index.js.map +1 -1
- package/dist/cli/delegate/register.d.ts +5 -0
- package/dist/cli/delegate/register.js +90 -0
- package/dist/cli/delegate/register.js.map +1 -0
- package/dist/cli/doctor/expire-on-tool-match.d.ts +15 -0
- package/dist/cli/doctor/expire-on-tool-match.js +58 -0
- package/dist/cli/doctor/expire-on-tool-match.js.map +1 -0
- package/dist/cli/doctor/format.js +7 -0
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.js +8 -0
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +9 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/dry-run.d.ts +12 -0
- package/dist/cli/dry-run.js +60 -27
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/enriched-event.d.ts +35 -0
- package/dist/cli/enriched-event.js +60 -0
- package/dist/cli/enriched-event.js.map +1 -0
- package/dist/cli/explain-action.d.ts +6 -1
- package/dist/cli/explain-action.js +31 -1
- package/dist/cli/explain-action.js.map +1 -1
- package/dist/cli/explain-policy.d.ts +23 -0
- package/dist/cli/explain-policy.js +28 -8
- package/dist/cli/explain-policy.js.map +1 -1
- package/dist/cli/index.js +14 -484
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/profiles.js +9 -7
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/pack/auto-approve-path.js +13 -2
- package/dist/cli/pack/auto-approve-path.js.map +1 -1
- package/dist/cli/pack/hook-codex-pre-tool-use.js +4 -2
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +17 -4
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js +102 -1
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +1 -1
- package/dist/cli/policy/intercept.js +25 -90
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/policy/risk-envelope-enrichment.d.ts +40 -0
- package/dist/cli/policy/risk-envelope-enrichment.js +110 -0
- package/dist/cli/policy/risk-envelope-enrichment.js.map +1 -0
- package/dist/cli/register-audit-group.d.ts +5 -0
- package/dist/cli/register-audit-group.js +91 -0
- package/dist/cli/register-audit-group.js.map +1 -0
- package/dist/cli/register-operator-lifecycle.d.ts +5 -0
- package/dist/cli/register-operator-lifecycle.js +117 -0
- package/dist/cli/register-operator-lifecycle.js.map +1 -0
- package/dist/cli/remove/register.d.ts +5 -0
- package/dist/cli/remove/register.js +42 -0
- package/dist/cli/remove/register.js.map +1 -0
- package/dist/cli/resolve-env.js +12 -13
- package/dist/cli/resolve-env.js.map +1 -1
- package/dist/cli/test-risk.d.ts +2 -1
- package/dist/cli/test-risk.js +5 -1
- package/dist/cli/test-risk.js.map +1 -1
- package/dist/cli/uninstall/register.d.ts +5 -0
- package/dist/cli/uninstall/register.js +169 -0
- package/dist/cli/uninstall/register.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js +38 -13
- package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.d.ts +2 -2
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +26 -13
- package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +94 -21
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +221 -42
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +7 -3
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/intercept.d.ts +130 -1
- package/dist/runtime/intercept.js +85 -14
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/task-providers/agent-tasks.d.ts +11 -9
- package/dist/runtime/task-providers/agent-tasks.js +11 -9
- package/dist/runtime/task-providers/agent-tasks.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ActionEnvelope } from "../runtime/index.js";
|
|
2
|
+
import type { Manifest } from "../schema/index.js";
|
|
3
|
+
import type { LoadedEvent } from "./event-input.js";
|
|
4
|
+
import { type BashPrefixEnrichment } from "./policy/risk-envelope-enrichment.js";
|
|
5
|
+
/** Injectable env and kube seams (tests); ambient values otherwise. */
|
|
6
|
+
export interface EnrichmentSeams {
|
|
7
|
+
/** Env vars the inline assignments merge over; defaults to `process.env`. */
|
|
8
|
+
env?: Record<string, string | undefined>;
|
|
9
|
+
/** Kube context; when either kube seam is set `~/.kube/config` is not read. */
|
|
10
|
+
kubeContext?: string;
|
|
11
|
+
/** Kube namespace; see `kubeContext`. */
|
|
12
|
+
kubeNamespace?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface EnrichedEvent {
|
|
15
|
+
/** The envelope after the `cd` merge and the branch-switch upgrade. */
|
|
16
|
+
envelope: ActionEnvelope;
|
|
17
|
+
enrichment: BashPrefixEnrichment;
|
|
18
|
+
kube: {
|
|
19
|
+
context: string;
|
|
20
|
+
namespace: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Kube seams resolve together: if either is injected, skip the
|
|
25
|
+
* `~/.kube/config` read entirely (same contract `resolve-env` always had).
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveKubeSeams(seams: EnrichmentSeams): {
|
|
28
|
+
context: string;
|
|
29
|
+
namespace: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Run the hook's Bash-prefix enrichment over a loaded event and rebuild
|
|
33
|
+
* the envelope when the enrichment moved the git context.
|
|
34
|
+
*/
|
|
35
|
+
export declare function enrichLoadedEvent(loaded: LoadedEvent, manifest: Manifest, seams: EnrichmentSeams): EnrichedEvent;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Shared "enrich a loaded tool event like the hook does" front end for the
|
|
2
|
+
// Risk Gate debug verbs that read an event file and predict a verdict:
|
|
3
|
+
// `harness explain-policy`, `harness resolve-env`, `harness test-risk` and
|
|
4
|
+
// `harness explain-action` (task 8b891e83).
|
|
5
|
+
//
|
|
6
|
+
// `harness policy intercept` resolves its envelope and environment through
|
|
7
|
+
// `resolveBashPrefixEnrichment` (inline `VAR=value`, leading `cd`, leading
|
|
8
|
+
// `git switch|checkout`). Every verb that predicts the gate must read the
|
|
9
|
+
// same enriched inputs, or an operator's prediction disagrees with the
|
|
10
|
+
// gate (`DATABASE_URL=...prod... psql ...` resolved `unknown` in the debug
|
|
11
|
+
// verbs and `production` in the hook). This module is the one place the
|
|
12
|
+
// verbs call that helper; it reads no ledger or evidence and imports
|
|
13
|
+
// nothing from `policies/`.
|
|
14
|
+
import { buildActionEnvelope, resolveKubeContext, } from "../runtime/index.js";
|
|
15
|
+
import { resolveBashPrefixEnrichment, } from "./policy/risk-envelope-enrichment.js";
|
|
16
|
+
/**
|
|
17
|
+
* Kube seams resolve together: if either is injected, skip the
|
|
18
|
+
* `~/.kube/config` read entirely (same contract `resolve-env` always had).
|
|
19
|
+
*/
|
|
20
|
+
export function resolveKubeSeams(seams) {
|
|
21
|
+
return seams.kubeContext !== undefined || seams.kubeNamespace !== undefined
|
|
22
|
+
? { context: seams.kubeContext ?? "", namespace: seams.kubeNamespace ?? "" }
|
|
23
|
+
: resolveKubeContext();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run the hook's Bash-prefix enrichment over a loaded event and rebuild
|
|
27
|
+
* the envelope when the enrichment moved the git context.
|
|
28
|
+
*/
|
|
29
|
+
export function enrichLoadedEvent(loaded, manifest, seams) {
|
|
30
|
+
const { event, envelope: baseEnvelope } = loaded;
|
|
31
|
+
const kube = resolveKubeSeams(seams);
|
|
32
|
+
const enrichment = resolveBashPrefixEnrichment({
|
|
33
|
+
event,
|
|
34
|
+
manifest,
|
|
35
|
+
cwd: baseEnvelope.runtime.cwd,
|
|
36
|
+
cwdGitContext: {
|
|
37
|
+
repo: baseEnvelope.session.repo,
|
|
38
|
+
branch: baseEnvelope.session.branch,
|
|
39
|
+
sha: "",
|
|
40
|
+
},
|
|
41
|
+
env: seams.env ?? process.env,
|
|
42
|
+
kubeContext: kube.context,
|
|
43
|
+
kubeNamespace: kube.namespace,
|
|
44
|
+
user: baseEnvelope.runtime.user,
|
|
45
|
+
host: baseEnvelope.runtime.host,
|
|
46
|
+
now: new Date(baseEnvelope.timestamp),
|
|
47
|
+
});
|
|
48
|
+
const envelope = enrichment.git.repo === baseEnvelope.session.repo &&
|
|
49
|
+
enrichment.git.branch === baseEnvelope.session.branch
|
|
50
|
+
? baseEnvelope
|
|
51
|
+
: buildActionEnvelope(event, {
|
|
52
|
+
cwd: baseEnvelope.runtime.cwd,
|
|
53
|
+
git: enrichment.git,
|
|
54
|
+
user: baseEnvelope.runtime.user,
|
|
55
|
+
host: baseEnvelope.runtime.host,
|
|
56
|
+
now: new Date(baseEnvelope.timestamp),
|
|
57
|
+
});
|
|
58
|
+
return { envelope, enrichment, kube };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=enriched-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enriched-event.js","sourceRoot":"","sources":["../../src/cli/enriched-event.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,uEAAuE;AACvE,2EAA2E;AAC3E,4CAA4C;AAC5C,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,uEAAuE;AACvE,2EAA2E;AAC3E,wEAAwE;AACxE,qEAAqE;AACrE,4BAA4B;AAE5B,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GAEnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,2BAA2B,GAE5B,MAAM,sCAAsC,CAAC;AAmB9C;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,OAAO,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;QACzE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE;QAC5E,CAAC,CAAC,kBAAkB,EAAE,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAmB,EACnB,QAAkB,EAClB,KAAsB;IAEtB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IACjD,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,2BAA2B,CAAC;QAC7C,KAAK;QACL,QAAQ;QACR,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG;QAC7B,aAAa,EAAE;YACb,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI;YAC/B,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM;YACnC,GAAG,EAAE,EAAE;SACR;QACD,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG;QAC7B,WAAW,EAAE,IAAI,CAAC,OAAO;QACzB,aAAa,EAAE,IAAI,CAAC,SAAS;QAC7B,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI;QAC/B,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI;QAC/B,GAAG,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;KACtC,CAAC,CAAC;IACH,MAAM,QAAQ,GACZ,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,OAAO,CAAC,IAAI;QACjD,UAAU,CAAC,GAAG,CAAC,MAAM,KAAK,YAAY,CAAC,OAAO,CAAC,MAAM;QACnD,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE;YACzB,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG;YAC7B,GAAG,EAAE,UAAU,CAAC,GAAG;YACnB,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI;YAC/B,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI;YAC/B,GAAG,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;SACtC,CAAC,CAAC;IACT,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { ActionEnvelope } from "../runtime/index.js";
|
|
2
|
+
import { type Manifest } from "../schema/index.js";
|
|
3
|
+
import { type EnrichmentSeams } from "./enriched-event.js";
|
|
2
4
|
import { type EventInputSeams } from "./event-input.js";
|
|
3
|
-
|
|
5
|
+
import { type LoaderOptions } from "./loader.js";
|
|
6
|
+
export interface ExplainActionOptions extends EventInputSeams, EnrichmentSeams, LoaderOptions {
|
|
7
|
+
/** Inject the resolved manifest (tests); bypasses `loadManifest`. */
|
|
8
|
+
manifest?: Manifest;
|
|
4
9
|
/** Path to the tool-event JSON file. */
|
|
5
10
|
eventPath: string;
|
|
6
11
|
/** Emit JSON instead of YAML. */
|
|
@@ -8,8 +8,30 @@
|
|
|
8
8
|
//
|
|
9
9
|
// File read, JSON guards, and envelope build live in the shared
|
|
10
10
|
// `event-input` front end; this module only renders the result.
|
|
11
|
+
import * as fs from "node:fs";
|
|
11
12
|
import { stringify as stringifyYaml } from "yaml";
|
|
13
|
+
import { parseBashPrefix } from "../runtime/bash-prefix-parse.js";
|
|
14
|
+
import { parseManifest } from "../schema/index.js";
|
|
15
|
+
import { enrichLoadedEvent } from "./enriched-event.js";
|
|
12
16
|
import { loadEventEnvelope } from "./event-input.js";
|
|
17
|
+
import { loadManifest, resolvePaths } from "./loader.js";
|
|
18
|
+
import { readBashCommand } from "./policy/risk-envelope-enrichment.js";
|
|
19
|
+
function manifestFor(event, opts) {
|
|
20
|
+
if (opts.manifest)
|
|
21
|
+
return opts.manifest;
|
|
22
|
+
const command = event.tool_name === "Bash" ? readBashCommand(event.tool_input) : null;
|
|
23
|
+
const branchTarget = command === null ? null : parseBashPrefix(command).branchTarget;
|
|
24
|
+
if (branchTarget === null)
|
|
25
|
+
return parseManifest({ version: 1 });
|
|
26
|
+
// With no `--config` and no manifest at the default location there are no
|
|
27
|
+
// resolvers, so the branch-switch upgrade is a no-op: use the empty
|
|
28
|
+
// manifest, as for any other command. An explicit `--config` that is
|
|
29
|
+
// missing, and a manifest that is invalid, still fail loudly.
|
|
30
|
+
if (opts.configPath === undefined && !fs.existsSync(resolvePaths(opts).base)) {
|
|
31
|
+
return parseManifest({ version: 1 });
|
|
32
|
+
}
|
|
33
|
+
return loadManifest(opts).manifest;
|
|
34
|
+
}
|
|
13
35
|
/**
|
|
14
36
|
* Build and render the Action Envelope for a tool-event JSON file.
|
|
15
37
|
*
|
|
@@ -18,7 +40,15 @@ import { loadEventEnvelope } from "./event-input.js";
|
|
|
18
40
|
* `loadEventEnvelope`). A well-formed but sparse event is accepted.
|
|
19
41
|
*/
|
|
20
42
|
export function explainAction(opts) {
|
|
21
|
-
const
|
|
43
|
+
const loaded = loadEventEnvelope(opts.eventPath, opts, "explain-action");
|
|
44
|
+
// Print the envelope the hook builds: after its Bash-prefix enrichment
|
|
45
|
+
// (leading `cd`, `git switch|checkout`; task 8b891e83). The manifest is
|
|
46
|
+
// consulted only by the branch-switch upgrade (it compares what the
|
|
47
|
+
// resolvers say for the two branches), so it is loaded only for a
|
|
48
|
+
// command with a leading `git switch|checkout`; every other event, and a
|
|
49
|
+
// machine with no manifest at the default location, keeps working with
|
|
50
|
+
// the empty manifest, as before.
|
|
51
|
+
const { envelope } = enrichLoadedEvent(loaded, manifestFor(loaded.event, opts), opts);
|
|
22
52
|
const output = opts.json
|
|
23
53
|
? `${JSON.stringify(envelope, null, 2)}\n`
|
|
24
54
|
: stringifyYaml(envelope, { lineWidth: 0 });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain-action.js","sourceRoot":"","sources":["../../src/cli/explain-action.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,kEAAkE;AAClE,gEAAgE;AAChE,kEAAkE;AAClE,kEAAkE;AAClE,4CAA4C;AAC5C,EAAE;AACF,gEAAgE;AAChE,gEAAgE;AAEhE,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAwB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"explain-action.js","sourceRoot":"","sources":["../../src/cli/explain-action.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,kEAAkE;AAClE,gEAAgE;AAChE,kEAAkE;AAClE,kEAAkE;AAClE,4CAA4C;AAC5C,EAAE;AACF,gEAAgE;AAChE,gEAAgE;AAEhE,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,aAAa,EAAiB,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAwB,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAwB,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAgBvE,SAAS,WAAW,CAAC,KAAgB,EAAE,IAA0B;IAC/D,IAAI,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtF,MAAM,YAAY,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC;IACrF,IAAI,YAAY,KAAK,IAAI;QAAE,OAAO,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAChE,0EAA0E;IAC1E,oEAAoE;IACpE,qEAAqE;IACrE,8DAA8D;IAC9D,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7E,OAAO,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAA0B;IACtD,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,oEAAoE;IACpE,kEAAkE;IAClE,yEAAyE;IACzE,uEAAuE;IACvE,iCAAiC;IACjC,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI;QACtB,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI;QAC1C,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -65,6 +65,29 @@ interface ExplainPolicyProjection {
|
|
|
65
65
|
};
|
|
66
66
|
classifier: RiskProfile;
|
|
67
67
|
environment: EnvironmentResolution;
|
|
68
|
+
/**
|
|
69
|
+
* Which parts of the hook's evaluation this verb mirrors and which it
|
|
70
|
+
* deliberately does not (task 7c3919a2). `envelope_enrichment` lists the
|
|
71
|
+
* Bash-prefix merges shared with `harness policy intercept`;
|
|
72
|
+
* `not_evaluated` lists what the hook does that this hypothetical,
|
|
73
|
+
* read-free verb does not.
|
|
74
|
+
*/
|
|
75
|
+
parity: {
|
|
76
|
+
envelope_enrichment: string[];
|
|
77
|
+
not_evaluated: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Per-event (task 8b891e83): true when the Bash prefix remainder
|
|
80
|
+
* (what follows a leading `cd`/`VAR=value`/`git switch` prefix) is a
|
|
81
|
+
* `kubectl` command carrying an explicit `--context`/`--namespace`/
|
|
82
|
+
* `-n`. The hook merges that target into its environment resolution
|
|
83
|
+
* upgrade-only; this verb never does (`not_evaluated` lists
|
|
84
|
+
* `kubectl_target` statically), so `true` tells the operator the
|
|
85
|
+
* `applies` verdict below was computed WITHOUT a merge the hook
|
|
86
|
+
* would perform for this very event. `false` means no such target
|
|
87
|
+
* was found, i.e. the skipped merge would have been a no-op.
|
|
88
|
+
*/
|
|
89
|
+
kubectl_target_present: boolean;
|
|
90
|
+
};
|
|
68
91
|
/** Static deletion-target verdict (task d03af8f6); null when the
|
|
69
92
|
* event's command is not a recognized deletion verb. */
|
|
70
93
|
deletion_target: DeletionTargetVerdict | null;
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
// evaluates a hypothetical event live and reads nothing from the ledger.
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
17
|
-
import { classifyRisk, deriveProjectName, evaluateWhen, policyMatchesEvent, resolveDeletionTarget, resolveEnvironment,
|
|
17
|
+
import { classifyRisk, deriveProjectName, evaluateWhen, policyMatchesEvent, resolveDeletionTarget, resolveEnvironment, } from "../runtime/index.js";
|
|
18
|
+
import { parseKubectlTarget } from "../runtime/kubectl-target-parse.js";
|
|
18
19
|
import { extractShellCommand } from "../runtime/tool-name-aliases.js";
|
|
19
20
|
import { DEFAULT_SAFE_DELETION_ROOTS } from "../schema/risk.js";
|
|
21
|
+
import { enrichLoadedEvent } from "./enriched-event.js";
|
|
20
22
|
import { loadEventEnvelope } from "./event-input.js";
|
|
21
23
|
import { EX_USAGE, HarnessExitError } from "./exit-codes.js";
|
|
22
24
|
import { loadManifest } from "./loader.js";
|
|
@@ -164,15 +166,21 @@ export function explainPolicy(policyName, opts) {
|
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
const loaded = loadEventEnvelope(opts.eventPath, opts, "explain-policy");
|
|
170
|
+
const { event } = loaded;
|
|
171
|
+
// Envelope enrichment shared with `harness policy intercept` (task
|
|
172
|
+
// 7c3919a2): the leading Bash prefix (inline `VAR=value`, `cd <path>
|
|
173
|
+
// &&`, `git switch|checkout <branch> &&`) is parsed and merged into
|
|
174
|
+
// the resolver inputs by the SAME helper the hook uses (through
|
|
175
|
+
// `enrichLoadedEvent`, shared with `resolve-env`, `test-risk` and
|
|
176
|
+
// `explain-action`, task 8b891e83), so this verb cannot report
|
|
177
|
+
// `unknown` for a command the hook resolves to `production`. Only the
|
|
178
|
+
// envelope enrichment is shared; this verb still reads no ledger or
|
|
179
|
+
// evidence.
|
|
180
|
+
const { envelope, enrichment, kube } = enrichLoadedEvent(loaded, manifest, opts);
|
|
173
181
|
const classifier = classifyRisk(envelope, manifest.risk.classifiers);
|
|
174
182
|
const environment = resolveEnvironment(envelope, manifest.environments.resolvers, {
|
|
175
|
-
env:
|
|
183
|
+
env: enrichment.env,
|
|
176
184
|
kubeContext: kube.context,
|
|
177
185
|
kubeNamespace: kube.namespace,
|
|
178
186
|
});
|
|
@@ -187,6 +195,13 @@ export function explainPolicy(policyName, opts) {
|
|
|
187
195
|
const whenEval = policy.when !== undefined
|
|
188
196
|
? evaluateWhen(policy.when, { risk: classifier, environment, deletionTarget })
|
|
189
197
|
: undefined;
|
|
198
|
+
// Same remainder the hook feeds `parseKubectlTarget` (after the
|
|
199
|
+
// prefix consumed `cd`/`VAR=value`/`git switch`); flag only, no merge.
|
|
200
|
+
const kubectlSubject = enrichment.riskBashCommand === null
|
|
201
|
+
? null
|
|
202
|
+
: enrichment.riskBashCommand.slice(enrichment.bashPrefix?.remainderStart ?? 0);
|
|
203
|
+
const kubectlTarget = kubectlSubject === null ? null : parseKubectlTarget(kubectlSubject);
|
|
204
|
+
const kubectlTargetPresent = kubectlTarget !== null && (kubectlTarget.context !== null || kubectlTarget.namespace !== null);
|
|
190
205
|
const projection = {
|
|
191
206
|
policy: policy.name,
|
|
192
207
|
description: policy.description,
|
|
@@ -203,6 +218,11 @@ export function explainPolicy(policyName, opts) {
|
|
|
203
218
|
},
|
|
204
219
|
classifier,
|
|
205
220
|
environment,
|
|
221
|
+
parity: {
|
|
222
|
+
envelope_enrichment: ["inline_env", "cd_git_context", "branch_switch_upgrade"],
|
|
223
|
+
not_evaluated: ["ledger_requires", "kubectl_target"],
|
|
224
|
+
kubectl_target_present: kubectlTargetPresent,
|
|
225
|
+
},
|
|
206
226
|
deletion_target: deletionTarget,
|
|
207
227
|
...(policy.name.startsWith("preflight-before-") && {
|
|
208
228
|
session_start_preflight: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain-policy.js","sourceRoot":"","sources":["../../src/cli/explain-policy.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,wEAAwE;AACxE,mEAAmE;AACnE,wEAAwE;AACxE,wEAAwE;AACxE,qEAAqE;AACrE,yEAAyE;AACzE,uEAAuE;AACvE,6CAA6C;AAC7C,EAAE;AACF,qEAAqE;AACrE,oEAAoE;AACpE,yEAAyE;AAEzE,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AACtE,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"explain-policy.js","sourceRoot":"","sources":["../../src/cli/explain-policy.ts"],"names":[],"mappings":"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"}
|