@fabricorg/policy-opa 0.2.0 → 0.3.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 +9 -0
- package/README.md +9 -6
- package/dist/index.cjs +79 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +79 -3
- package/dist/index.js.map +1 -1
- package/package.json +71 -70
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @fabricorg/policy-opa
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — 2026-05-17
|
|
4
|
+
|
|
5
|
+
### Minor
|
|
6
|
+
|
|
7
|
+
- **Policy guidance support.** `OpaDecision` now accepts `guidance?: PolicyGuidance` and the evaluator maps it directly to `PolicyOutcome.guidance`. This makes OPA-backed policies render through the same audit/remediation UI contract as TypeScript code policies.
|
|
8
|
+
- **Engine metadata cleanup.** User-facing facts move out of `dispatchEvidence.engine.metadata` and into `PolicyOutcome.guidance`. Engine metadata remains focused on OPA provenance (`decisionPath`, `inputHash`, `decisionId`, `bundleRevision`, `conditionResults`, `obligations`, raw `provenance`).
|
|
9
|
+
- **Default-deny failure guidance.** OPA infrastructure failures still return `block` by default, now with generic corrective guidance telling operators to check OPA reachability, bundle serving, and decision shape.
|
|
10
|
+
- **Peer-deps:** `@fabricorg/platform ^0.4.0`.
|
|
11
|
+
|
|
3
12
|
## 0.2.0 — 2026-05-16
|
|
4
13
|
|
|
5
14
|
### Minor
|
package/README.md
CHANGED
|
@@ -78,14 +78,18 @@ decision := {
|
|
|
78
78
|
"result": result,
|
|
79
79
|
"reason": reason,
|
|
80
80
|
"conditionResults": condition_results,
|
|
81
|
-
"
|
|
81
|
+
"guidance": {
|
|
82
|
+
"summary": reason,
|
|
83
|
+
"factsUsed": facts_used,
|
|
84
|
+
"correctiveActions": corrective_actions,
|
|
85
|
+
},
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
result := "block" if not consent_active
|
|
85
89
|
result := "block" if not within_window
|
|
86
90
|
default result := "pass"
|
|
87
91
|
|
|
88
|
-
# ...rules computing reason, condition_results, facts_used...
|
|
92
|
+
# ...rules computing reason, condition_results, facts_used, corrective_actions...
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
The validator accepts the following fields (TypeScript types: `OpaDecision`):
|
|
@@ -100,7 +104,7 @@ The validator accepts the following fields (TypeScript types: `OpaDecision`):
|
|
|
100
104
|
result: "pass" | "warn" | "block";
|
|
101
105
|
reason?: string;
|
|
102
106
|
}>;
|
|
103
|
-
|
|
107
|
+
guidance?: PolicyGuidance;
|
|
104
108
|
obligations?: Record<string, unknown>;
|
|
105
109
|
}
|
|
106
110
|
```
|
|
@@ -179,13 +183,12 @@ For a successful OPA evaluation under the HTTP executor (with provenance enabled
|
|
|
179
183
|
"version": "0.65.0",
|
|
180
184
|
"bundles": { "lending": { "revision": "2026-05-16.3" } }
|
|
181
185
|
},
|
|
182
|
-
"conditionResults": [{ "conditionId": "consent_active", "passed": true, "result": "pass" }]
|
|
183
|
-
"factsUsed": { "consent": { "active": true } }
|
|
186
|
+
"conditionResults": [{ "conditionId": "consent_active", "passed": true, "result": "pass" }]
|
|
184
187
|
}
|
|
185
188
|
}
|
|
186
189
|
```
|
|
187
190
|
|
|
188
|
-
Fabric's `policyId` / `policyVersion` remain canonical (`"what was evaluated"`). `engine.metadata.{decisionId, bundleName, bundleRevision}` answers `"which artifact produced the answer"`, and `decisionId` is the join key against OPA's own decision-log stream if you ship it to a SIEM.
|
|
191
|
+
Fabric's `policyId` / `policyVersion` remain canonical (`"what was evaluated"`). `engine.metadata.{decisionId, bundleName, bundleRevision}` answers `"which artifact produced the answer"`, and `decisionId` is the join key against OPA's own decision-log stream if you ship it to a SIEM. User-facing facts and remediation steps live on `PolicyOutcome.guidance`, not in engine metadata, so code policies and OPA policies render through the same audit UI contract.
|
|
189
192
|
|
|
190
193
|
If OPA serves multiple bundles and the binding has no `bundleName` selector, `bundleRevision` is omitted and `bundleRevisionStatus: "ambiguous"` is recorded — audit dashboards can flag these for a manual fix.
|
|
191
194
|
|
package/dist/index.cjs
CHANGED
|
@@ -54,8 +54,9 @@ function parseOpaDecision(raw) {
|
|
|
54
54
|
if (err) return { ok: false, error: err };
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
if (d.
|
|
58
|
-
|
|
57
|
+
if (d.guidance !== void 0) {
|
|
58
|
+
const err = validateGuidance(d.guidance);
|
|
59
|
+
if (err) return { ok: false, error: err };
|
|
59
60
|
}
|
|
60
61
|
if (d.obligations !== void 0 && !isPlainObject(d.obligations)) {
|
|
61
62
|
return { ok: false, error: "obligations must be an object when present" };
|
|
@@ -81,6 +82,68 @@ function validateConditionResult(value, index) {
|
|
|
81
82
|
}
|
|
82
83
|
return null;
|
|
83
84
|
}
|
|
85
|
+
function validateGuidance(value) {
|
|
86
|
+
if (!isPlainObject(value)) {
|
|
87
|
+
return "guidance must be an object when present";
|
|
88
|
+
}
|
|
89
|
+
const guidance = value;
|
|
90
|
+
if (guidance.summary !== void 0 && typeof guidance.summary !== "string") {
|
|
91
|
+
return "guidance.summary must be a string when present";
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(guidance.factsUsed)) {
|
|
94
|
+
return "guidance.factsUsed must be an array";
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < guidance.factsUsed.length; i += 1) {
|
|
97
|
+
const err = validatePolicyFact(guidance.factsUsed[i], i);
|
|
98
|
+
if (err) return err;
|
|
99
|
+
}
|
|
100
|
+
if (!Array.isArray(guidance.correctiveActions)) {
|
|
101
|
+
return "guidance.correctiveActions must be an array";
|
|
102
|
+
}
|
|
103
|
+
for (let i = 0; i < guidance.correctiveActions.length; i += 1) {
|
|
104
|
+
const err = validateCorrectiveAction(guidance.correctiveActions[i], i);
|
|
105
|
+
if (err) return err;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function validatePolicyFact(value, index) {
|
|
110
|
+
if (!isPlainObject(value)) {
|
|
111
|
+
return `guidance.factsUsed[${index}] must be an object`;
|
|
112
|
+
}
|
|
113
|
+
const fact = value;
|
|
114
|
+
if (typeof fact.name !== "string" || fact.name.length === 0) {
|
|
115
|
+
return `guidance.factsUsed[${index}].name must be a non-empty string`;
|
|
116
|
+
}
|
|
117
|
+
if (fact.label !== void 0 && typeof fact.label !== "string") {
|
|
118
|
+
return `guidance.factsUsed[${index}].label must be a string when present`;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function validateCorrectiveAction(value, index) {
|
|
123
|
+
if (!isPlainObject(value)) {
|
|
124
|
+
return `guidance.correctiveActions[${index}] must be an object`;
|
|
125
|
+
}
|
|
126
|
+
const action = value;
|
|
127
|
+
if (typeof action.label !== "string" || action.label.length === 0) {
|
|
128
|
+
return `guidance.correctiveActions[${index}].label must be a non-empty string`;
|
|
129
|
+
}
|
|
130
|
+
if (action.description !== void 0 && typeof action.description !== "string") {
|
|
131
|
+
return `guidance.correctiveActions[${index}].description must be a string when present`;
|
|
132
|
+
}
|
|
133
|
+
if (action.actionId !== void 0 && typeof action.actionId !== "string") {
|
|
134
|
+
return `guidance.correctiveActions[${index}].actionId must be a string when present`;
|
|
135
|
+
}
|
|
136
|
+
if (action.parameters !== void 0 && !isPlainObject(action.parameters)) {
|
|
137
|
+
return `guidance.correctiveActions[${index}].parameters must be an object when present`;
|
|
138
|
+
}
|
|
139
|
+
if (action.target !== void 0 && !isPlainObject(action.target)) {
|
|
140
|
+
return `guidance.correctiveActions[${index}].target must be an object when present`;
|
|
141
|
+
}
|
|
142
|
+
if (action.requiresConfirmation !== void 0 && typeof action.requiresConfirmation !== "boolean") {
|
|
143
|
+
return `guidance.correctiveActions[${index}].requiresConfirmation must be a boolean when present`;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
84
147
|
function isPlainObject(value) {
|
|
85
148
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
86
149
|
}
|
|
@@ -205,6 +268,7 @@ function buildSuccessOutcome(binding, engineVersion, decision, execution, inputH
|
|
|
205
268
|
policyVersion: binding.version,
|
|
206
269
|
result: mapped.result ?? decision.result,
|
|
207
270
|
reason: mapped.reason ?? decision.reason,
|
|
271
|
+
guidance: mapped.guidance ?? decision.guidance,
|
|
208
272
|
metadata: {
|
|
209
273
|
...mapped.metadata ?? {},
|
|
210
274
|
opaDecision: decision
|
|
@@ -227,7 +291,6 @@ function buildSuccessOutcome(binding, engineVersion, decision, execution, inputH
|
|
|
227
291
|
bundleRevisionStatus: selection.bundleRevisionStatus,
|
|
228
292
|
provenance: execution.provenance,
|
|
229
293
|
conditionResults: decision.conditionResults,
|
|
230
|
-
factsUsed: decision.factsUsed,
|
|
231
294
|
obligations: decision.obligations
|
|
232
295
|
})
|
|
233
296
|
}
|
|
@@ -246,6 +309,19 @@ function makeFailureOutcome(binding, engineVersion, error, reason, onError, inpu
|
|
|
246
309
|
policyVersion: binding.version,
|
|
247
310
|
result: "block",
|
|
248
311
|
reason,
|
|
312
|
+
guidance: {
|
|
313
|
+
summary: "OPA policy evaluation could not complete.",
|
|
314
|
+
factsUsed: [
|
|
315
|
+
{ name: "opaFailureKind", value: error, label: "Failure kind" },
|
|
316
|
+
{ name: "decisionPath", value: binding.decisionPath, label: "Decision path" }
|
|
317
|
+
],
|
|
318
|
+
correctiveActions: [
|
|
319
|
+
{
|
|
320
|
+
label: "Check OPA policy engine",
|
|
321
|
+
description: "Verify the OPA service is reachable, serving the expected bundle, and returning the Fabric OPA decision shape."
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
},
|
|
249
325
|
metadata: {
|
|
250
326
|
opaError: error
|
|
251
327
|
},
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bundle-selector.ts","../src/decision-schema.ts","../src/hash.ts","../src/factory.ts","../src/executor-client.ts","../src/executor-http.ts"],"names":["createHash"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { OpaPolicyBinding, OpaPolicyExecution } from \"./types\";\n\nexport interface BundleSelection {\n\tbundleRevision?: string;\n\t/**\n\t * Set to `\"ambiguous\"` when OPA reports multiple bundles and the binding\n\t * did not pick one with `bundleName`. The adapter records this on the\n\t * evidence so dashboards can flag policies that need explicit selectors.\n\t * `\"missing\"` is set when provenance was expected but absent.\n\t */\n\tbundleRevisionStatus?: \"ambiguous\" | \"missing\";\n}\n\n/**\n * Determine which OPA bundle revision applies to this decision.\n *\n * Rules (in order):\n * 1. If the executor already populated `execution.bundleRevision`, use it.\n * 2. If `binding.bundleName` is set, look it up in `provenance.bundles`.\n * 3. If exactly one bundle is reported, use its revision unambiguously.\n * 4. If multiple bundles are reported and no selector exists, mark\n * `bundleRevisionStatus: \"ambiguous\"`.\n * 5. If no provenance is reported at all, return `{}` — the caller decides\n * whether absence is meaningful (it is not, for executors that don't\n * carry provenance).\n */\nexport function selectBundleRevision(\n\texecution: OpaPolicyExecution,\n\tbinding: OpaPolicyBinding,\n): BundleSelection {\n\tif (typeof execution.bundleRevision === \"string\" && execution.bundleRevision.length > 0) {\n\t\treturn { bundleRevision: execution.bundleRevision };\n\t}\n\n\tconst bundles = readBundles(execution.provenance);\n\tif (!bundles) return {};\n\n\tconst names = Object.keys(bundles);\n\tif (names.length === 0) return {};\n\n\tif (binding.bundleName) {\n\t\tconst revision = bundles[binding.bundleName]?.revision;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\tif (names.length === 1) {\n\t\tconst only = names[0];\n\t\t// names.length === 1 guarantees names[0] is defined, but the\n\t\t// non-null assertion satisfies noUncheckedIndexedAccess if enabled.\n\t\tconst revision = only !== undefined ? bundles[only]?.revision : undefined;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\treturn { bundleRevisionStatus: \"ambiguous\" };\n}\n\nfunction readBundles(\n\tprovenance: Record<string, unknown> | undefined,\n): Record<string, { revision?: unknown }> | undefined {\n\tif (!provenance || typeof provenance !== \"object\") return undefined;\n\tconst b = (provenance as { bundles?: unknown }).bundles;\n\tif (!b || typeof b !== \"object\" || Array.isArray(b)) return undefined;\n\treturn b as Record<string, { revision?: unknown }>;\n}\n","import type { PolicyEvaluationResult } from \"@fabricorg/platform/policies\";\nimport type { OpaDecision } from \"./types\";\n\nconst POLICY_RESULTS: readonly PolicyEvaluationResult[] = [\"pass\", \"warn\", \"block\"];\n\nexport type OpaDecisionParseResult =\n\t| { ok: true; decision: OpaDecision }\n\t| { ok: false; error: string };\n\n/**\n * Hand-rolled validator for the Rego \"house style\" decision shape. Avoids a\n * runtime zod dependency to keep the adapter zero-dep.\n *\n * Accepts a raw OPA result and either returns the parsed `OpaDecision` or a\n * single human-readable error string describing the first violation. We don't\n * accumulate every error: when audit evidence has to record \"OPA returned\n * something invalid\", one precise reason is more useful than a list.\n */\nexport function parseOpaDecision(raw: unknown): OpaDecisionParseResult {\n\tif (raw === null || typeof raw !== \"object\") {\n\t\treturn { ok: false, error: \"decision must be an object\" };\n\t}\n\n\tconst d = raw as Record<string, unknown>;\n\n\tif (typeof d.result !== \"string\" || !POLICY_RESULTS.includes(d.result as PolicyEvaluationResult)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `result must be \"pass\" | \"warn\" | \"block\" (got ${JSON.stringify(d.result)})`,\n\t\t};\n\t}\n\n\tif (d.reason !== undefined && typeof d.reason !== \"string\") {\n\t\treturn { ok: false, error: \"reason must be a string when present\" };\n\t}\n\n\tif (d.conditionResults !== undefined) {\n\t\tif (!Array.isArray(d.conditionResults)) {\n\t\t\treturn { ok: false, error: \"conditionResults must be an array when present\" };\n\t\t}\n\t\tfor (let i = 0; i < d.conditionResults.length; i += 1) {\n\t\t\tconst err = validateConditionResult(d.conditionResults[i], i);\n\t\t\tif (err) return { ok: false, error: err };\n\t\t}\n\t}\n\n\tif (d.factsUsed !== undefined && !isPlainObject(d.factsUsed)) {\n\t\treturn { ok: false, error: \"factsUsed must be an object when present\" };\n\t}\n\n\tif (d.obligations !== undefined && !isPlainObject(d.obligations)) {\n\t\treturn { ok: false, error: \"obligations must be an object when present\" };\n\t}\n\n\treturn { ok: true, decision: d as unknown as OpaDecision };\n}\n\nfunction validateConditionResult(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `conditionResults[${index}] must be an object`;\n\t}\n\tconst c = value as Record<string, unknown>;\n\tif (typeof c.conditionId !== \"string\" || c.conditionId.length === 0) {\n\t\treturn `conditionResults[${index}].conditionId must be a non-empty string`;\n\t}\n\tif (typeof c.passed !== \"boolean\") {\n\t\treturn `conditionResults[${index}].passed must be a boolean`;\n\t}\n\tif (typeof c.result !== \"string\" || !POLICY_RESULTS.includes(c.result as PolicyEvaluationResult)) {\n\t\treturn `conditionResults[${index}].result must be \"pass\" | \"warn\" | \"block\"`;\n\t}\n\tif (c.reason !== undefined && typeof c.reason !== \"string\") {\n\t\treturn `conditionResults[${index}].reason must be a string when present`;\n\t}\n\treturn null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * Stable JSON stringify — sorts object keys recursively so two\n * structurally-equal inputs produce the same string regardless of property\n * insertion order. Required for hash determinism across builds of the same\n * `buildInput` function.\n */\nexport function stableStringify(value: unknown): string {\n\tif (value === undefined) return \"null\";\n\tif (value === null || typeof value !== \"object\") return JSON.stringify(value);\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(stableStringify).join(\",\")}]`;\n\t}\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys\n\t\t.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n\t\t.join(\",\")}}`;\n}\n\n/**\n * Default input hasher: SHA-256 over a stable stringification.\n * Format: `sha256:<hex>` — namespaced so future migrations to a different\n * algorithm can be detected by a prefix check in evidence dashboards.\n */\nexport function defaultHashInput(input: Record<string, unknown>): string {\n\tconst hex = createHash(\"sha256\").update(stableStringify(input)).digest(\"hex\");\n\treturn `sha256:${hex}`;\n}\n","import type {\n\tPolicyContext,\n\tPolicyEvaluator,\n\tPolicyOutcome,\n} from \"@fabricorg/platform/policies\";\n\nimport { selectBundleRevision } from \"./bundle-selector\";\nimport { parseOpaDecision } from \"./decision-schema\";\nimport { defaultHashInput } from \"./hash\";\nimport type {\n\tCreateOpaPolicyEvaluatorOptions,\n\tOpaDecision,\n\tOpaPolicyBinding,\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n\tOpaPolicyFailureKind,\n} from \"./types\";\n\nconst DEFAULT_SERVICE_KEY = \"opa\";\nconst ENGINE_NAME = \"opa\";\n\n/**\n * Build a Fabric `PolicyEvaluator` that delegates decision-making to an OPA\n * (Open Policy Agent) deployment.\n *\n * The adapter accepts either of two service shapes under\n * `ctx.services[serviceKey]`:\n *\n * - `OpaPolicyExecutor` (preferred) — `execute(path, input)` returning a rich\n * `OpaPolicyExecution` (decision plus `decisionId`, `bundleRevision`,\n * `opaVersion`, `provenance`). Ship one with {@link executorFromOpaHttp} or\n * {@link executorFromOpaClient}.\n * - `OpaPolicyClient` — `evaluate(path, input)` returning the raw decision\n * only. Compatible with the high-level `@open-policy-agent/opa` SDK at the\n * cost of OPA-side provenance.\n *\n * If both methods are present, `execute` is preferred.\n *\n * The evaluator:\n *\n * 1. Resolves the service from `ctx.services[serviceKey]`.\n * 2. Builds a JSON input snapshot via `buildInput(ctx)` — domain code that\n * snapshots facts the engine needs. OPA never reaches into the database\n * directly; all facts flow through this function.\n * 3. Calls OPA at `binding.decisionPath`.\n * 4. Validates the response against the house-style shape (`OpaDecision`).\n * 5. Maps the decision to a `PolicyOutcome` and attaches engine provenance\n * under `dispatchEvidence.engine` — `name`, optional `version`, and\n * metadata covering `decisionPath`, `regoPackage`, `inputHash`, plus (when\n * the executor surfaces them) `decisionId`, `bundleName`, `bundleRevision`,\n * `bundleRevisionStatus`, and the raw `provenance` block.\n *\n * Failures (missing client, build failure, network failure, malformed\n * response) are governed by `onError`. Default `\"block\"` honors Fabric's\n * default-deny posture and records the failure category under\n * `engine.metadata.error`.\n */\nexport function createOpaPolicyEvaluator<TDb = unknown>(\n\toptions: CreateOpaPolicyEvaluatorOptions<TDb>,\n): PolicyEvaluator<TDb> {\n\tconst {\n\t\tbinding,\n\t\tbuildInput,\n\t\tserviceKey = DEFAULT_SERVICE_KEY,\n\t\tonError = \"block\",\n\t\tpreviewSafe = false,\n\t\tengineVersion,\n\t\tmapDecision,\n\t\thashInput = defaultHashInput,\n\t} = options;\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tversion: binding.version,\n\t\tpreviewSafe,\n\t\tevaluate: async (ctx) => {\n\t\t\tconst service = ctx.services?.[serviceKey];\n\t\t\tconst transport = resolveTransport(service);\n\t\t\tif (!transport) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"client_unavailable\",\n\t\t\t\t\t`No OPA client/executor at ctx.services[\"${serviceKey}\"]`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet input: Record<string, unknown>;\n\t\t\ttry {\n\t\t\t\tinput = await buildInput(ctx);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"input_build_failed\",\n\t\t\t\t\t`buildInput threw: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet inputHash: string | undefined;\n\t\t\ttry {\n\t\t\t\tinputHash = hashInput(input);\n\t\t\t} catch {\n\t\t\t\t// hashing is best-effort — if a custom hasher throws, omit the\n\t\t\t\t// field rather than fail the policy.\n\t\t\t}\n\n\t\t\tlet execution: OpaPolicyExecution;\n\t\t\ttry {\n\t\t\t\texecution = await transport(binding.decisionPath, input);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"evaluation_failed\",\n\t\t\t\t\t`OPA evaluation failed: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst parsed = parseOpaDecision(execution.decision);\n\t\t\tif (!parsed.ok) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"invalid_response\",\n\t\t\t\t\t`OPA returned malformed decision: ${parsed.error}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t\texecution,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn buildSuccessOutcome(\n\t\t\t\tbinding,\n\t\t\t\tengineVersion,\n\t\t\t\tparsed.decision,\n\t\t\t\texecution,\n\t\t\t\tinputHash,\n\t\t\t\tctx,\n\t\t\t\tmapDecision,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Discriminates between the rich executor shape and the legacy client shape.\n * Returns a uniform `(path, input) => OpaPolicyExecution` transport, or\n * `null` if the service doesn't implement either contract.\n *\n * `execute` is preferred when both are present so a v0.1 client wrapped by a\n * richer executor still wins.\n */\nfunction resolveTransport(\n\tservice: unknown,\n): ((path: string, input: unknown) => Promise<OpaPolicyExecution>) | null {\n\tif (!service || typeof service !== \"object\") return null;\n\tconst exec = (service as Partial<OpaPolicyExecutor>).execute;\n\tif (typeof exec === \"function\") {\n\t\treturn (path, input) => (service as OpaPolicyExecutor).execute(path, input);\n\t}\n\tconst eval_ = (service as Partial<OpaPolicyClient>).evaluate;\n\tif (typeof eval_ === \"function\") {\n\t\treturn async (path, input) => ({\n\t\t\tdecision: await (service as OpaPolicyClient).evaluate(path, input),\n\t\t});\n\t}\n\treturn null;\n}\n\nfunction buildSuccessOutcome<TDb>(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\tdecision: OpaDecision,\n\texecution: OpaPolicyExecution,\n\tinputHash: string | undefined,\n\tctx: PolicyContext<TDb>,\n\tmapDecision: CreateOpaPolicyEvaluatorOptions<TDb>[\"mapDecision\"],\n): PolicyOutcome {\n\tconst selection = selectBundleRevision(execution, binding);\n\tconst mapped = mapDecision\n\t\t? mapDecision(decision, ctx, execution)\n\t\t: { result: decision.result, reason: decision.reason };\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: mapped.result ?? decision.result,\n\t\treason: mapped.reason ?? decision.reason,\n\t\tmetadata: {\n\t\t\t...(mapped.metadata ?? {}),\n\t\t\topaDecision: decision,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution.provenance,\n\t\t\t\t\tconditionResults: decision.conditionResults,\n\t\t\t\t\tfactsUsed: decision.factsUsed,\n\t\t\t\t\tobligations: decision.obligations,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction makeFailureOutcome(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\terror: OpaPolicyFailureKind,\n\treason: string,\n\tonError: \"block\" | \"throw\",\n\tinputHash?: string,\n\texecution?: OpaPolicyExecution,\n): PolicyOutcome {\n\tif (onError === \"throw\") {\n\t\tconst err = new Error(reason);\n\t\t(err as Error & { kind?: OpaPolicyFailureKind }).kind = error;\n\t\tthrow err;\n\t}\n\n\tconst selection = execution ? selectBundleRevision(execution, binding) : {};\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: \"block\",\n\t\treason,\n\t\tmetadata: {\n\t\t\topaError: error,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution?.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution?.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution?.provenance,\n\t\t\t\t\terror,\n\t\t\t\t\terrorReason: reason,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction stripUndefined(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(obj)) {\n\t\tif (v !== undefined) out[k] = v;\n\t}\n\treturn out;\n}\n\nfunction formatError(err: unknown): string {\n\tif (err instanceof Error) return err.message;\n\tif (typeof err === \"string\") return err;\n\ttry {\n\t\treturn JSON.stringify(err);\n\t} catch {\n\t\treturn String(err);\n\t}\n}\n","import type {\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n} from \"./types\";\n\n/**\n * Wrap an `OpaPolicyClient` (the high-level `@open-policy-agent/opa` SDK\n * shape) as an {@link OpaPolicyExecutor}. The resulting executor carries no\n * OPA-side provenance — the high-level SDK discards `decisionId` and\n * `provenance` from the response — but does centralize the\n * `(path, input) => execution` plumbing so hosts can choose at startup whether\n * to wire a client or a full executor.\n *\n * For full provenance (decisionId, bundleRevision, opaVersion), prefer\n * {@link executorFromOpaHttp}.\n */\nexport function executorFromOpaClient(client: OpaPolicyClient): OpaPolicyExecutor {\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst decision = await client.evaluate(path, input);\n\t\t\treturn { decision };\n\t\t},\n\t};\n}\n","import type { OpaPolicyExecution, OpaPolicyExecutor } from \"./types\";\n\nexport interface OpaHttpExecutorOptions {\n\t/**\n\t * Base URL of the OPA server, e.g. `\"http://localhost:8181\"` or\n\t * `\"https://opa.internal.example.com\"`. Trailing slash is permitted; the\n\t * executor normalizes it.\n\t */\n\tbaseUrl: string;\n\t/**\n\t * Override for `globalThis.fetch`. Lets the host inject a fetch instance\n\t * with retries, mTLS, OpenTelemetry instrumentation, etc. Defaults to\n\t * `globalThis.fetch`.\n\t */\n\tfetch?: typeof globalThis.fetch;\n\t/** Extra request headers (e.g. `Authorization`). */\n\theaders?: Record<string, string>;\n\t/**\n\t * Request OPA-side provenance (`decision_id`, `provenance.version`,\n\t * `provenance.bundles[*].revision`) via `?provenance=true`. Defaults to\n\t * `true` — turning it off forfeits OPA's contribution to audit evidence\n\t * and is rarely what you want.\n\t */\n\tprovenance?: boolean;\n}\n\ninterface OpaHttpResponseBody {\n\tresult?: unknown;\n\tdecision_id?: string;\n\tprovenance?: {\n\t\tversion?: string;\n\t\tbuild_commit?: string;\n\t\tbuild_timestamp?: string;\n\t\tbuild_host?: string;\n\t\tbundles?: Record<string, { revision?: string }>;\n\t};\n}\n\n/**\n * Build an {@link OpaPolicyExecutor} that talks to OPA's REST API directly.\n *\n * POSTs to `{baseUrl}/v1/data/{path}?provenance=true` with body\n * `{ \"input\": ... }`. Parses the snake_case response and lifts:\n *\n * - `result` → `execution.decision`\n * - `decision_id` → `execution.decisionId`\n * - `provenance.version` → `execution.opaVersion`\n * - `provenance` (whole block) → `execution.provenance`\n *\n * Bundle revision selection happens in the factory, not the executor, because\n * it depends on `binding.bundleName`.\n *\n * Calling this in environments without `globalThis.fetch` (older Node) throws\n * at call time; pass `fetch: nodeFetch` or `fetch: undici-fetch` etc. via the\n * options to support those runtimes explicitly.\n */\nexport function executorFromOpaHttp(opts: OpaHttpExecutorOptions): OpaPolicyExecutor {\n\tconst baseUrl = stripTrailingSlash(opts.baseUrl);\n\tconst provenance = opts.provenance ?? true;\n\tconst customFetch = opts.fetch;\n\tconst headers = {\n\t\t\"content-type\": \"application/json\",\n\t\taccept: \"application/json\",\n\t\t...(opts.headers ?? {}),\n\t};\n\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst fetchImpl = customFetch ?? globalThis.fetch;\n\t\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"executorFromOpaHttp: no fetch implementation available. Pass `fetch` in options or run on a runtime with `globalThis.fetch`.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst url = `${baseUrl}/v1/data/${stripLeadingSlash(path)}${\n\t\t\t\tprovenance ? \"?provenance=true\" : \"\"\n\t\t\t}`;\n\t\t\tconst response = await fetchImpl(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({ input }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await safeReadText(response);\n\t\t\t\tthrow new Error(`OPA HTTP ${response.status} at ${url}: ${truncate(text, 500)}`);\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as OpaHttpResponseBody;\n\n\t\t\treturn {\n\t\t\t\tdecision: body.result,\n\t\t\t\tdecisionId: body.decision_id,\n\t\t\t\topaVersion: body.provenance?.version,\n\t\t\t\tprovenance: body.provenance as Record<string, unknown> | undefined,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction stripTrailingSlash(s: string): string {\n\treturn s.replace(/\\/+$/, \"\");\n}\n\nfunction stripLeadingSlash(s: string): string {\n\treturn s.replace(/^\\/+/, \"\");\n}\n\nasync function safeReadText(response: Response): Promise<string> {\n\ttry {\n\t\treturn await response.text();\n\t} catch {\n\t\treturn \"<unreadable body>\";\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\treturn s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/bundle-selector.ts","../src/decision-schema.ts","../src/hash.ts","../src/factory.ts","../src/executor-client.ts","../src/executor-http.ts"],"names":["createHash"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { OpaPolicyBinding, OpaPolicyExecution } from \"./types\";\n\nexport interface BundleSelection {\n\tbundleRevision?: string;\n\t/**\n\t * Set to `\"ambiguous\"` when OPA reports multiple bundles and the binding\n\t * did not pick one with `bundleName`. The adapter records this on the\n\t * evidence so dashboards can flag policies that need explicit selectors.\n\t * `\"missing\"` is set when provenance was expected but absent.\n\t */\n\tbundleRevisionStatus?: \"ambiguous\" | \"missing\";\n}\n\n/**\n * Determine which OPA bundle revision applies to this decision.\n *\n * Rules (in order):\n * 1. If the executor already populated `execution.bundleRevision`, use it.\n * 2. If `binding.bundleName` is set, look it up in `provenance.bundles`.\n * 3. If exactly one bundle is reported, use its revision unambiguously.\n * 4. If multiple bundles are reported and no selector exists, mark\n * `bundleRevisionStatus: \"ambiguous\"`.\n * 5. If no provenance is reported at all, return `{}` — the caller decides\n * whether absence is meaningful (it is not, for executors that don't\n * carry provenance).\n */\nexport function selectBundleRevision(\n\texecution: OpaPolicyExecution,\n\tbinding: OpaPolicyBinding,\n): BundleSelection {\n\tif (typeof execution.bundleRevision === \"string\" && execution.bundleRevision.length > 0) {\n\t\treturn { bundleRevision: execution.bundleRevision };\n\t}\n\n\tconst bundles = readBundles(execution.provenance);\n\tif (!bundles) return {};\n\n\tconst names = Object.keys(bundles);\n\tif (names.length === 0) return {};\n\n\tif (binding.bundleName) {\n\t\tconst revision = bundles[binding.bundleName]?.revision;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\tif (names.length === 1) {\n\t\tconst only = names[0];\n\t\t// names.length === 1 guarantees names[0] is defined, but the\n\t\t// non-null assertion satisfies noUncheckedIndexedAccess if enabled.\n\t\tconst revision = only !== undefined ? bundles[only]?.revision : undefined;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\treturn { bundleRevisionStatus: \"ambiguous\" };\n}\n\nfunction readBundles(\n\tprovenance: Record<string, unknown> | undefined,\n): Record<string, { revision?: unknown }> | undefined {\n\tif (!provenance || typeof provenance !== \"object\") return undefined;\n\tconst b = (provenance as { bundles?: unknown }).bundles;\n\tif (!b || typeof b !== \"object\" || Array.isArray(b)) return undefined;\n\treturn b as Record<string, { revision?: unknown }>;\n}\n","import type { PolicyEvaluationResult } from \"@fabricorg/platform/policies\";\nimport type { OpaDecision } from \"./types\";\n\nconst POLICY_RESULTS: readonly PolicyEvaluationResult[] = [\"pass\", \"warn\", \"block\"];\n\nexport type OpaDecisionParseResult =\n\t| { ok: true; decision: OpaDecision }\n\t| { ok: false; error: string };\n\n/**\n * Hand-rolled validator for the Rego \"house style\" decision shape. Avoids a\n * runtime zod dependency to keep the adapter zero-dep.\n *\n * Accepts a raw OPA result and either returns the parsed `OpaDecision` or a\n * single human-readable error string describing the first violation. We don't\n * accumulate every error: when audit evidence has to record \"OPA returned\n * something invalid\", one precise reason is more useful than a list.\n */\nexport function parseOpaDecision(raw: unknown): OpaDecisionParseResult {\n\tif (raw === null || typeof raw !== \"object\") {\n\t\treturn { ok: false, error: \"decision must be an object\" };\n\t}\n\n\tconst d = raw as Record<string, unknown>;\n\n\tif (typeof d.result !== \"string\" || !POLICY_RESULTS.includes(d.result as PolicyEvaluationResult)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `result must be \"pass\" | \"warn\" | \"block\" (got ${JSON.stringify(d.result)})`,\n\t\t};\n\t}\n\n\tif (d.reason !== undefined && typeof d.reason !== \"string\") {\n\t\treturn { ok: false, error: \"reason must be a string when present\" };\n\t}\n\n\tif (d.conditionResults !== undefined) {\n\t\tif (!Array.isArray(d.conditionResults)) {\n\t\t\treturn { ok: false, error: \"conditionResults must be an array when present\" };\n\t\t}\n\t\tfor (let i = 0; i < d.conditionResults.length; i += 1) {\n\t\t\tconst err = validateConditionResult(d.conditionResults[i], i);\n\t\t\tif (err) return { ok: false, error: err };\n\t\t}\n\t}\n\n\tif (d.guidance !== undefined) {\n\t\tconst err = validateGuidance(d.guidance);\n\t\tif (err) return { ok: false, error: err };\n\t}\n\n\tif (d.obligations !== undefined && !isPlainObject(d.obligations)) {\n\t\treturn { ok: false, error: \"obligations must be an object when present\" };\n\t}\n\n\treturn { ok: true, decision: d as unknown as OpaDecision };\n}\n\nfunction validateConditionResult(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `conditionResults[${index}] must be an object`;\n\t}\n\tconst c = value as Record<string, unknown>;\n\tif (typeof c.conditionId !== \"string\" || c.conditionId.length === 0) {\n\t\treturn `conditionResults[${index}].conditionId must be a non-empty string`;\n\t}\n\tif (typeof c.passed !== \"boolean\") {\n\t\treturn `conditionResults[${index}].passed must be a boolean`;\n\t}\n\tif (typeof c.result !== \"string\" || !POLICY_RESULTS.includes(c.result as PolicyEvaluationResult)) {\n\t\treturn `conditionResults[${index}].result must be \"pass\" | \"warn\" | \"block\"`;\n\t}\n\tif (c.reason !== undefined && typeof c.reason !== \"string\") {\n\t\treturn `conditionResults[${index}].reason must be a string when present`;\n\t}\n\treturn null;\n}\n\nfunction validateGuidance(value: unknown): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn \"guidance must be an object when present\";\n\t}\n\tconst guidance = value as Record<string, unknown>;\n\tif (guidance.summary !== undefined && typeof guidance.summary !== \"string\") {\n\t\treturn \"guidance.summary must be a string when present\";\n\t}\n\tif (!Array.isArray(guidance.factsUsed)) {\n\t\treturn \"guidance.factsUsed must be an array\";\n\t}\n\tfor (let i = 0; i < guidance.factsUsed.length; i += 1) {\n\t\tconst err = validatePolicyFact(guidance.factsUsed[i], i);\n\t\tif (err) return err;\n\t}\n\tif (!Array.isArray(guidance.correctiveActions)) {\n\t\treturn \"guidance.correctiveActions must be an array\";\n\t}\n\tfor (let i = 0; i < guidance.correctiveActions.length; i += 1) {\n\t\tconst err = validateCorrectiveAction(guidance.correctiveActions[i], i);\n\t\tif (err) return err;\n\t}\n\treturn null;\n}\n\nfunction validatePolicyFact(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `guidance.factsUsed[${index}] must be an object`;\n\t}\n\tconst fact = value as Record<string, unknown>;\n\tif (typeof fact.name !== \"string\" || fact.name.length === 0) {\n\t\treturn `guidance.factsUsed[${index}].name must be a non-empty string`;\n\t}\n\tif (fact.label !== undefined && typeof fact.label !== \"string\") {\n\t\treturn `guidance.factsUsed[${index}].label must be a string when present`;\n\t}\n\treturn null;\n}\n\nfunction validateCorrectiveAction(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `guidance.correctiveActions[${index}] must be an object`;\n\t}\n\tconst action = value as Record<string, unknown>;\n\tif (typeof action.label !== \"string\" || action.label.length === 0) {\n\t\treturn `guidance.correctiveActions[${index}].label must be a non-empty string`;\n\t}\n\tif (action.description !== undefined && typeof action.description !== \"string\") {\n\t\treturn `guidance.correctiveActions[${index}].description must be a string when present`;\n\t}\n\tif (action.actionId !== undefined && typeof action.actionId !== \"string\") {\n\t\treturn `guidance.correctiveActions[${index}].actionId must be a string when present`;\n\t}\n\tif (action.parameters !== undefined && !isPlainObject(action.parameters)) {\n\t\treturn `guidance.correctiveActions[${index}].parameters must be an object when present`;\n\t}\n\tif (action.target !== undefined && !isPlainObject(action.target)) {\n\t\treturn `guidance.correctiveActions[${index}].target must be an object when present`;\n\t}\n\tif (action.requiresConfirmation !== undefined && typeof action.requiresConfirmation !== \"boolean\") {\n\t\treturn `guidance.correctiveActions[${index}].requiresConfirmation must be a boolean when present`;\n\t}\n\treturn null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * Stable JSON stringify — sorts object keys recursively so two\n * structurally-equal inputs produce the same string regardless of property\n * insertion order. Required for hash determinism across builds of the same\n * `buildInput` function.\n */\nexport function stableStringify(value: unknown): string {\n\tif (value === undefined) return \"null\";\n\tif (value === null || typeof value !== \"object\") return JSON.stringify(value);\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(stableStringify).join(\",\")}]`;\n\t}\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys\n\t\t.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n\t\t.join(\",\")}}`;\n}\n\n/**\n * Default input hasher: SHA-256 over a stable stringification.\n * Format: `sha256:<hex>` — namespaced so future migrations to a different\n * algorithm can be detected by a prefix check in evidence dashboards.\n */\nexport function defaultHashInput(input: Record<string, unknown>): string {\n\tconst hex = createHash(\"sha256\").update(stableStringify(input)).digest(\"hex\");\n\treturn `sha256:${hex}`;\n}\n","import type {\n\tPolicyContext,\n\tPolicyEvaluator,\n\tPolicyOutcome,\n} from \"@fabricorg/platform/policies\";\n\nimport { selectBundleRevision } from \"./bundle-selector\";\nimport { parseOpaDecision } from \"./decision-schema\";\nimport { defaultHashInput } from \"./hash\";\nimport type {\n\tCreateOpaPolicyEvaluatorOptions,\n\tOpaDecision,\n\tOpaPolicyBinding,\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n\tOpaPolicyFailureKind,\n} from \"./types\";\n\nconst DEFAULT_SERVICE_KEY = \"opa\";\nconst ENGINE_NAME = \"opa\";\n\n/**\n * Build a Fabric `PolicyEvaluator` that delegates decision-making to an OPA\n * (Open Policy Agent) deployment.\n *\n * The adapter accepts either of two service shapes under\n * `ctx.services[serviceKey]`:\n *\n * - `OpaPolicyExecutor` (preferred) — `execute(path, input)` returning a rich\n * `OpaPolicyExecution` (decision plus `decisionId`, `bundleRevision`,\n * `opaVersion`, `provenance`). Ship one with {@link executorFromOpaHttp} or\n * {@link executorFromOpaClient}.\n * - `OpaPolicyClient` — `evaluate(path, input)` returning the raw decision\n * only. Compatible with the high-level `@open-policy-agent/opa` SDK at the\n * cost of OPA-side provenance.\n *\n * If both methods are present, `execute` is preferred.\n *\n * The evaluator:\n *\n * 1. Resolves the service from `ctx.services[serviceKey]`.\n * 2. Builds a JSON input snapshot via `buildInput(ctx)` — domain code that\n * snapshots facts the engine needs. OPA never reaches into the database\n * directly; all facts flow through this function.\n * 3. Calls OPA at `binding.decisionPath`.\n * 4. Validates the response against the house-style shape (`OpaDecision`).\n * 5. Maps the decision to a `PolicyOutcome` and attaches engine provenance\n * under `dispatchEvidence.engine` — `name`, optional `version`, and\n * metadata covering `decisionPath`, `regoPackage`, `inputHash`, plus (when\n * the executor surfaces them) `decisionId`, `bundleName`, `bundleRevision`,\n * `bundleRevisionStatus`, and the raw `provenance` block.\n *\n * Failures (missing client, build failure, network failure, malformed\n * response) are governed by `onError`. Default `\"block\"` honors Fabric's\n * default-deny posture and records the failure category under\n * `engine.metadata.error`.\n */\nexport function createOpaPolicyEvaluator<TDb = unknown>(\n\toptions: CreateOpaPolicyEvaluatorOptions<TDb>,\n): PolicyEvaluator<TDb> {\n\tconst {\n\t\tbinding,\n\t\tbuildInput,\n\t\tserviceKey = DEFAULT_SERVICE_KEY,\n\t\tonError = \"block\",\n\t\tpreviewSafe = false,\n\t\tengineVersion,\n\t\tmapDecision,\n\t\thashInput = defaultHashInput,\n\t} = options;\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tversion: binding.version,\n\t\tpreviewSafe,\n\t\tevaluate: async (ctx) => {\n\t\t\tconst service = ctx.services?.[serviceKey];\n\t\t\tconst transport = resolveTransport(service);\n\t\t\tif (!transport) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"client_unavailable\",\n\t\t\t\t\t`No OPA client/executor at ctx.services[\"${serviceKey}\"]`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet input: Record<string, unknown>;\n\t\t\ttry {\n\t\t\t\tinput = await buildInput(ctx);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"input_build_failed\",\n\t\t\t\t\t`buildInput threw: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet inputHash: string | undefined;\n\t\t\ttry {\n\t\t\t\tinputHash = hashInput(input);\n\t\t\t} catch {\n\t\t\t\t// hashing is best-effort — if a custom hasher throws, omit the\n\t\t\t\t// field rather than fail the policy.\n\t\t\t}\n\n\t\t\tlet execution: OpaPolicyExecution;\n\t\t\ttry {\n\t\t\t\texecution = await transport(binding.decisionPath, input);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"evaluation_failed\",\n\t\t\t\t\t`OPA evaluation failed: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst parsed = parseOpaDecision(execution.decision);\n\t\t\tif (!parsed.ok) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"invalid_response\",\n\t\t\t\t\t`OPA returned malformed decision: ${parsed.error}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t\texecution,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn buildSuccessOutcome(\n\t\t\t\tbinding,\n\t\t\t\tengineVersion,\n\t\t\t\tparsed.decision,\n\t\t\t\texecution,\n\t\t\t\tinputHash,\n\t\t\t\tctx,\n\t\t\t\tmapDecision,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Discriminates between the rich executor shape and the legacy client shape.\n * Returns a uniform `(path, input) => OpaPolicyExecution` transport, or\n * `null` if the service doesn't implement either contract.\n *\n * `execute` is preferred when both are present so a v0.1 client wrapped by a\n * richer executor still wins.\n */\nfunction resolveTransport(\n\tservice: unknown,\n): ((path: string, input: unknown) => Promise<OpaPolicyExecution>) | null {\n\tif (!service || typeof service !== \"object\") return null;\n\tconst exec = (service as Partial<OpaPolicyExecutor>).execute;\n\tif (typeof exec === \"function\") {\n\t\treturn (path, input) => (service as OpaPolicyExecutor).execute(path, input);\n\t}\n\tconst eval_ = (service as Partial<OpaPolicyClient>).evaluate;\n\tif (typeof eval_ === \"function\") {\n\t\treturn async (path, input) => ({\n\t\t\tdecision: await (service as OpaPolicyClient).evaluate(path, input),\n\t\t});\n\t}\n\treturn null;\n}\n\nfunction buildSuccessOutcome<TDb>(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\tdecision: OpaDecision,\n\texecution: OpaPolicyExecution,\n\tinputHash: string | undefined,\n\tctx: PolicyContext<TDb>,\n\tmapDecision: CreateOpaPolicyEvaluatorOptions<TDb>[\"mapDecision\"],\n): PolicyOutcome {\n\tconst selection = selectBundleRevision(execution, binding);\n\tconst mapped = mapDecision\n\t\t? mapDecision(decision, ctx, execution)\n\t\t: { result: decision.result, reason: decision.reason };\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: mapped.result ?? decision.result,\n\t\treason: mapped.reason ?? decision.reason,\n\t\tguidance: mapped.guidance ?? decision.guidance,\n\t\tmetadata: {\n\t\t\t...(mapped.metadata ?? {}),\n\t\t\topaDecision: decision,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution.provenance,\n\t\t\t\t\tconditionResults: decision.conditionResults,\n\t\t\t\t\tobligations: decision.obligations,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction makeFailureOutcome(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\terror: OpaPolicyFailureKind,\n\treason: string,\n\tonError: \"block\" | \"throw\",\n\tinputHash?: string,\n\texecution?: OpaPolicyExecution,\n): PolicyOutcome {\n\tif (onError === \"throw\") {\n\t\tconst err = new Error(reason);\n\t\t(err as Error & { kind?: OpaPolicyFailureKind }).kind = error;\n\t\tthrow err;\n\t}\n\n\tconst selection = execution ? selectBundleRevision(execution, binding) : {};\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: \"block\",\n\t\treason,\n\t\tguidance: {\n\t\t\tsummary: \"OPA policy evaluation could not complete.\",\n\t\t\tfactsUsed: [\n\t\t\t\t{ name: \"opaFailureKind\", value: error, label: \"Failure kind\" },\n\t\t\t\t{ name: \"decisionPath\", value: binding.decisionPath, label: \"Decision path\" },\n\t\t\t],\n\t\t\tcorrectiveActions: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Check OPA policy engine\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Verify the OPA service is reachable, serving the expected bundle, and returning the Fabric OPA decision shape.\",\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tmetadata: {\n\t\t\topaError: error,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution?.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution?.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution?.provenance,\n\t\t\t\t\terror,\n\t\t\t\t\terrorReason: reason,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction stripUndefined(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(obj)) {\n\t\tif (v !== undefined) out[k] = v;\n\t}\n\treturn out;\n}\n\nfunction formatError(err: unknown): string {\n\tif (err instanceof Error) return err.message;\n\tif (typeof err === \"string\") return err;\n\ttry {\n\t\treturn JSON.stringify(err);\n\t} catch {\n\t\treturn String(err);\n\t}\n}\n","import type {\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n} from \"./types\";\n\n/**\n * Wrap an `OpaPolicyClient` (the high-level `@open-policy-agent/opa` SDK\n * shape) as an {@link OpaPolicyExecutor}. The resulting executor carries no\n * OPA-side provenance — the high-level SDK discards `decisionId` and\n * `provenance` from the response — but does centralize the\n * `(path, input) => execution` plumbing so hosts can choose at startup whether\n * to wire a client or a full executor.\n *\n * For full provenance (decisionId, bundleRevision, opaVersion), prefer\n * {@link executorFromOpaHttp}.\n */\nexport function executorFromOpaClient(client: OpaPolicyClient): OpaPolicyExecutor {\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst decision = await client.evaluate(path, input);\n\t\t\treturn { decision };\n\t\t},\n\t};\n}\n","import type { OpaPolicyExecution, OpaPolicyExecutor } from \"./types\";\n\nexport interface OpaHttpExecutorOptions {\n\t/**\n\t * Base URL of the OPA server, e.g. `\"http://localhost:8181\"` or\n\t * `\"https://opa.internal.example.com\"`. Trailing slash is permitted; the\n\t * executor normalizes it.\n\t */\n\tbaseUrl: string;\n\t/**\n\t * Override for `globalThis.fetch`. Lets the host inject a fetch instance\n\t * with retries, mTLS, OpenTelemetry instrumentation, etc. Defaults to\n\t * `globalThis.fetch`.\n\t */\n\tfetch?: typeof globalThis.fetch;\n\t/** Extra request headers (e.g. `Authorization`). */\n\theaders?: Record<string, string>;\n\t/**\n\t * Request OPA-side provenance (`decision_id`, `provenance.version`,\n\t * `provenance.bundles[*].revision`) via `?provenance=true`. Defaults to\n\t * `true` — turning it off forfeits OPA's contribution to audit evidence\n\t * and is rarely what you want.\n\t */\n\tprovenance?: boolean;\n}\n\ninterface OpaHttpResponseBody {\n\tresult?: unknown;\n\tdecision_id?: string;\n\tprovenance?: {\n\t\tversion?: string;\n\t\tbuild_commit?: string;\n\t\tbuild_timestamp?: string;\n\t\tbuild_host?: string;\n\t\tbundles?: Record<string, { revision?: string }>;\n\t};\n}\n\n/**\n * Build an {@link OpaPolicyExecutor} that talks to OPA's REST API directly.\n *\n * POSTs to `{baseUrl}/v1/data/{path}?provenance=true` with body\n * `{ \"input\": ... }`. Parses the snake_case response and lifts:\n *\n * - `result` → `execution.decision`\n * - `decision_id` → `execution.decisionId`\n * - `provenance.version` → `execution.opaVersion`\n * - `provenance` (whole block) → `execution.provenance`\n *\n * Bundle revision selection happens in the factory, not the executor, because\n * it depends on `binding.bundleName`.\n *\n * Calling this in environments without `globalThis.fetch` (older Node) throws\n * at call time; pass `fetch: nodeFetch` or `fetch: undici-fetch` etc. via the\n * options to support those runtimes explicitly.\n */\nexport function executorFromOpaHttp(opts: OpaHttpExecutorOptions): OpaPolicyExecutor {\n\tconst baseUrl = stripTrailingSlash(opts.baseUrl);\n\tconst provenance = opts.provenance ?? true;\n\tconst customFetch = opts.fetch;\n\tconst headers = {\n\t\t\"content-type\": \"application/json\",\n\t\taccept: \"application/json\",\n\t\t...(opts.headers ?? {}),\n\t};\n\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst fetchImpl = customFetch ?? globalThis.fetch;\n\t\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"executorFromOpaHttp: no fetch implementation available. Pass `fetch` in options or run on a runtime with `globalThis.fetch`.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst url = `${baseUrl}/v1/data/${stripLeadingSlash(path)}${\n\t\t\t\tprovenance ? \"?provenance=true\" : \"\"\n\t\t\t}`;\n\t\t\tconst response = await fetchImpl(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({ input }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await safeReadText(response);\n\t\t\t\tthrow new Error(`OPA HTTP ${response.status} at ${url}: ${truncate(text, 500)}`);\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as OpaHttpResponseBody;\n\n\t\t\treturn {\n\t\t\t\tdecision: body.result,\n\t\t\t\tdecisionId: body.decision_id,\n\t\t\t\topaVersion: body.provenance?.version,\n\t\t\t\tprovenance: body.provenance as Record<string, unknown> | undefined,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction stripTrailingSlash(s: string): string {\n\treturn s.replace(/\\/+$/, \"\");\n}\n\nfunction stripLeadingSlash(s: string): string {\n\treturn s.replace(/^\\/+/, \"\");\n}\n\nasync function safeReadText(response: Response): Promise<string> {\n\ttry {\n\t\treturn await response.text();\n\t} catch {\n\t\treturn \"<unreadable body>\";\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\treturn s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PolicyId, PolicyContext, PolicyEvaluationResult, PolicyOutcome, PolicyEvaluator } from '@fabricorg/platform/policies';
|
|
1
|
+
import { PolicyId, PolicyContext, PolicyEvaluationResult, PolicyGuidance, PolicyOutcome, PolicyEvaluator } from '@fabricorg/platform/policies';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Binding between a Fabric platform policy and an OPA decision. Verticals
|
|
@@ -87,13 +87,13 @@ interface OpaPolicyExecutor {
|
|
|
87
87
|
interface OpaDecision {
|
|
88
88
|
result: PolicyEvaluationResult;
|
|
89
89
|
reason?: string;
|
|
90
|
+
guidance?: PolicyGuidance;
|
|
90
91
|
conditionResults?: Array<{
|
|
91
92
|
conditionId: string;
|
|
92
93
|
passed: boolean;
|
|
93
94
|
result: PolicyEvaluationResult;
|
|
94
95
|
reason?: string;
|
|
95
96
|
}>;
|
|
96
|
-
factsUsed?: Record<string, unknown>;
|
|
97
97
|
obligations?: Record<string, unknown>;
|
|
98
98
|
}
|
|
99
99
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PolicyId, PolicyContext, PolicyEvaluationResult, PolicyOutcome, PolicyEvaluator } from '@fabricorg/platform/policies';
|
|
1
|
+
import { PolicyId, PolicyContext, PolicyEvaluationResult, PolicyGuidance, PolicyOutcome, PolicyEvaluator } from '@fabricorg/platform/policies';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Binding between a Fabric platform policy and an OPA decision. Verticals
|
|
@@ -87,13 +87,13 @@ interface OpaPolicyExecutor {
|
|
|
87
87
|
interface OpaDecision {
|
|
88
88
|
result: PolicyEvaluationResult;
|
|
89
89
|
reason?: string;
|
|
90
|
+
guidance?: PolicyGuidance;
|
|
90
91
|
conditionResults?: Array<{
|
|
91
92
|
conditionId: string;
|
|
92
93
|
passed: boolean;
|
|
93
94
|
result: PolicyEvaluationResult;
|
|
94
95
|
reason?: string;
|
|
95
96
|
}>;
|
|
96
|
-
factsUsed?: Record<string, unknown>;
|
|
97
97
|
obligations?: Record<string, unknown>;
|
|
98
98
|
}
|
|
99
99
|
/**
|
package/dist/index.js
CHANGED
|
@@ -52,8 +52,9 @@ function parseOpaDecision(raw) {
|
|
|
52
52
|
if (err) return { ok: false, error: err };
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
if (d.
|
|
56
|
-
|
|
55
|
+
if (d.guidance !== void 0) {
|
|
56
|
+
const err = validateGuidance(d.guidance);
|
|
57
|
+
if (err) return { ok: false, error: err };
|
|
57
58
|
}
|
|
58
59
|
if (d.obligations !== void 0 && !isPlainObject(d.obligations)) {
|
|
59
60
|
return { ok: false, error: "obligations must be an object when present" };
|
|
@@ -79,6 +80,68 @@ function validateConditionResult(value, index) {
|
|
|
79
80
|
}
|
|
80
81
|
return null;
|
|
81
82
|
}
|
|
83
|
+
function validateGuidance(value) {
|
|
84
|
+
if (!isPlainObject(value)) {
|
|
85
|
+
return "guidance must be an object when present";
|
|
86
|
+
}
|
|
87
|
+
const guidance = value;
|
|
88
|
+
if (guidance.summary !== void 0 && typeof guidance.summary !== "string") {
|
|
89
|
+
return "guidance.summary must be a string when present";
|
|
90
|
+
}
|
|
91
|
+
if (!Array.isArray(guidance.factsUsed)) {
|
|
92
|
+
return "guidance.factsUsed must be an array";
|
|
93
|
+
}
|
|
94
|
+
for (let i = 0; i < guidance.factsUsed.length; i += 1) {
|
|
95
|
+
const err = validatePolicyFact(guidance.factsUsed[i], i);
|
|
96
|
+
if (err) return err;
|
|
97
|
+
}
|
|
98
|
+
if (!Array.isArray(guidance.correctiveActions)) {
|
|
99
|
+
return "guidance.correctiveActions must be an array";
|
|
100
|
+
}
|
|
101
|
+
for (let i = 0; i < guidance.correctiveActions.length; i += 1) {
|
|
102
|
+
const err = validateCorrectiveAction(guidance.correctiveActions[i], i);
|
|
103
|
+
if (err) return err;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function validatePolicyFact(value, index) {
|
|
108
|
+
if (!isPlainObject(value)) {
|
|
109
|
+
return `guidance.factsUsed[${index}] must be an object`;
|
|
110
|
+
}
|
|
111
|
+
const fact = value;
|
|
112
|
+
if (typeof fact.name !== "string" || fact.name.length === 0) {
|
|
113
|
+
return `guidance.factsUsed[${index}].name must be a non-empty string`;
|
|
114
|
+
}
|
|
115
|
+
if (fact.label !== void 0 && typeof fact.label !== "string") {
|
|
116
|
+
return `guidance.factsUsed[${index}].label must be a string when present`;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function validateCorrectiveAction(value, index) {
|
|
121
|
+
if (!isPlainObject(value)) {
|
|
122
|
+
return `guidance.correctiveActions[${index}] must be an object`;
|
|
123
|
+
}
|
|
124
|
+
const action = value;
|
|
125
|
+
if (typeof action.label !== "string" || action.label.length === 0) {
|
|
126
|
+
return `guidance.correctiveActions[${index}].label must be a non-empty string`;
|
|
127
|
+
}
|
|
128
|
+
if (action.description !== void 0 && typeof action.description !== "string") {
|
|
129
|
+
return `guidance.correctiveActions[${index}].description must be a string when present`;
|
|
130
|
+
}
|
|
131
|
+
if (action.actionId !== void 0 && typeof action.actionId !== "string") {
|
|
132
|
+
return `guidance.correctiveActions[${index}].actionId must be a string when present`;
|
|
133
|
+
}
|
|
134
|
+
if (action.parameters !== void 0 && !isPlainObject(action.parameters)) {
|
|
135
|
+
return `guidance.correctiveActions[${index}].parameters must be an object when present`;
|
|
136
|
+
}
|
|
137
|
+
if (action.target !== void 0 && !isPlainObject(action.target)) {
|
|
138
|
+
return `guidance.correctiveActions[${index}].target must be an object when present`;
|
|
139
|
+
}
|
|
140
|
+
if (action.requiresConfirmation !== void 0 && typeof action.requiresConfirmation !== "boolean") {
|
|
141
|
+
return `guidance.correctiveActions[${index}].requiresConfirmation must be a boolean when present`;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
82
145
|
function isPlainObject(value) {
|
|
83
146
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
84
147
|
}
|
|
@@ -203,6 +266,7 @@ function buildSuccessOutcome(binding, engineVersion, decision, execution, inputH
|
|
|
203
266
|
policyVersion: binding.version,
|
|
204
267
|
result: mapped.result ?? decision.result,
|
|
205
268
|
reason: mapped.reason ?? decision.reason,
|
|
269
|
+
guidance: mapped.guidance ?? decision.guidance,
|
|
206
270
|
metadata: {
|
|
207
271
|
...mapped.metadata ?? {},
|
|
208
272
|
opaDecision: decision
|
|
@@ -225,7 +289,6 @@ function buildSuccessOutcome(binding, engineVersion, decision, execution, inputH
|
|
|
225
289
|
bundleRevisionStatus: selection.bundleRevisionStatus,
|
|
226
290
|
provenance: execution.provenance,
|
|
227
291
|
conditionResults: decision.conditionResults,
|
|
228
|
-
factsUsed: decision.factsUsed,
|
|
229
292
|
obligations: decision.obligations
|
|
230
293
|
})
|
|
231
294
|
}
|
|
@@ -244,6 +307,19 @@ function makeFailureOutcome(binding, engineVersion, error, reason, onError, inpu
|
|
|
244
307
|
policyVersion: binding.version,
|
|
245
308
|
result: "block",
|
|
246
309
|
reason,
|
|
310
|
+
guidance: {
|
|
311
|
+
summary: "OPA policy evaluation could not complete.",
|
|
312
|
+
factsUsed: [
|
|
313
|
+
{ name: "opaFailureKind", value: error, label: "Failure kind" },
|
|
314
|
+
{ name: "decisionPath", value: binding.decisionPath, label: "Decision path" }
|
|
315
|
+
],
|
|
316
|
+
correctiveActions: [
|
|
317
|
+
{
|
|
318
|
+
label: "Check OPA policy engine",
|
|
319
|
+
description: "Verify the OPA service is reachable, serving the expected bundle, and returning the Fabric OPA decision shape."
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
},
|
|
247
323
|
metadata: {
|
|
248
324
|
opaError: error
|
|
249
325
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bundle-selector.ts","../src/decision-schema.ts","../src/hash.ts","../src/factory.ts","../src/executor-client.ts","../src/executor-http.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { OpaPolicyBinding, OpaPolicyExecution } from \"./types\";\n\nexport interface BundleSelection {\n\tbundleRevision?: string;\n\t/**\n\t * Set to `\"ambiguous\"` when OPA reports multiple bundles and the binding\n\t * did not pick one with `bundleName`. The adapter records this on the\n\t * evidence so dashboards can flag policies that need explicit selectors.\n\t * `\"missing\"` is set when provenance was expected but absent.\n\t */\n\tbundleRevisionStatus?: \"ambiguous\" | \"missing\";\n}\n\n/**\n * Determine which OPA bundle revision applies to this decision.\n *\n * Rules (in order):\n * 1. If the executor already populated `execution.bundleRevision`, use it.\n * 2. If `binding.bundleName` is set, look it up in `provenance.bundles`.\n * 3. If exactly one bundle is reported, use its revision unambiguously.\n * 4. If multiple bundles are reported and no selector exists, mark\n * `bundleRevisionStatus: \"ambiguous\"`.\n * 5. If no provenance is reported at all, return `{}` — the caller decides\n * whether absence is meaningful (it is not, for executors that don't\n * carry provenance).\n */\nexport function selectBundleRevision(\n\texecution: OpaPolicyExecution,\n\tbinding: OpaPolicyBinding,\n): BundleSelection {\n\tif (typeof execution.bundleRevision === \"string\" && execution.bundleRevision.length > 0) {\n\t\treturn { bundleRevision: execution.bundleRevision };\n\t}\n\n\tconst bundles = readBundles(execution.provenance);\n\tif (!bundles) return {};\n\n\tconst names = Object.keys(bundles);\n\tif (names.length === 0) return {};\n\n\tif (binding.bundleName) {\n\t\tconst revision = bundles[binding.bundleName]?.revision;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\tif (names.length === 1) {\n\t\tconst only = names[0];\n\t\t// names.length === 1 guarantees names[0] is defined, but the\n\t\t// non-null assertion satisfies noUncheckedIndexedAccess if enabled.\n\t\tconst revision = only !== undefined ? bundles[only]?.revision : undefined;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\treturn { bundleRevisionStatus: \"ambiguous\" };\n}\n\nfunction readBundles(\n\tprovenance: Record<string, unknown> | undefined,\n): Record<string, { revision?: unknown }> | undefined {\n\tif (!provenance || typeof provenance !== \"object\") return undefined;\n\tconst b = (provenance as { bundles?: unknown }).bundles;\n\tif (!b || typeof b !== \"object\" || Array.isArray(b)) return undefined;\n\treturn b as Record<string, { revision?: unknown }>;\n}\n","import type { PolicyEvaluationResult } from \"@fabricorg/platform/policies\";\nimport type { OpaDecision } from \"./types\";\n\nconst POLICY_RESULTS: readonly PolicyEvaluationResult[] = [\"pass\", \"warn\", \"block\"];\n\nexport type OpaDecisionParseResult =\n\t| { ok: true; decision: OpaDecision }\n\t| { ok: false; error: string };\n\n/**\n * Hand-rolled validator for the Rego \"house style\" decision shape. Avoids a\n * runtime zod dependency to keep the adapter zero-dep.\n *\n * Accepts a raw OPA result and either returns the parsed `OpaDecision` or a\n * single human-readable error string describing the first violation. We don't\n * accumulate every error: when audit evidence has to record \"OPA returned\n * something invalid\", one precise reason is more useful than a list.\n */\nexport function parseOpaDecision(raw: unknown): OpaDecisionParseResult {\n\tif (raw === null || typeof raw !== \"object\") {\n\t\treturn { ok: false, error: \"decision must be an object\" };\n\t}\n\n\tconst d = raw as Record<string, unknown>;\n\n\tif (typeof d.result !== \"string\" || !POLICY_RESULTS.includes(d.result as PolicyEvaluationResult)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `result must be \"pass\" | \"warn\" | \"block\" (got ${JSON.stringify(d.result)})`,\n\t\t};\n\t}\n\n\tif (d.reason !== undefined && typeof d.reason !== \"string\") {\n\t\treturn { ok: false, error: \"reason must be a string when present\" };\n\t}\n\n\tif (d.conditionResults !== undefined) {\n\t\tif (!Array.isArray(d.conditionResults)) {\n\t\t\treturn { ok: false, error: \"conditionResults must be an array when present\" };\n\t\t}\n\t\tfor (let i = 0; i < d.conditionResults.length; i += 1) {\n\t\t\tconst err = validateConditionResult(d.conditionResults[i], i);\n\t\t\tif (err) return { ok: false, error: err };\n\t\t}\n\t}\n\n\tif (d.factsUsed !== undefined && !isPlainObject(d.factsUsed)) {\n\t\treturn { ok: false, error: \"factsUsed must be an object when present\" };\n\t}\n\n\tif (d.obligations !== undefined && !isPlainObject(d.obligations)) {\n\t\treturn { ok: false, error: \"obligations must be an object when present\" };\n\t}\n\n\treturn { ok: true, decision: d as unknown as OpaDecision };\n}\n\nfunction validateConditionResult(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `conditionResults[${index}] must be an object`;\n\t}\n\tconst c = value as Record<string, unknown>;\n\tif (typeof c.conditionId !== \"string\" || c.conditionId.length === 0) {\n\t\treturn `conditionResults[${index}].conditionId must be a non-empty string`;\n\t}\n\tif (typeof c.passed !== \"boolean\") {\n\t\treturn `conditionResults[${index}].passed must be a boolean`;\n\t}\n\tif (typeof c.result !== \"string\" || !POLICY_RESULTS.includes(c.result as PolicyEvaluationResult)) {\n\t\treturn `conditionResults[${index}].result must be \"pass\" | \"warn\" | \"block\"`;\n\t}\n\tif (c.reason !== undefined && typeof c.reason !== \"string\") {\n\t\treturn `conditionResults[${index}].reason must be a string when present`;\n\t}\n\treturn null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * Stable JSON stringify — sorts object keys recursively so two\n * structurally-equal inputs produce the same string regardless of property\n * insertion order. Required for hash determinism across builds of the same\n * `buildInput` function.\n */\nexport function stableStringify(value: unknown): string {\n\tif (value === undefined) return \"null\";\n\tif (value === null || typeof value !== \"object\") return JSON.stringify(value);\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(stableStringify).join(\",\")}]`;\n\t}\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys\n\t\t.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n\t\t.join(\",\")}}`;\n}\n\n/**\n * Default input hasher: SHA-256 over a stable stringification.\n * Format: `sha256:<hex>` — namespaced so future migrations to a different\n * algorithm can be detected by a prefix check in evidence dashboards.\n */\nexport function defaultHashInput(input: Record<string, unknown>): string {\n\tconst hex = createHash(\"sha256\").update(stableStringify(input)).digest(\"hex\");\n\treturn `sha256:${hex}`;\n}\n","import type {\n\tPolicyContext,\n\tPolicyEvaluator,\n\tPolicyOutcome,\n} from \"@fabricorg/platform/policies\";\n\nimport { selectBundleRevision } from \"./bundle-selector\";\nimport { parseOpaDecision } from \"./decision-schema\";\nimport { defaultHashInput } from \"./hash\";\nimport type {\n\tCreateOpaPolicyEvaluatorOptions,\n\tOpaDecision,\n\tOpaPolicyBinding,\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n\tOpaPolicyFailureKind,\n} from \"./types\";\n\nconst DEFAULT_SERVICE_KEY = \"opa\";\nconst ENGINE_NAME = \"opa\";\n\n/**\n * Build a Fabric `PolicyEvaluator` that delegates decision-making to an OPA\n * (Open Policy Agent) deployment.\n *\n * The adapter accepts either of two service shapes under\n * `ctx.services[serviceKey]`:\n *\n * - `OpaPolicyExecutor` (preferred) — `execute(path, input)` returning a rich\n * `OpaPolicyExecution` (decision plus `decisionId`, `bundleRevision`,\n * `opaVersion`, `provenance`). Ship one with {@link executorFromOpaHttp} or\n * {@link executorFromOpaClient}.\n * - `OpaPolicyClient` — `evaluate(path, input)` returning the raw decision\n * only. Compatible with the high-level `@open-policy-agent/opa` SDK at the\n * cost of OPA-side provenance.\n *\n * If both methods are present, `execute` is preferred.\n *\n * The evaluator:\n *\n * 1. Resolves the service from `ctx.services[serviceKey]`.\n * 2. Builds a JSON input snapshot via `buildInput(ctx)` — domain code that\n * snapshots facts the engine needs. OPA never reaches into the database\n * directly; all facts flow through this function.\n * 3. Calls OPA at `binding.decisionPath`.\n * 4. Validates the response against the house-style shape (`OpaDecision`).\n * 5. Maps the decision to a `PolicyOutcome` and attaches engine provenance\n * under `dispatchEvidence.engine` — `name`, optional `version`, and\n * metadata covering `decisionPath`, `regoPackage`, `inputHash`, plus (when\n * the executor surfaces them) `decisionId`, `bundleName`, `bundleRevision`,\n * `bundleRevisionStatus`, and the raw `provenance` block.\n *\n * Failures (missing client, build failure, network failure, malformed\n * response) are governed by `onError`. Default `\"block\"` honors Fabric's\n * default-deny posture and records the failure category under\n * `engine.metadata.error`.\n */\nexport function createOpaPolicyEvaluator<TDb = unknown>(\n\toptions: CreateOpaPolicyEvaluatorOptions<TDb>,\n): PolicyEvaluator<TDb> {\n\tconst {\n\t\tbinding,\n\t\tbuildInput,\n\t\tserviceKey = DEFAULT_SERVICE_KEY,\n\t\tonError = \"block\",\n\t\tpreviewSafe = false,\n\t\tengineVersion,\n\t\tmapDecision,\n\t\thashInput = defaultHashInput,\n\t} = options;\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tversion: binding.version,\n\t\tpreviewSafe,\n\t\tevaluate: async (ctx) => {\n\t\t\tconst service = ctx.services?.[serviceKey];\n\t\t\tconst transport = resolveTransport(service);\n\t\t\tif (!transport) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"client_unavailable\",\n\t\t\t\t\t`No OPA client/executor at ctx.services[\"${serviceKey}\"]`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet input: Record<string, unknown>;\n\t\t\ttry {\n\t\t\t\tinput = await buildInput(ctx);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"input_build_failed\",\n\t\t\t\t\t`buildInput threw: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet inputHash: string | undefined;\n\t\t\ttry {\n\t\t\t\tinputHash = hashInput(input);\n\t\t\t} catch {\n\t\t\t\t// hashing is best-effort — if a custom hasher throws, omit the\n\t\t\t\t// field rather than fail the policy.\n\t\t\t}\n\n\t\t\tlet execution: OpaPolicyExecution;\n\t\t\ttry {\n\t\t\t\texecution = await transport(binding.decisionPath, input);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"evaluation_failed\",\n\t\t\t\t\t`OPA evaluation failed: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst parsed = parseOpaDecision(execution.decision);\n\t\t\tif (!parsed.ok) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"invalid_response\",\n\t\t\t\t\t`OPA returned malformed decision: ${parsed.error}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t\texecution,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn buildSuccessOutcome(\n\t\t\t\tbinding,\n\t\t\t\tengineVersion,\n\t\t\t\tparsed.decision,\n\t\t\t\texecution,\n\t\t\t\tinputHash,\n\t\t\t\tctx,\n\t\t\t\tmapDecision,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Discriminates between the rich executor shape and the legacy client shape.\n * Returns a uniform `(path, input) => OpaPolicyExecution` transport, or\n * `null` if the service doesn't implement either contract.\n *\n * `execute` is preferred when both are present so a v0.1 client wrapped by a\n * richer executor still wins.\n */\nfunction resolveTransport(\n\tservice: unknown,\n): ((path: string, input: unknown) => Promise<OpaPolicyExecution>) | null {\n\tif (!service || typeof service !== \"object\") return null;\n\tconst exec = (service as Partial<OpaPolicyExecutor>).execute;\n\tif (typeof exec === \"function\") {\n\t\treturn (path, input) => (service as OpaPolicyExecutor).execute(path, input);\n\t}\n\tconst eval_ = (service as Partial<OpaPolicyClient>).evaluate;\n\tif (typeof eval_ === \"function\") {\n\t\treturn async (path, input) => ({\n\t\t\tdecision: await (service as OpaPolicyClient).evaluate(path, input),\n\t\t});\n\t}\n\treturn null;\n}\n\nfunction buildSuccessOutcome<TDb>(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\tdecision: OpaDecision,\n\texecution: OpaPolicyExecution,\n\tinputHash: string | undefined,\n\tctx: PolicyContext<TDb>,\n\tmapDecision: CreateOpaPolicyEvaluatorOptions<TDb>[\"mapDecision\"],\n): PolicyOutcome {\n\tconst selection = selectBundleRevision(execution, binding);\n\tconst mapped = mapDecision\n\t\t? mapDecision(decision, ctx, execution)\n\t\t: { result: decision.result, reason: decision.reason };\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: mapped.result ?? decision.result,\n\t\treason: mapped.reason ?? decision.reason,\n\t\tmetadata: {\n\t\t\t...(mapped.metadata ?? {}),\n\t\t\topaDecision: decision,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution.provenance,\n\t\t\t\t\tconditionResults: decision.conditionResults,\n\t\t\t\t\tfactsUsed: decision.factsUsed,\n\t\t\t\t\tobligations: decision.obligations,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction makeFailureOutcome(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\terror: OpaPolicyFailureKind,\n\treason: string,\n\tonError: \"block\" | \"throw\",\n\tinputHash?: string,\n\texecution?: OpaPolicyExecution,\n): PolicyOutcome {\n\tif (onError === \"throw\") {\n\t\tconst err = new Error(reason);\n\t\t(err as Error & { kind?: OpaPolicyFailureKind }).kind = error;\n\t\tthrow err;\n\t}\n\n\tconst selection = execution ? selectBundleRevision(execution, binding) : {};\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: \"block\",\n\t\treason,\n\t\tmetadata: {\n\t\t\topaError: error,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution?.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution?.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution?.provenance,\n\t\t\t\t\terror,\n\t\t\t\t\terrorReason: reason,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction stripUndefined(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(obj)) {\n\t\tif (v !== undefined) out[k] = v;\n\t}\n\treturn out;\n}\n\nfunction formatError(err: unknown): string {\n\tif (err instanceof Error) return err.message;\n\tif (typeof err === \"string\") return err;\n\ttry {\n\t\treturn JSON.stringify(err);\n\t} catch {\n\t\treturn String(err);\n\t}\n}\n","import type {\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n} from \"./types\";\n\n/**\n * Wrap an `OpaPolicyClient` (the high-level `@open-policy-agent/opa` SDK\n * shape) as an {@link OpaPolicyExecutor}. The resulting executor carries no\n * OPA-side provenance — the high-level SDK discards `decisionId` and\n * `provenance` from the response — but does centralize the\n * `(path, input) => execution` plumbing so hosts can choose at startup whether\n * to wire a client or a full executor.\n *\n * For full provenance (decisionId, bundleRevision, opaVersion), prefer\n * {@link executorFromOpaHttp}.\n */\nexport function executorFromOpaClient(client: OpaPolicyClient): OpaPolicyExecutor {\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst decision = await client.evaluate(path, input);\n\t\t\treturn { decision };\n\t\t},\n\t};\n}\n","import type { OpaPolicyExecution, OpaPolicyExecutor } from \"./types\";\n\nexport interface OpaHttpExecutorOptions {\n\t/**\n\t * Base URL of the OPA server, e.g. `\"http://localhost:8181\"` or\n\t * `\"https://opa.internal.example.com\"`. Trailing slash is permitted; the\n\t * executor normalizes it.\n\t */\n\tbaseUrl: string;\n\t/**\n\t * Override for `globalThis.fetch`. Lets the host inject a fetch instance\n\t * with retries, mTLS, OpenTelemetry instrumentation, etc. Defaults to\n\t * `globalThis.fetch`.\n\t */\n\tfetch?: typeof globalThis.fetch;\n\t/** Extra request headers (e.g. `Authorization`). */\n\theaders?: Record<string, string>;\n\t/**\n\t * Request OPA-side provenance (`decision_id`, `provenance.version`,\n\t * `provenance.bundles[*].revision`) via `?provenance=true`. Defaults to\n\t * `true` — turning it off forfeits OPA's contribution to audit evidence\n\t * and is rarely what you want.\n\t */\n\tprovenance?: boolean;\n}\n\ninterface OpaHttpResponseBody {\n\tresult?: unknown;\n\tdecision_id?: string;\n\tprovenance?: {\n\t\tversion?: string;\n\t\tbuild_commit?: string;\n\t\tbuild_timestamp?: string;\n\t\tbuild_host?: string;\n\t\tbundles?: Record<string, { revision?: string }>;\n\t};\n}\n\n/**\n * Build an {@link OpaPolicyExecutor} that talks to OPA's REST API directly.\n *\n * POSTs to `{baseUrl}/v1/data/{path}?provenance=true` with body\n * `{ \"input\": ... }`. Parses the snake_case response and lifts:\n *\n * - `result` → `execution.decision`\n * - `decision_id` → `execution.decisionId`\n * - `provenance.version` → `execution.opaVersion`\n * - `provenance` (whole block) → `execution.provenance`\n *\n * Bundle revision selection happens in the factory, not the executor, because\n * it depends on `binding.bundleName`.\n *\n * Calling this in environments without `globalThis.fetch` (older Node) throws\n * at call time; pass `fetch: nodeFetch` or `fetch: undici-fetch` etc. via the\n * options to support those runtimes explicitly.\n */\nexport function executorFromOpaHttp(opts: OpaHttpExecutorOptions): OpaPolicyExecutor {\n\tconst baseUrl = stripTrailingSlash(opts.baseUrl);\n\tconst provenance = opts.provenance ?? true;\n\tconst customFetch = opts.fetch;\n\tconst headers = {\n\t\t\"content-type\": \"application/json\",\n\t\taccept: \"application/json\",\n\t\t...(opts.headers ?? {}),\n\t};\n\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst fetchImpl = customFetch ?? globalThis.fetch;\n\t\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"executorFromOpaHttp: no fetch implementation available. Pass `fetch` in options or run on a runtime with `globalThis.fetch`.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst url = `${baseUrl}/v1/data/${stripLeadingSlash(path)}${\n\t\t\t\tprovenance ? \"?provenance=true\" : \"\"\n\t\t\t}`;\n\t\t\tconst response = await fetchImpl(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({ input }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await safeReadText(response);\n\t\t\t\tthrow new Error(`OPA HTTP ${response.status} at ${url}: ${truncate(text, 500)}`);\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as OpaHttpResponseBody;\n\n\t\t\treturn {\n\t\t\t\tdecision: body.result,\n\t\t\t\tdecisionId: body.decision_id,\n\t\t\t\topaVersion: body.provenance?.version,\n\t\t\t\tprovenance: body.provenance as Record<string, unknown> | undefined,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction stripTrailingSlash(s: string): string {\n\treturn s.replace(/\\/+$/, \"\");\n}\n\nfunction stripLeadingSlash(s: string): string {\n\treturn s.replace(/^\\/+/, \"\");\n}\n\nasync function safeReadText(response: Response): Promise<string> {\n\ttry {\n\t\treturn await response.text();\n\t} catch {\n\t\treturn \"<unreadable body>\";\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\treturn s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/bundle-selector.ts","../src/decision-schema.ts","../src/hash.ts","../src/factory.ts","../src/executor-client.ts","../src/executor-http.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { OpaPolicyBinding, OpaPolicyExecution } from \"./types\";\n\nexport interface BundleSelection {\n\tbundleRevision?: string;\n\t/**\n\t * Set to `\"ambiguous\"` when OPA reports multiple bundles and the binding\n\t * did not pick one with `bundleName`. The adapter records this on the\n\t * evidence so dashboards can flag policies that need explicit selectors.\n\t * `\"missing\"` is set when provenance was expected but absent.\n\t */\n\tbundleRevisionStatus?: \"ambiguous\" | \"missing\";\n}\n\n/**\n * Determine which OPA bundle revision applies to this decision.\n *\n * Rules (in order):\n * 1. If the executor already populated `execution.bundleRevision`, use it.\n * 2. If `binding.bundleName` is set, look it up in `provenance.bundles`.\n * 3. If exactly one bundle is reported, use its revision unambiguously.\n * 4. If multiple bundles are reported and no selector exists, mark\n * `bundleRevisionStatus: \"ambiguous\"`.\n * 5. If no provenance is reported at all, return `{}` — the caller decides\n * whether absence is meaningful (it is not, for executors that don't\n * carry provenance).\n */\nexport function selectBundleRevision(\n\texecution: OpaPolicyExecution,\n\tbinding: OpaPolicyBinding,\n): BundleSelection {\n\tif (typeof execution.bundleRevision === \"string\" && execution.bundleRevision.length > 0) {\n\t\treturn { bundleRevision: execution.bundleRevision };\n\t}\n\n\tconst bundles = readBundles(execution.provenance);\n\tif (!bundles) return {};\n\n\tconst names = Object.keys(bundles);\n\tif (names.length === 0) return {};\n\n\tif (binding.bundleName) {\n\t\tconst revision = bundles[binding.bundleName]?.revision;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\tif (names.length === 1) {\n\t\tconst only = names[0];\n\t\t// names.length === 1 guarantees names[0] is defined, but the\n\t\t// non-null assertion satisfies noUncheckedIndexedAccess if enabled.\n\t\tconst revision = only !== undefined ? bundles[only]?.revision : undefined;\n\t\treturn typeof revision === \"string\" && revision.length > 0\n\t\t\t? { bundleRevision: revision }\n\t\t\t: {};\n\t}\n\n\treturn { bundleRevisionStatus: \"ambiguous\" };\n}\n\nfunction readBundles(\n\tprovenance: Record<string, unknown> | undefined,\n): Record<string, { revision?: unknown }> | undefined {\n\tif (!provenance || typeof provenance !== \"object\") return undefined;\n\tconst b = (provenance as { bundles?: unknown }).bundles;\n\tif (!b || typeof b !== \"object\" || Array.isArray(b)) return undefined;\n\treturn b as Record<string, { revision?: unknown }>;\n}\n","import type { PolicyEvaluationResult } from \"@fabricorg/platform/policies\";\nimport type { OpaDecision } from \"./types\";\n\nconst POLICY_RESULTS: readonly PolicyEvaluationResult[] = [\"pass\", \"warn\", \"block\"];\n\nexport type OpaDecisionParseResult =\n\t| { ok: true; decision: OpaDecision }\n\t| { ok: false; error: string };\n\n/**\n * Hand-rolled validator for the Rego \"house style\" decision shape. Avoids a\n * runtime zod dependency to keep the adapter zero-dep.\n *\n * Accepts a raw OPA result and either returns the parsed `OpaDecision` or a\n * single human-readable error string describing the first violation. We don't\n * accumulate every error: when audit evidence has to record \"OPA returned\n * something invalid\", one precise reason is more useful than a list.\n */\nexport function parseOpaDecision(raw: unknown): OpaDecisionParseResult {\n\tif (raw === null || typeof raw !== \"object\") {\n\t\treturn { ok: false, error: \"decision must be an object\" };\n\t}\n\n\tconst d = raw as Record<string, unknown>;\n\n\tif (typeof d.result !== \"string\" || !POLICY_RESULTS.includes(d.result as PolicyEvaluationResult)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `result must be \"pass\" | \"warn\" | \"block\" (got ${JSON.stringify(d.result)})`,\n\t\t};\n\t}\n\n\tif (d.reason !== undefined && typeof d.reason !== \"string\") {\n\t\treturn { ok: false, error: \"reason must be a string when present\" };\n\t}\n\n\tif (d.conditionResults !== undefined) {\n\t\tif (!Array.isArray(d.conditionResults)) {\n\t\t\treturn { ok: false, error: \"conditionResults must be an array when present\" };\n\t\t}\n\t\tfor (let i = 0; i < d.conditionResults.length; i += 1) {\n\t\t\tconst err = validateConditionResult(d.conditionResults[i], i);\n\t\t\tif (err) return { ok: false, error: err };\n\t\t}\n\t}\n\n\tif (d.guidance !== undefined) {\n\t\tconst err = validateGuidance(d.guidance);\n\t\tif (err) return { ok: false, error: err };\n\t}\n\n\tif (d.obligations !== undefined && !isPlainObject(d.obligations)) {\n\t\treturn { ok: false, error: \"obligations must be an object when present\" };\n\t}\n\n\treturn { ok: true, decision: d as unknown as OpaDecision };\n}\n\nfunction validateConditionResult(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `conditionResults[${index}] must be an object`;\n\t}\n\tconst c = value as Record<string, unknown>;\n\tif (typeof c.conditionId !== \"string\" || c.conditionId.length === 0) {\n\t\treturn `conditionResults[${index}].conditionId must be a non-empty string`;\n\t}\n\tif (typeof c.passed !== \"boolean\") {\n\t\treturn `conditionResults[${index}].passed must be a boolean`;\n\t}\n\tif (typeof c.result !== \"string\" || !POLICY_RESULTS.includes(c.result as PolicyEvaluationResult)) {\n\t\treturn `conditionResults[${index}].result must be \"pass\" | \"warn\" | \"block\"`;\n\t}\n\tif (c.reason !== undefined && typeof c.reason !== \"string\") {\n\t\treturn `conditionResults[${index}].reason must be a string when present`;\n\t}\n\treturn null;\n}\n\nfunction validateGuidance(value: unknown): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn \"guidance must be an object when present\";\n\t}\n\tconst guidance = value as Record<string, unknown>;\n\tif (guidance.summary !== undefined && typeof guidance.summary !== \"string\") {\n\t\treturn \"guidance.summary must be a string when present\";\n\t}\n\tif (!Array.isArray(guidance.factsUsed)) {\n\t\treturn \"guidance.factsUsed must be an array\";\n\t}\n\tfor (let i = 0; i < guidance.factsUsed.length; i += 1) {\n\t\tconst err = validatePolicyFact(guidance.factsUsed[i], i);\n\t\tif (err) return err;\n\t}\n\tif (!Array.isArray(guidance.correctiveActions)) {\n\t\treturn \"guidance.correctiveActions must be an array\";\n\t}\n\tfor (let i = 0; i < guidance.correctiveActions.length; i += 1) {\n\t\tconst err = validateCorrectiveAction(guidance.correctiveActions[i], i);\n\t\tif (err) return err;\n\t}\n\treturn null;\n}\n\nfunction validatePolicyFact(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `guidance.factsUsed[${index}] must be an object`;\n\t}\n\tconst fact = value as Record<string, unknown>;\n\tif (typeof fact.name !== \"string\" || fact.name.length === 0) {\n\t\treturn `guidance.factsUsed[${index}].name must be a non-empty string`;\n\t}\n\tif (fact.label !== undefined && typeof fact.label !== \"string\") {\n\t\treturn `guidance.factsUsed[${index}].label must be a string when present`;\n\t}\n\treturn null;\n}\n\nfunction validateCorrectiveAction(value: unknown, index: number): string | null {\n\tif (!isPlainObject(value)) {\n\t\treturn `guidance.correctiveActions[${index}] must be an object`;\n\t}\n\tconst action = value as Record<string, unknown>;\n\tif (typeof action.label !== \"string\" || action.label.length === 0) {\n\t\treturn `guidance.correctiveActions[${index}].label must be a non-empty string`;\n\t}\n\tif (action.description !== undefined && typeof action.description !== \"string\") {\n\t\treturn `guidance.correctiveActions[${index}].description must be a string when present`;\n\t}\n\tif (action.actionId !== undefined && typeof action.actionId !== \"string\") {\n\t\treturn `guidance.correctiveActions[${index}].actionId must be a string when present`;\n\t}\n\tif (action.parameters !== undefined && !isPlainObject(action.parameters)) {\n\t\treturn `guidance.correctiveActions[${index}].parameters must be an object when present`;\n\t}\n\tif (action.target !== undefined && !isPlainObject(action.target)) {\n\t\treturn `guidance.correctiveActions[${index}].target must be an object when present`;\n\t}\n\tif (action.requiresConfirmation !== undefined && typeof action.requiresConfirmation !== \"boolean\") {\n\t\treturn `guidance.correctiveActions[${index}].requiresConfirmation must be a boolean when present`;\n\t}\n\treturn null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * Stable JSON stringify — sorts object keys recursively so two\n * structurally-equal inputs produce the same string regardless of property\n * insertion order. Required for hash determinism across builds of the same\n * `buildInput` function.\n */\nexport function stableStringify(value: unknown): string {\n\tif (value === undefined) return \"null\";\n\tif (value === null || typeof value !== \"object\") return JSON.stringify(value);\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map(stableStringify).join(\",\")}]`;\n\t}\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys\n\t\t.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n\t\t.join(\",\")}}`;\n}\n\n/**\n * Default input hasher: SHA-256 over a stable stringification.\n * Format: `sha256:<hex>` — namespaced so future migrations to a different\n * algorithm can be detected by a prefix check in evidence dashboards.\n */\nexport function defaultHashInput(input: Record<string, unknown>): string {\n\tconst hex = createHash(\"sha256\").update(stableStringify(input)).digest(\"hex\");\n\treturn `sha256:${hex}`;\n}\n","import type {\n\tPolicyContext,\n\tPolicyEvaluator,\n\tPolicyOutcome,\n} from \"@fabricorg/platform/policies\";\n\nimport { selectBundleRevision } from \"./bundle-selector\";\nimport { parseOpaDecision } from \"./decision-schema\";\nimport { defaultHashInput } from \"./hash\";\nimport type {\n\tCreateOpaPolicyEvaluatorOptions,\n\tOpaDecision,\n\tOpaPolicyBinding,\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n\tOpaPolicyFailureKind,\n} from \"./types\";\n\nconst DEFAULT_SERVICE_KEY = \"opa\";\nconst ENGINE_NAME = \"opa\";\n\n/**\n * Build a Fabric `PolicyEvaluator` that delegates decision-making to an OPA\n * (Open Policy Agent) deployment.\n *\n * The adapter accepts either of two service shapes under\n * `ctx.services[serviceKey]`:\n *\n * - `OpaPolicyExecutor` (preferred) — `execute(path, input)` returning a rich\n * `OpaPolicyExecution` (decision plus `decisionId`, `bundleRevision`,\n * `opaVersion`, `provenance`). Ship one with {@link executorFromOpaHttp} or\n * {@link executorFromOpaClient}.\n * - `OpaPolicyClient` — `evaluate(path, input)` returning the raw decision\n * only. Compatible with the high-level `@open-policy-agent/opa` SDK at the\n * cost of OPA-side provenance.\n *\n * If both methods are present, `execute` is preferred.\n *\n * The evaluator:\n *\n * 1. Resolves the service from `ctx.services[serviceKey]`.\n * 2. Builds a JSON input snapshot via `buildInput(ctx)` — domain code that\n * snapshots facts the engine needs. OPA never reaches into the database\n * directly; all facts flow through this function.\n * 3. Calls OPA at `binding.decisionPath`.\n * 4. Validates the response against the house-style shape (`OpaDecision`).\n * 5. Maps the decision to a `PolicyOutcome` and attaches engine provenance\n * under `dispatchEvidence.engine` — `name`, optional `version`, and\n * metadata covering `decisionPath`, `regoPackage`, `inputHash`, plus (when\n * the executor surfaces them) `decisionId`, `bundleName`, `bundleRevision`,\n * `bundleRevisionStatus`, and the raw `provenance` block.\n *\n * Failures (missing client, build failure, network failure, malformed\n * response) are governed by `onError`. Default `\"block\"` honors Fabric's\n * default-deny posture and records the failure category under\n * `engine.metadata.error`.\n */\nexport function createOpaPolicyEvaluator<TDb = unknown>(\n\toptions: CreateOpaPolicyEvaluatorOptions<TDb>,\n): PolicyEvaluator<TDb> {\n\tconst {\n\t\tbinding,\n\t\tbuildInput,\n\t\tserviceKey = DEFAULT_SERVICE_KEY,\n\t\tonError = \"block\",\n\t\tpreviewSafe = false,\n\t\tengineVersion,\n\t\tmapDecision,\n\t\thashInput = defaultHashInput,\n\t} = options;\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tversion: binding.version,\n\t\tpreviewSafe,\n\t\tevaluate: async (ctx) => {\n\t\t\tconst service = ctx.services?.[serviceKey];\n\t\t\tconst transport = resolveTransport(service);\n\t\t\tif (!transport) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"client_unavailable\",\n\t\t\t\t\t`No OPA client/executor at ctx.services[\"${serviceKey}\"]`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet input: Record<string, unknown>;\n\t\t\ttry {\n\t\t\t\tinput = await buildInput(ctx);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"input_build_failed\",\n\t\t\t\t\t`buildInput threw: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet inputHash: string | undefined;\n\t\t\ttry {\n\t\t\t\tinputHash = hashInput(input);\n\t\t\t} catch {\n\t\t\t\t// hashing is best-effort — if a custom hasher throws, omit the\n\t\t\t\t// field rather than fail the policy.\n\t\t\t}\n\n\t\t\tlet execution: OpaPolicyExecution;\n\t\t\ttry {\n\t\t\t\texecution = await transport(binding.decisionPath, input);\n\t\t\t} catch (err) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"evaluation_failed\",\n\t\t\t\t\t`OPA evaluation failed: ${formatError(err)}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst parsed = parseOpaDecision(execution.decision);\n\t\t\tif (!parsed.ok) {\n\t\t\t\treturn makeFailureOutcome(\n\t\t\t\t\tbinding,\n\t\t\t\t\tengineVersion,\n\t\t\t\t\t\"invalid_response\",\n\t\t\t\t\t`OPA returned malformed decision: ${parsed.error}`,\n\t\t\t\t\tonError,\n\t\t\t\t\tinputHash,\n\t\t\t\t\texecution,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn buildSuccessOutcome(\n\t\t\t\tbinding,\n\t\t\t\tengineVersion,\n\t\t\t\tparsed.decision,\n\t\t\t\texecution,\n\t\t\t\tinputHash,\n\t\t\t\tctx,\n\t\t\t\tmapDecision,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Discriminates between the rich executor shape and the legacy client shape.\n * Returns a uniform `(path, input) => OpaPolicyExecution` transport, or\n * `null` if the service doesn't implement either contract.\n *\n * `execute` is preferred when both are present so a v0.1 client wrapped by a\n * richer executor still wins.\n */\nfunction resolveTransport(\n\tservice: unknown,\n): ((path: string, input: unknown) => Promise<OpaPolicyExecution>) | null {\n\tif (!service || typeof service !== \"object\") return null;\n\tconst exec = (service as Partial<OpaPolicyExecutor>).execute;\n\tif (typeof exec === \"function\") {\n\t\treturn (path, input) => (service as OpaPolicyExecutor).execute(path, input);\n\t}\n\tconst eval_ = (service as Partial<OpaPolicyClient>).evaluate;\n\tif (typeof eval_ === \"function\") {\n\t\treturn async (path, input) => ({\n\t\t\tdecision: await (service as OpaPolicyClient).evaluate(path, input),\n\t\t});\n\t}\n\treturn null;\n}\n\nfunction buildSuccessOutcome<TDb>(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\tdecision: OpaDecision,\n\texecution: OpaPolicyExecution,\n\tinputHash: string | undefined,\n\tctx: PolicyContext<TDb>,\n\tmapDecision: CreateOpaPolicyEvaluatorOptions<TDb>[\"mapDecision\"],\n): PolicyOutcome {\n\tconst selection = selectBundleRevision(execution, binding);\n\tconst mapped = mapDecision\n\t\t? mapDecision(decision, ctx, execution)\n\t\t: { result: decision.result, reason: decision.reason };\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: mapped.result ?? decision.result,\n\t\treason: mapped.reason ?? decision.reason,\n\t\tguidance: mapped.guidance ?? decision.guidance,\n\t\tmetadata: {\n\t\t\t...(mapped.metadata ?? {}),\n\t\t\topaDecision: decision,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution.provenance,\n\t\t\t\t\tconditionResults: decision.conditionResults,\n\t\t\t\t\tobligations: decision.obligations,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction makeFailureOutcome(\n\tbinding: OpaPolicyBinding,\n\tengineVersion: string | undefined,\n\terror: OpaPolicyFailureKind,\n\treason: string,\n\tonError: \"block\" | \"throw\",\n\tinputHash?: string,\n\texecution?: OpaPolicyExecution,\n): PolicyOutcome {\n\tif (onError === \"throw\") {\n\t\tconst err = new Error(reason);\n\t\t(err as Error & { kind?: OpaPolicyFailureKind }).kind = error;\n\t\tthrow err;\n\t}\n\n\tconst selection = execution ? selectBundleRevision(execution, binding) : {};\n\n\treturn {\n\t\tpolicyId: binding.policyId,\n\t\tpolicyVersion: binding.version,\n\t\tresult: \"block\",\n\t\treason,\n\t\tguidance: {\n\t\t\tsummary: \"OPA policy evaluation could not complete.\",\n\t\t\tfactsUsed: [\n\t\t\t\t{ name: \"opaFailureKind\", value: error, label: \"Failure kind\" },\n\t\t\t\t{ name: \"decisionPath\", value: binding.decisionPath, label: \"Decision path\" },\n\t\t\t],\n\t\t\tcorrectiveActions: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Check OPA policy engine\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Verify the OPA service is reachable, serving the expected bundle, and returning the Fabric OPA decision shape.\",\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tmetadata: {\n\t\t\topaError: error,\n\t\t},\n\t\tdispatchEvidence: {\n\t\t\tpolicyKind: \"code\",\n\t\t\tpolicyId: binding.policyId,\n\t\t\tpolicyVersion: binding.version,\n\t\t\tdispatchPath: [\"code\"],\n\t\t\tengine: {\n\t\t\t\tname: ENGINE_NAME,\n\t\t\t\tversion: execution?.opaVersion ?? engineVersion,\n\t\t\t\tmetadata: stripUndefined({\n\t\t\t\t\tdecisionPath: binding.decisionPath,\n\t\t\t\t\tregoPackage: binding.regoPackage,\n\t\t\t\t\tinputHash,\n\t\t\t\t\tdecisionId: execution?.decisionId,\n\t\t\t\t\tbundleName: binding.bundleName,\n\t\t\t\t\tbundleRevision: selection.bundleRevision,\n\t\t\t\t\tbundleRevisionStatus: selection.bundleRevisionStatus,\n\t\t\t\t\tprovenance: execution?.provenance,\n\t\t\t\t\terror,\n\t\t\t\t\terrorReason: reason,\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t};\n}\n\nfunction stripUndefined(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(obj)) {\n\t\tif (v !== undefined) out[k] = v;\n\t}\n\treturn out;\n}\n\nfunction formatError(err: unknown): string {\n\tif (err instanceof Error) return err.message;\n\tif (typeof err === \"string\") return err;\n\ttry {\n\t\treturn JSON.stringify(err);\n\t} catch {\n\t\treturn String(err);\n\t}\n}\n","import type {\n\tOpaPolicyClient,\n\tOpaPolicyExecution,\n\tOpaPolicyExecutor,\n} from \"./types\";\n\n/**\n * Wrap an `OpaPolicyClient` (the high-level `@open-policy-agent/opa` SDK\n * shape) as an {@link OpaPolicyExecutor}. The resulting executor carries no\n * OPA-side provenance — the high-level SDK discards `decisionId` and\n * `provenance` from the response — but does centralize the\n * `(path, input) => execution` plumbing so hosts can choose at startup whether\n * to wire a client or a full executor.\n *\n * For full provenance (decisionId, bundleRevision, opaVersion), prefer\n * {@link executorFromOpaHttp}.\n */\nexport function executorFromOpaClient(client: OpaPolicyClient): OpaPolicyExecutor {\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst decision = await client.evaluate(path, input);\n\t\t\treturn { decision };\n\t\t},\n\t};\n}\n","import type { OpaPolicyExecution, OpaPolicyExecutor } from \"./types\";\n\nexport interface OpaHttpExecutorOptions {\n\t/**\n\t * Base URL of the OPA server, e.g. `\"http://localhost:8181\"` or\n\t * `\"https://opa.internal.example.com\"`. Trailing slash is permitted; the\n\t * executor normalizes it.\n\t */\n\tbaseUrl: string;\n\t/**\n\t * Override for `globalThis.fetch`. Lets the host inject a fetch instance\n\t * with retries, mTLS, OpenTelemetry instrumentation, etc. Defaults to\n\t * `globalThis.fetch`.\n\t */\n\tfetch?: typeof globalThis.fetch;\n\t/** Extra request headers (e.g. `Authorization`). */\n\theaders?: Record<string, string>;\n\t/**\n\t * Request OPA-side provenance (`decision_id`, `provenance.version`,\n\t * `provenance.bundles[*].revision`) via `?provenance=true`. Defaults to\n\t * `true` — turning it off forfeits OPA's contribution to audit evidence\n\t * and is rarely what you want.\n\t */\n\tprovenance?: boolean;\n}\n\ninterface OpaHttpResponseBody {\n\tresult?: unknown;\n\tdecision_id?: string;\n\tprovenance?: {\n\t\tversion?: string;\n\t\tbuild_commit?: string;\n\t\tbuild_timestamp?: string;\n\t\tbuild_host?: string;\n\t\tbundles?: Record<string, { revision?: string }>;\n\t};\n}\n\n/**\n * Build an {@link OpaPolicyExecutor} that talks to OPA's REST API directly.\n *\n * POSTs to `{baseUrl}/v1/data/{path}?provenance=true` with body\n * `{ \"input\": ... }`. Parses the snake_case response and lifts:\n *\n * - `result` → `execution.decision`\n * - `decision_id` → `execution.decisionId`\n * - `provenance.version` → `execution.opaVersion`\n * - `provenance` (whole block) → `execution.provenance`\n *\n * Bundle revision selection happens in the factory, not the executor, because\n * it depends on `binding.bundleName`.\n *\n * Calling this in environments without `globalThis.fetch` (older Node) throws\n * at call time; pass `fetch: nodeFetch` or `fetch: undici-fetch` etc. via the\n * options to support those runtimes explicitly.\n */\nexport function executorFromOpaHttp(opts: OpaHttpExecutorOptions): OpaPolicyExecutor {\n\tconst baseUrl = stripTrailingSlash(opts.baseUrl);\n\tconst provenance = opts.provenance ?? true;\n\tconst customFetch = opts.fetch;\n\tconst headers = {\n\t\t\"content-type\": \"application/json\",\n\t\taccept: \"application/json\",\n\t\t...(opts.headers ?? {}),\n\t};\n\n\treturn {\n\t\tasync execute(path: string, input: unknown): Promise<OpaPolicyExecution> {\n\t\t\tconst fetchImpl = customFetch ?? globalThis.fetch;\n\t\t\tif (typeof fetchImpl !== \"function\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"executorFromOpaHttp: no fetch implementation available. Pass `fetch` in options or run on a runtime with `globalThis.fetch`.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst url = `${baseUrl}/v1/data/${stripLeadingSlash(path)}${\n\t\t\t\tprovenance ? \"?provenance=true\" : \"\"\n\t\t\t}`;\n\t\t\tconst response = await fetchImpl(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({ input }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await safeReadText(response);\n\t\t\t\tthrow new Error(`OPA HTTP ${response.status} at ${url}: ${truncate(text, 500)}`);\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as OpaHttpResponseBody;\n\n\t\t\treturn {\n\t\t\t\tdecision: body.result,\n\t\t\t\tdecisionId: body.decision_id,\n\t\t\t\topaVersion: body.provenance?.version,\n\t\t\t\tprovenance: body.provenance as Record<string, unknown> | undefined,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction stripTrailingSlash(s: string): string {\n\treturn s.replace(/\\/+$/, \"\");\n}\n\nfunction stripLeadingSlash(s: string): string {\n\treturn s.replace(/^\\/+/, \"\");\n}\n\nasync function safeReadText(response: Response): Promise<string> {\n\ttry {\n\t\treturn await response.text();\n\t} catch {\n\t\treturn \"<unreadable body>\";\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\treturn s.length <= max ? s : `${s.slice(0, max)}…`;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,71 +1,72 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
2
|
+
"name": "@fabricorg/policy-opa",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Open Policy Agent (OPA) adapter for @fabricorg/platform. Wraps a Rego decision behind Fabric's PolicyEvaluator contract — Fabric keeps policyId.vN canonical for audit; engine evidence carries OPA provenance (decision path, input hash, bundle revision). Zero coupling to a specific OPA SDK; structurally typed client.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fabric",
|
|
7
|
+
"fabricorg",
|
|
8
|
+
"policy",
|
|
9
|
+
"opa",
|
|
10
|
+
"open-policy-agent",
|
|
11
|
+
"rego",
|
|
12
|
+
"authorization",
|
|
13
|
+
"policy-engine"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Fabric",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/Fabric-Pro/fabric-platform.git",
|
|
20
|
+
"directory": "packages/policy-opa"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/Fabric-Pro/fabric-platform#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/Fabric-Pro/fabric-platform/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/index.d.cts",
|
|
38
|
+
"default": "./dist/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"CHANGELOG.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"type-check": "tsc --noEmit",
|
|
52
|
+
"test": "vitest run --passWithNoTests",
|
|
53
|
+
"prepublishOnly": "pnpm clean && pnpm build && pnpm test"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@fabricorg/platform": "^0.4.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@fabricorg/platform": "workspace:*",
|
|
61
|
+
"@types/node": "^22.10.0",
|
|
62
|
+
"tsup": "^8.5.0",
|
|
63
|
+
"typescript": "^5.7.3",
|
|
64
|
+
"vitest": "^4.1.5"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=20"
|
|
68
|
+
},
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public"
|
|
71
|
+
}
|
|
72
|
+
}
|