@ifc-lite/lists 2.1.0 → 2.1.2
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/dist/name-pattern.d.ts +32 -12
- package/dist/name-pattern.d.ts.map +1 -1
- package/dist/name-pattern.js +71 -0
- package/dist/name-pattern.js.map +1 -1
- package/package.json +4 -3
package/dist/name-pattern.d.ts
CHANGED
|
@@ -1,22 +1,42 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Property-set / property NAME matching for list columns and queries.
|
|
3
|
-
*
|
|
4
|
-
* A name wrapped in slashes (`/Qto_.*BaseQuantities/`, optionally with trailing
|
|
5
|
-
* flags such as `/qto_.+/i`) is treated as a regular expression matched against
|
|
6
|
-
* the candidate name; anything else is an exact, case-sensitive string match
|
|
7
|
-
* (the historical behaviour). This lets one column / query pull a value from
|
|
8
|
-
* several property or quantity sets at once — e.g. `NetVolume` from
|
|
9
|
-
* `Qto_WallBaseQuantities` AND `Qto_SlabBaseQuantities` — the way Bonsai's
|
|
10
|
-
* `/regex/` syntax works (issue #1591). IFC set / property names never contain
|
|
11
|
-
* slashes, so the `/.../` form is unambiguous.
|
|
12
|
-
*/
|
|
13
1
|
type NameMatcher = (name: string) => boolean;
|
|
14
2
|
/** True when `pattern` uses the `/regex/` form (a valid slash-delimited literal). */
|
|
15
3
|
export declare function isNamePattern(pattern: string): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Returns a human-readable rejection reason when `source` is not safe to
|
|
6
|
+
* compile and run `.test()` with, or `undefined` when it's fine. Pure, never
|
|
7
|
+
* throws — delegates to `@ifc-lite/regex-guard`'s `assertGuardedRegexPattern`
|
|
8
|
+
* so this package shares the same catastrophic-backtracking-shape and
|
|
9
|
+
* length-cap checks as `@ifc-lite/ids`, `@ifc-lite/extensions` and
|
|
10
|
+
* `@ifc-lite/mutations`, instead of carrying its own copy. Exposed so a
|
|
11
|
+
* caller that wants to reject a dangerous pattern WITHOUT triggering
|
|
12
|
+
* `compileNameMatcher`'s throw (e.g. a live "does this pattern look right"
|
|
13
|
+
* preview) can check first.
|
|
14
|
+
*/
|
|
15
|
+
export declare function unsafeNamePatternReason(source: string): string | undefined;
|
|
16
16
|
/**
|
|
17
17
|
* Compile a name pattern into a predicate. `/body/flags` compiles to a RegExp;
|
|
18
18
|
* anything else (including a malformed literal, which is logged) becomes an
|
|
19
19
|
* exact, case-sensitive match.
|
|
20
|
+
*
|
|
21
|
+
* `compileNameMatcher` hands its returned predicate straight to `.test()`
|
|
22
|
+
* against untrusted data: a viewer list column, an SDK `psetName`/`propName`
|
|
23
|
+
* argument, or an LLM/agent-authored sandbox script's own call arguments
|
|
24
|
+
* (`packages/sandbox/src/bridge-query.ts`'s `property` tool forwards its
|
|
25
|
+
* args unmodified into `sdk.property` → `compileNameMatcher`, and the regex
|
|
26
|
+
* compiles and runs on the HOST's main thread, outside the QuickJS sandbox —
|
|
27
|
+
* a pattern the sandboxed script "owns" can hang the real browser tab). For
|
|
28
|
+
* that reason, before compiling a `/regex/` literal's body, this runs it
|
|
29
|
+
* through `@ifc-lite/regex-guard`'s `assertGuardedRegexPattern`, which
|
|
30
|
+
* rejects the well-known catastrophic-backtracking shapes and overlong
|
|
31
|
+
* patterns.
|
|
32
|
+
*
|
|
33
|
+
* Throws a plain `Error` — naming the pattern and the reason — when the body
|
|
34
|
+
* is syntactically valid but rejected by the guard: unlike a malformed
|
|
35
|
+
* literal, there is no safe fallback here, because the whole point of
|
|
36
|
+
* rejecting it is to never call `.test()` with it. A caller that cannot let
|
|
37
|
+
* an exception propagate (the sandbox bridge, in particular) MUST catch
|
|
38
|
+
* this — see the callers' own comments for why a throw is safe to cross
|
|
39
|
+
* that boundary.
|
|
20
40
|
*/
|
|
21
41
|
export declare function compileNameMatcher(pattern: string): NameMatcher;
|
|
22
42
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"name-pattern.d.ts","sourceRoot":"","sources":["../src/name-pattern.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"name-pattern.d.ts","sourceRoot":"","sources":["../src/name-pattern.ts"],"names":[],"mappings":"AAmBA,KAAK,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;AAU7C,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ1E;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAoB/D"}
|
package/dist/name-pattern.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Property-set / property NAME matching for list columns and queries.
|
|
6
|
+
*
|
|
7
|
+
* A name wrapped in slashes (`/Qto_.*BaseQuantities/`, optionally with trailing
|
|
8
|
+
* flags such as `/qto_.+/i`) is treated as a regular expression matched against
|
|
9
|
+
* the candidate name; anything else is an exact, case-sensitive string match
|
|
10
|
+
* (the historical behaviour). This lets one column / query pull a value from
|
|
11
|
+
* several property or quantity sets at once — e.g. `NetVolume` from
|
|
12
|
+
* `Qto_WallBaseQuantities` AND `Qto_SlabBaseQuantities` — the way Bonsai's
|
|
13
|
+
* `/regex/` syntax works (issue #1591). IFC set / property names never contain
|
|
14
|
+
* slashes, so the `/.../` form is unambiguous.
|
|
15
|
+
*/
|
|
16
|
+
import { assertGuardedRegexPattern, UnsafeRegexPatternError } from '@ifc-lite/regex-guard';
|
|
4
17
|
// Compiled matchers are cached by pattern string: `findPropertyEntry` /
|
|
5
18
|
// `findQuantityEntry` run per row, and the pattern is fixed per column, so
|
|
6
19
|
// without this a regex column would recompile its RegExp for every element.
|
|
@@ -12,16 +25,69 @@ const matcherCache = new Map();
|
|
|
12
25
|
export function isNamePattern(pattern) {
|
|
13
26
|
return parseRegexLiteral(pattern) !== null;
|
|
14
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns a human-readable rejection reason when `source` is not safe to
|
|
30
|
+
* compile and run `.test()` with, or `undefined` when it's fine. Pure, never
|
|
31
|
+
* throws — delegates to `@ifc-lite/regex-guard`'s `assertGuardedRegexPattern`
|
|
32
|
+
* so this package shares the same catastrophic-backtracking-shape and
|
|
33
|
+
* length-cap checks as `@ifc-lite/ids`, `@ifc-lite/extensions` and
|
|
34
|
+
* `@ifc-lite/mutations`, instead of carrying its own copy. Exposed so a
|
|
35
|
+
* caller that wants to reject a dangerous pattern WITHOUT triggering
|
|
36
|
+
* `compileNameMatcher`'s throw (e.g. a live "does this pattern look right"
|
|
37
|
+
* preview) can check first.
|
|
38
|
+
*/
|
|
39
|
+
export function unsafeNamePatternReason(source) {
|
|
40
|
+
try {
|
|
41
|
+
assertGuardedRegexPattern(source);
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof UnsafeRegexPatternError)
|
|
46
|
+
return err.reason;
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
15
50
|
/**
|
|
16
51
|
* Compile a name pattern into a predicate. `/body/flags` compiles to a RegExp;
|
|
17
52
|
* anything else (including a malformed literal, which is logged) becomes an
|
|
18
53
|
* exact, case-sensitive match.
|
|
54
|
+
*
|
|
55
|
+
* `compileNameMatcher` hands its returned predicate straight to `.test()`
|
|
56
|
+
* against untrusted data: a viewer list column, an SDK `psetName`/`propName`
|
|
57
|
+
* argument, or an LLM/agent-authored sandbox script's own call arguments
|
|
58
|
+
* (`packages/sandbox/src/bridge-query.ts`'s `property` tool forwards its
|
|
59
|
+
* args unmodified into `sdk.property` → `compileNameMatcher`, and the regex
|
|
60
|
+
* compiles and runs on the HOST's main thread, outside the QuickJS sandbox —
|
|
61
|
+
* a pattern the sandboxed script "owns" can hang the real browser tab). For
|
|
62
|
+
* that reason, before compiling a `/regex/` literal's body, this runs it
|
|
63
|
+
* through `@ifc-lite/regex-guard`'s `assertGuardedRegexPattern`, which
|
|
64
|
+
* rejects the well-known catastrophic-backtracking shapes and overlong
|
|
65
|
+
* patterns.
|
|
66
|
+
*
|
|
67
|
+
* Throws a plain `Error` — naming the pattern and the reason — when the body
|
|
68
|
+
* is syntactically valid but rejected by the guard: unlike a malformed
|
|
69
|
+
* literal, there is no safe fallback here, because the whole point of
|
|
70
|
+
* rejecting it is to never call `.test()` with it. A caller that cannot let
|
|
71
|
+
* an exception propagate (the sandbox bridge, in particular) MUST catch
|
|
72
|
+
* this — see the callers' own comments for why a throw is safe to cross
|
|
73
|
+
* that boundary.
|
|
19
74
|
*/
|
|
20
75
|
export function compileNameMatcher(pattern) {
|
|
21
76
|
const cached = matcherCache.get(pattern);
|
|
22
77
|
if (cached)
|
|
23
78
|
return cached;
|
|
24
79
|
const re = parseRegexLiteral(pattern);
|
|
80
|
+
if (re) {
|
|
81
|
+
try {
|
|
82
|
+
assertGuardedRegexPattern(re.source);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (err instanceof UnsafeRegexPatternError) {
|
|
86
|
+
throw new Error(`[lists] rejected name pattern ${JSON.stringify(pattern)}: ${err.reason}`);
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
25
91
|
const matcher = re ? (name) => re.test(name) : (name) => name === pattern;
|
|
26
92
|
if (matcherCache.size >= CACHE_CAP)
|
|
27
93
|
matcherCache.clear();
|
|
@@ -32,6 +98,11 @@ export function compileNameMatcher(pattern) {
|
|
|
32
98
|
* Parse a `/body/flags` regex literal, or return null for a plain name. A
|
|
33
99
|
* malformed literal is NOT silently swallowed: it's logged and treated as a
|
|
34
100
|
* plain name (so it matches only itself), keeping behaviour predictable.
|
|
101
|
+
*
|
|
102
|
+
* This only validates SYNTAX (does it compile at all) — `isNamePattern` uses
|
|
103
|
+
* it as-is, unaffected by the ReDoS guard above, because merely
|
|
104
|
+
* *constructing* a RegExp never runs it and so cannot ReDoS; only
|
|
105
|
+
* `compileNameMatcher`'s returned predicate ever calls `.test()`.
|
|
35
106
|
*/
|
|
36
107
|
function parseRegexLiteral(pattern) {
|
|
37
108
|
const m = /^\/(.+)\/([a-z]*)$/.exec(pattern);
|
package/dist/name-pattern.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"name-pattern.js","sourceRoot":"","sources":["../src/name-pattern.ts"],"names":[],"mappings":"AAAA;;+DAE+D;
|
|
1
|
+
{"version":3,"file":"name-pattern.js","sourceRoot":"","sources":["../src/name-pattern.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAI3F,wEAAwE;AACxE,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAC/E,qEAAqE;AACrE,MAAM,SAAS,GAAG,GAAG,CAAC;AACtB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;AAEpD,qFAAqF;AACrF,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,iBAAiB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,IAAI,CAAC;QACH,yBAAyB,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,uBAAuB;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC;QAC9D,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,EAAE,EAAE,CAAC;QACP,IAAI,CAAC;YACH,yBAAyB,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,uBAAuB,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7F,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC;IAEvF,IAAI,YAAY,CAAC,IAAI,IAAI,SAAS;QAAE,YAAY,CAAC,KAAK,EAAE,CAAC;IACzD,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,OAAe;IACxC,MAAM,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC;QACH,uEAAuE;QACvE,uEAAuE;QACvE,wEAAwE;QACxE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,gCAAgC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACnG,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifc-lite/lists",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Configurable property tables and schedules from IFC data for IFC-Lite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@ifc-lite/
|
|
17
|
-
"@ifc-lite/
|
|
16
|
+
"@ifc-lite/encoding": "2.2.0",
|
|
17
|
+
"@ifc-lite/data": "4.2.0",
|
|
18
|
+
"@ifc-lite/regex-guard": "0.2.0"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"csv-parse": "7.0.2",
|