@visiq/core-wasm 0.1.6 → 0.1.8
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/README.md +21 -11
- package/index.d.ts +8 -0
- package/index.js +116 -0
- package/package.json +3 -2
- package/pkg/visiq_core_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -7,28 +7,38 @@ of a parallel TS implementation that can drift.
|
|
|
7
7
|
|
|
8
8
|
```js
|
|
9
9
|
const { evaluate } = require('@visiq/core-wasm');
|
|
10
|
-
|
|
10
|
+
// PLAIN OBJECTS IN, PLAIN OBJECT OUT — `evaluate` does the JSON marshalling
|
|
11
|
+
// itself. Do NOT pre-stringify: `evaluate(JSON.stringify(event), …)` double-
|
|
12
|
+
// encodes, the core sees a JSON *string* where an object belongs, and the
|
|
13
|
+
// decision comes back with `allowed: true` for an event policy DENIES.
|
|
14
|
+
const decision = evaluate(event, bundle);
|
|
15
|
+
if (!decision.allowed) {
|
|
16
|
+
// blocked by policy — do not execute the tool call
|
|
17
|
+
}
|
|
11
18
|
```
|
|
12
19
|
|
|
13
20
|
- **Synchronous, no async init.** `--target nodejs` glue instantiates the wasm from bytes
|
|
14
21
|
at `require` time (`new WebAssembly.Module`/`Instance`) — a drop-in for a sync call.
|
|
15
22
|
(Node/server only; a browser build would need `--target web` + async init.)
|
|
16
|
-
- **`evaluate(
|
|
17
|
-
|
|
23
|
+
- **`evaluate(event, bundle) → decision`** takes and returns plain objects; the
|
|
24
|
+
language-neutral seam underneath is `evaluate_json` (JSON string in, JSON string out),
|
|
25
|
+
and this package's wrapper is what stringifies/parses around it. Fail-closed (G001) on
|
|
26
|
+
malformed input — never throws.
|
|
18
27
|
- **Faithful.** `node conformance.mjs` proves it reproduces the frozen golden corpus
|
|
19
28
|
(`sdk-corpus/GOLDEN-oracle-vectors.json`) — the same vectors every other binding
|
|
20
|
-
reproduces. **
|
|
29
|
+
reproduces. **118/118, 0 diverge.**
|
|
21
30
|
|
|
22
31
|
## Rebuild
|
|
23
32
|
|
|
24
33
|
`./build.sh` (needs rustup + `wasm32-unknown-unknown` + `wasm-bindgen-cli` **=0.2.100**;
|
|
25
34
|
the crate pins `wasm-bindgen = "=0.2.100"` so CLI/schema versions match).
|
|
26
35
|
|
|
27
|
-
## Scope
|
|
36
|
+
## Scope
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
`reason` prose,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
`evaluate` returns the full decision object — `{decision, allowed, reason, ruleId,
|
|
39
|
+
ruleCode, enforced, agentMode, action:{decision,argRedactionRules},
|
|
40
|
+
retrieval:{action,redactionRules}}`. Matched-rule attribution (`reason` prose,
|
|
41
|
+
`ruleId`, `ruleCode`) IS emitted in-core; `decisionId` is not — it is minted by the
|
|
42
|
+
caller. `@visiq/runtime` declares this package a REQUIRED dependency and calls it the
|
|
43
|
+
sole decision engine, so the harness cutover is done, not pending. Remaining seams are
|
|
44
|
+
tracked in `experiments/matrix-v2/visiq-core-rs/PARITY-GAPS.md`.
|
package/index.d.ts
CHANGED
|
@@ -93,3 +93,11 @@ export function recursiveKeyPaths(event: unknown): string[];
|
|
|
93
93
|
export function deriveDeclaredCore(samples: unknown[], opts?: unknown): { requiredPaths: string[]; optionalPaths: string[] };
|
|
94
94
|
export function shouldMintNewVersion(prev: string[] | null | undefined, next: string[]): boolean;
|
|
95
95
|
export function sanitizeAgentReason(reason: string | null | undefined): string;
|
|
96
|
+
/**
|
|
97
|
+
* Does the CORE compile this `regex.match` pattern? The arbiter of what a
|
|
98
|
+
* persisted rule can evaluate is this engine (`Regex::new`, RE2 dialect), NOT
|
|
99
|
+
* JavaScript's `RegExp` — see the implementation note in `index.js` for how the
|
|
100
|
+
* verdict is derived from the core's own decision path (no new Rust export, no
|
|
101
|
+
* re-stamp) and for the fail-closed edges (raw newline, non-string, throw).
|
|
102
|
+
*/
|
|
103
|
+
export function patternCompiles(pattern: string): boolean;
|
package/index.js
CHANGED
|
@@ -63,7 +63,123 @@ const deriveDeclaredCore = (samples, opts) => call('derive_declared_core', { sam
|
|
|
63
63
|
const shouldMintNewVersion = (prev, next) => call('should_mint_new_version', { prev: prev ?? null, next });
|
|
64
64
|
const sanitizeAgentReason = (reason) => call('sanitize_agent_reason', { reason });
|
|
65
65
|
|
|
66
|
+
// ── regex compilability, ARBITRATED BY THE CORE ────────────────────────────
|
|
67
|
+
/**
|
|
68
|
+
* Does THE SHIPPED CORE compile this `regex.match` pattern?
|
|
69
|
+
*
|
|
70
|
+
* WHY THIS EXISTS. The rule write guard
|
|
71
|
+
* (`@visiq/rego-evaluator`'s `collectUninterpretableRegexPatterns`) has to
|
|
72
|
+
* decide whether a pattern an author is about to persist can fire at all. The
|
|
73
|
+
* engine that decides that in production is THIS core — `regex::Regex::new`
|
|
74
|
+
* (Rust regex, the RE2 dialect Rego's `regex.match` is specified over), whose
|
|
75
|
+
* `Err` the evaluator maps to `false`, so a `deny` carrying an uncompilable
|
|
76
|
+
* pattern PERMITS. Asking JavaScript's `RegExp` instead is wrong in BOTH
|
|
77
|
+
* directions and was measured wrong on 2026-07-31: `delete(?=_prod)`,
|
|
78
|
+
* `(?<=x)delete` and `(a)\1` are JS-valid and RE2-INVALID (accepted by the
|
|
79
|
+
* guard, dead in production), while `(?P<verb>del)ete` and `[[:alpha:]]+` are
|
|
80
|
+
* RE2-VALID and JS-invalid-or-different (refused by the guard, working in
|
|
81
|
+
* production).
|
|
82
|
+
*
|
|
83
|
+
* HOW IT ASKS, without a new Rust export or a wasm re-stamp. The core has no
|
|
84
|
+
* "compile this pattern" op, and adding one means editing the Rust core,
|
|
85
|
+
* re-stamping the corpus and regenerating every binding. It does not need one:
|
|
86
|
+
* compilability is DERIVABLE from the op the core already exposes. Evaluate a
|
|
87
|
+
* one-rule bundle whose body is `regex.match("|<pattern>", input.action)`. The
|
|
88
|
+
* leading empty alternative matches at position 0 of ANY subject, so:
|
|
89
|
+
*
|
|
90
|
+
* pattern compiles -> the whole pattern compiles -> matches -> matched:true
|
|
91
|
+
* pattern does not -> `Regex::new` errs -> condition false -> matched:false
|
|
92
|
+
*
|
|
93
|
+
* i.e. the verdict is the CORE's own `Regex::new`, read through the core's own
|
|
94
|
+
* decision path. Nothing here re-implements or enumerates a dialect difference.
|
|
95
|
+
*
|
|
96
|
+
* FAIL-CLOSED EDGES, stated rather than implied. A pattern carrying a raw
|
|
97
|
+
* newline or carriage return cannot be expressed in a single-line rego probe,
|
|
98
|
+
* so it is reported as NOT compilable rather than guessed at. (The rego parser
|
|
99
|
+
* is line-based, so such a pattern cannot reach a parsed condition in the first
|
|
100
|
+
* place.) A non-string, or any throw from the core, is likewise `false`.
|
|
101
|
+
*
|
|
102
|
+
* The pattern is a DECODED regex source (post `\\`/`\"` rego unescaping); it is
|
|
103
|
+
* re-encoded here with exactly the inverse the engines' `unescape_rego_string`
|
|
104
|
+
* applies, so the core sees the identical string back.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} pattern decoded regex source
|
|
107
|
+
* @returns {boolean} true iff the core compiles it
|
|
108
|
+
*/
|
|
109
|
+
const PATTERN_PROBE_INPUT = {
|
|
110
|
+
agentId: 'regex-probe',
|
|
111
|
+
agent: { trust_tier: null, categories: [], business_function: null },
|
|
112
|
+
facets: ['action'],
|
|
113
|
+
// A non-null targetApp is REQUIRED: with `targetApp: null` the core's
|
|
114
|
+
// action-facet prefilter drops the rule and the probe reads 'no match',
|
|
115
|
+
// which would report every pattern as uncompilable.
|
|
116
|
+
targetApp: 'visiq-regex-probe',
|
|
117
|
+
action: 'visiq-regex-compilability-probe',
|
|
118
|
+
context: {},
|
|
119
|
+
operation: null,
|
|
120
|
+
resourceType: null,
|
|
121
|
+
resourceMetadata: null,
|
|
122
|
+
trustTier: null,
|
|
123
|
+
surface: null,
|
|
124
|
+
query: null,
|
|
125
|
+
normalized: {},
|
|
126
|
+
capabilities: { transformableResponse: false },
|
|
127
|
+
};
|
|
128
|
+
const patternProbeCache = new Map();
|
|
129
|
+
function patternCompiles(pattern) {
|
|
130
|
+
if (typeof pattern !== 'string') {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const cached = patternProbeCache.get(pattern);
|
|
134
|
+
if (cached !== undefined) {
|
|
135
|
+
return cached;
|
|
136
|
+
}
|
|
137
|
+
let verdict = false;
|
|
138
|
+
if (!/[\n\r]/.test(pattern)) {
|
|
139
|
+
const encoded = `|${pattern}`.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
140
|
+
const rego = `deny { regex.match("${encoded}", input.action) }`;
|
|
141
|
+
try {
|
|
142
|
+
const res = evaluateUnifiedFromInput(
|
|
143
|
+
PATTERN_PROBE_INPUT,
|
|
144
|
+
{
|
|
145
|
+
rules: [
|
|
146
|
+
{
|
|
147
|
+
id: 'regex-probe',
|
|
148
|
+
origin_table: 'rules',
|
|
149
|
+
vendor_id: 'regex-probe',
|
|
150
|
+
name: 'regex-probe',
|
|
151
|
+
description: null,
|
|
152
|
+
rego_source: rego,
|
|
153
|
+
priority: 1,
|
|
154
|
+
enabled: true,
|
|
155
|
+
applies_to: ['action'],
|
|
156
|
+
target_app: null,
|
|
157
|
+
action_pattern: null,
|
|
158
|
+
trust_tier: null,
|
|
159
|
+
surface: null,
|
|
160
|
+
principal_exclusions: null,
|
|
161
|
+
bypass_active: false,
|
|
162
|
+
bypass_reason: null,
|
|
163
|
+
bypass_expires_at: null,
|
|
164
|
+
redaction_spec: null,
|
|
165
|
+
fail_closed_on_empty_spec: false,
|
|
166
|
+
rule_code: 'REGEX-PROBE',
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
0,
|
|
171
|
+
);
|
|
172
|
+
verdict = res.matched === true && res.decision === 'deny';
|
|
173
|
+
} catch {
|
|
174
|
+
verdict = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
patternProbeCache.set(pattern, verdict);
|
|
178
|
+
return verdict;
|
|
179
|
+
}
|
|
180
|
+
|
|
66
181
|
module.exports = {
|
|
182
|
+
patternCompiles,
|
|
67
183
|
evaluate,
|
|
68
184
|
evaluateUnifiedRulesRaw,
|
|
69
185
|
evaluateUnifiedFromInput,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visiq/core-wasm",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "VisIQ governance core
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"description": "VisIQ governance core for Node.js — the one compiled Rust rule engine behind every VisIQ SDK, prebuilt to WebAssembly; evaluate(event, bundle) takes plain objects and synchronously returns a decision object.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"visiq",
|
|
20
20
|
"ai-agent",
|
|
21
21
|
"governance",
|
|
22
|
+
"agent-governance",
|
|
22
23
|
"authorization",
|
|
23
24
|
"wasm",
|
|
24
25
|
"policy"
|
package/pkg/visiq_core_bg.wasm
CHANGED
|
Binary file
|