@remnic/core 9.3.620 → 9.3.622
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/access-cli.js +19 -19
- package/dist/access-http.js +10 -10
- package/dist/access-mcp.js +9 -9
- package/dist/access-service.js +8 -8
- package/dist/active-recall.js +1 -1
- package/dist/briefing.js +4 -4
- package/dist/causal-consolidation.js +5 -5
- package/dist/{chunk-5BTCT236.js → chunk-2GRRN7SZ.js} +2 -2
- package/dist/{chunk-TNOWU6RP.js → chunk-3GM7ZY6H.js} +5 -5
- package/dist/{chunk-AZDOWD2L.js → chunk-4G2RQTAE.js} +2 -2
- package/dist/{chunk-C6C7XVKG.js → chunk-4MHHUPNH.js} +2 -2
- package/dist/{chunk-NM5NQYJE.js → chunk-4NS2ELXF.js} +3 -3
- package/dist/{chunk-T7N6KQGS.js → chunk-5GOMXHLC.js} +6 -1
- package/dist/chunk-5GOMXHLC.js.map +1 -0
- package/dist/{chunk-UEY3VB6W.js → chunk-6HMYUWXR.js} +13 -3
- package/dist/chunk-6HMYUWXR.js.map +1 -0
- package/dist/{chunk-3HPAPHUK.js → chunk-6KYMPV2O.js} +12 -11
- package/dist/chunk-6KYMPV2O.js.map +1 -0
- package/dist/{chunk-S53OYO3F.js → chunk-7VFZTJ7K.js} +2 -2
- package/dist/{chunk-4RR6ROTB.js → chunk-AGNBY3VG.js} +2 -2
- package/dist/{chunk-OBIRVF36.js → chunk-AVHPSLQ2.js} +2 -2
- package/dist/{chunk-JQDZQ4TB.js → chunk-BMFZLLNI.js} +2 -2
- package/dist/{chunk-QPD426WT.js → chunk-CCNZM5UM.js} +2 -2
- package/dist/{chunk-QVO4YOB7.js → chunk-D2B22JDF.js} +2 -2
- package/dist/{chunk-7MLB4NCL.js → chunk-DXBCNDVD.js} +2 -2
- package/dist/{chunk-EXUAP5LH.js → chunk-EDP57PFC.js} +10 -10
- package/dist/{chunk-ZK32E74R.js → chunk-EOLCAPOU.js} +7 -7
- package/dist/{chunk-X4QQB7O6.js → chunk-FH3PPO42.js} +3 -3
- package/dist/{chunk-APRRL26Q.js → chunk-GA3PMY73.js} +2 -2
- package/dist/{chunk-IEUU7O4F.js → chunk-LANHQ7EN.js} +2 -2
- package/dist/{chunk-B6SU7YSE.js → chunk-LMZ7XQBB.js} +5 -5
- package/dist/{chunk-7XYTQGCC.js → chunk-MAV46GWQ.js} +2 -2
- package/dist/{chunk-KILOTVIF.js → chunk-MB5RSUW6.js} +2 -2
- package/dist/{chunk-CIOMS6DI.js → chunk-UCGCSZP2.js} +2 -2
- package/dist/{chunk-GLPBYIXN.js → chunk-UK727RHF.js} +2 -2
- package/dist/{chunk-KGLPJROV.js → chunk-VNR3K2R3.js} +17 -17
- package/dist/{chunk-5OHHEORR.js → chunk-XQUIHXNI.js} +3 -3
- package/dist/{chunk-PP2JH3GP.js → chunk-YPNGPHNZ.js} +2 -2
- package/dist/{chunk-DEPRLVLK.js → chunk-ZNCDQZIS.js} +2 -2
- package/dist/{chunk-VMGLYN42.js → chunk-ZUNNG6PC.js} +12 -7
- package/dist/chunk-ZUNNG6PC.js.map +1 -0
- package/dist/cli.js +23 -23
- package/dist/compounding/engine.js +4 -4
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.js +4 -4
- package/dist/connectors/index.js +4 -4
- package/dist/entity-retrieval.js +4 -4
- package/dist/index.js +30 -30
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.js +3 -3
- package/dist/lcm/schema.js +2 -2
- package/dist/maintenance/memory-governance.js +4 -4
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -4
- package/dist/maintenance/rebuild-memory-projection.js +5 -5
- package/dist/memory-projection-store.js +2 -2
- package/dist/namespaces/migrate.js +5 -5
- package/dist/namespaces/storage.js +4 -4
- package/dist/objective-state-writers.js +2 -2
- package/dist/objective-state.js +1 -1
- package/dist/operator-toolkit.js +8 -8
- package/dist/orchestrator.js +14 -14
- package/dist/resume-bundles.js +3 -3
- package/dist/runtime/better-sqlite.d.ts +2 -1
- package/dist/runtime/better-sqlite.js +3 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/semantic-consolidation.js +5 -5
- package/dist/semantic-rule-promotion.js +4 -4
- package/dist/semantic-rule-verifier.js +4 -4
- package/dist/storage.js +3 -3
- package/dist/transfer/export-sqlite.js +2 -2
- package/dist/transfer/import-sqlite.js +2 -2
- package/dist/transfer/types.d.ts +12 -12
- package/dist/verified-recall.js +4 -4
- package/package.json +1 -1
- package/src/config.ts +5 -0
- package/src/namespaces/storage.ts +30 -5
- package/src/objective-state.ts +17 -2
- package/src/runtime/better-sqlite.test.ts +29 -0
- package/src/runtime/better-sqlite.ts +30 -8
- package/dist/chunk-3HPAPHUK.js.map +0 -1
- package/dist/chunk-T7N6KQGS.js.map +0 -1
- package/dist/chunk-UEY3VB6W.js.map +0 -1
- package/dist/chunk-VMGLYN42.js.map +0 -1
- /package/dist/{chunk-5BTCT236.js.map → chunk-2GRRN7SZ.js.map} +0 -0
- /package/dist/{chunk-TNOWU6RP.js.map → chunk-3GM7ZY6H.js.map} +0 -0
- /package/dist/{chunk-AZDOWD2L.js.map → chunk-4G2RQTAE.js.map} +0 -0
- /package/dist/{chunk-C6C7XVKG.js.map → chunk-4MHHUPNH.js.map} +0 -0
- /package/dist/{chunk-NM5NQYJE.js.map → chunk-4NS2ELXF.js.map} +0 -0
- /package/dist/{chunk-S53OYO3F.js.map → chunk-7VFZTJ7K.js.map} +0 -0
- /package/dist/{chunk-4RR6ROTB.js.map → chunk-AGNBY3VG.js.map} +0 -0
- /package/dist/{chunk-OBIRVF36.js.map → chunk-AVHPSLQ2.js.map} +0 -0
- /package/dist/{chunk-JQDZQ4TB.js.map → chunk-BMFZLLNI.js.map} +0 -0
- /package/dist/{chunk-QPD426WT.js.map → chunk-CCNZM5UM.js.map} +0 -0
- /package/dist/{chunk-QVO4YOB7.js.map → chunk-D2B22JDF.js.map} +0 -0
- /package/dist/{chunk-7MLB4NCL.js.map → chunk-DXBCNDVD.js.map} +0 -0
- /package/dist/{chunk-EXUAP5LH.js.map → chunk-EDP57PFC.js.map} +0 -0
- /package/dist/{chunk-ZK32E74R.js.map → chunk-EOLCAPOU.js.map} +0 -0
- /package/dist/{chunk-X4QQB7O6.js.map → chunk-FH3PPO42.js.map} +0 -0
- /package/dist/{chunk-APRRL26Q.js.map → chunk-GA3PMY73.js.map} +0 -0
- /package/dist/{chunk-IEUU7O4F.js.map → chunk-LANHQ7EN.js.map} +0 -0
- /package/dist/{chunk-B6SU7YSE.js.map → chunk-LMZ7XQBB.js.map} +0 -0
- /package/dist/{chunk-7XYTQGCC.js.map → chunk-MAV46GWQ.js.map} +0 -0
- /package/dist/{chunk-KILOTVIF.js.map → chunk-MB5RSUW6.js.map} +0 -0
- /package/dist/{chunk-CIOMS6DI.js.map → chunk-UCGCSZP2.js.map} +0 -0
- /package/dist/{chunk-GLPBYIXN.js.map → chunk-UK727RHF.js.map} +0 -0
- /package/dist/{chunk-KGLPJROV.js.map → chunk-VNR3K2R3.js.map} +0 -0
- /package/dist/{chunk-5OHHEORR.js.map → chunk-XQUIHXNI.js.map} +0 -0
- /package/dist/{chunk-PP2JH3GP.js.map → chunk-YPNGPHNZ.js.map} +0 -0
- /package/dist/{chunk-DEPRLVLK.js.map → chunk-ZNCDQZIS.js.map} +0 -0
package/dist/transfer/types.d.ts
CHANGED
|
@@ -313,13 +313,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
|
|
|
313
313
|
peerProfiles: boolean;
|
|
314
314
|
}>;
|
|
315
315
|
}, "strip", z.ZodTypeAny, {
|
|
316
|
+
schemaVersion: string;
|
|
316
317
|
includes: {
|
|
317
318
|
procedural: boolean;
|
|
318
319
|
taxonomy: boolean;
|
|
319
320
|
identityAnchors: boolean;
|
|
320
321
|
peerProfiles: boolean;
|
|
321
322
|
};
|
|
322
|
-
schemaVersion: string;
|
|
323
323
|
id: string;
|
|
324
324
|
description: string;
|
|
325
325
|
version: string;
|
|
@@ -334,13 +334,13 @@ declare const CapsuleBlockSchema: z.ZodObject<{
|
|
|
334
334
|
directAnswerEnabled: boolean;
|
|
335
335
|
};
|
|
336
336
|
}, {
|
|
337
|
+
schemaVersion: string;
|
|
337
338
|
includes: {
|
|
338
339
|
procedural: boolean;
|
|
339
340
|
taxonomy: boolean;
|
|
340
341
|
identityAnchors: boolean;
|
|
341
342
|
peerProfiles: boolean;
|
|
342
343
|
};
|
|
343
|
-
schemaVersion: string;
|
|
344
344
|
id: string;
|
|
345
345
|
description: string;
|
|
346
346
|
version: string;
|
|
@@ -464,13 +464,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
464
464
|
peerProfiles: boolean;
|
|
465
465
|
}>;
|
|
466
466
|
}, "strip", z.ZodTypeAny, {
|
|
467
|
+
schemaVersion: string;
|
|
467
468
|
includes: {
|
|
468
469
|
procedural: boolean;
|
|
469
470
|
taxonomy: boolean;
|
|
470
471
|
identityAnchors: boolean;
|
|
471
472
|
peerProfiles: boolean;
|
|
472
473
|
};
|
|
473
|
-
schemaVersion: string;
|
|
474
474
|
id: string;
|
|
475
475
|
description: string;
|
|
476
476
|
version: string;
|
|
@@ -485,13 +485,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
485
485
|
directAnswerEnabled: boolean;
|
|
486
486
|
};
|
|
487
487
|
}, {
|
|
488
|
+
schemaVersion: string;
|
|
488
489
|
includes: {
|
|
489
490
|
procedural: boolean;
|
|
490
491
|
taxonomy: boolean;
|
|
491
492
|
identityAnchors: boolean;
|
|
492
493
|
peerProfiles: boolean;
|
|
493
494
|
};
|
|
494
|
-
schemaVersion: string;
|
|
495
495
|
id: string;
|
|
496
496
|
description: string;
|
|
497
497
|
version: string;
|
|
@@ -518,13 +518,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
518
518
|
pluginVersion: string;
|
|
519
519
|
includesTranscripts: boolean;
|
|
520
520
|
capsule: {
|
|
521
|
+
schemaVersion: string;
|
|
521
522
|
includes: {
|
|
522
523
|
procedural: boolean;
|
|
523
524
|
taxonomy: boolean;
|
|
524
525
|
identityAnchors: boolean;
|
|
525
526
|
peerProfiles: boolean;
|
|
526
527
|
};
|
|
527
|
-
schemaVersion: string;
|
|
528
528
|
id: string;
|
|
529
529
|
description: string;
|
|
530
530
|
version: string;
|
|
@@ -551,13 +551,13 @@ declare const ExportManifestV2Schema: z.ZodObject<{
|
|
|
551
551
|
pluginVersion: string;
|
|
552
552
|
includesTranscripts: boolean;
|
|
553
553
|
capsule: {
|
|
554
|
+
schemaVersion: string;
|
|
554
555
|
includes: {
|
|
555
556
|
procedural: boolean;
|
|
556
557
|
taxonomy: boolean;
|
|
557
558
|
identityAnchors: boolean;
|
|
558
559
|
peerProfiles: boolean;
|
|
559
560
|
};
|
|
560
|
-
schemaVersion: string;
|
|
561
561
|
id: string;
|
|
562
562
|
description: string;
|
|
563
563
|
version: string;
|
|
@@ -683,13 +683,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
683
683
|
peerProfiles: boolean;
|
|
684
684
|
}>;
|
|
685
685
|
}, "strip", z.ZodTypeAny, {
|
|
686
|
+
schemaVersion: string;
|
|
686
687
|
includes: {
|
|
687
688
|
procedural: boolean;
|
|
688
689
|
taxonomy: boolean;
|
|
689
690
|
identityAnchors: boolean;
|
|
690
691
|
peerProfiles: boolean;
|
|
691
692
|
};
|
|
692
|
-
schemaVersion: string;
|
|
693
693
|
id: string;
|
|
694
694
|
description: string;
|
|
695
695
|
version: string;
|
|
@@ -704,13 +704,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
704
704
|
directAnswerEnabled: boolean;
|
|
705
705
|
};
|
|
706
706
|
}, {
|
|
707
|
+
schemaVersion: string;
|
|
707
708
|
includes: {
|
|
708
709
|
procedural: boolean;
|
|
709
710
|
taxonomy: boolean;
|
|
710
711
|
identityAnchors: boolean;
|
|
711
712
|
peerProfiles: boolean;
|
|
712
713
|
};
|
|
713
|
-
schemaVersion: string;
|
|
714
714
|
id: string;
|
|
715
715
|
description: string;
|
|
716
716
|
version: string;
|
|
@@ -737,13 +737,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
737
737
|
pluginVersion: string;
|
|
738
738
|
includesTranscripts: boolean;
|
|
739
739
|
capsule: {
|
|
740
|
+
schemaVersion: string;
|
|
740
741
|
includes: {
|
|
741
742
|
procedural: boolean;
|
|
742
743
|
taxonomy: boolean;
|
|
743
744
|
identityAnchors: boolean;
|
|
744
745
|
peerProfiles: boolean;
|
|
745
746
|
};
|
|
746
|
-
schemaVersion: string;
|
|
747
747
|
id: string;
|
|
748
748
|
description: string;
|
|
749
749
|
version: string;
|
|
@@ -770,13 +770,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
770
770
|
pluginVersion: string;
|
|
771
771
|
includesTranscripts: boolean;
|
|
772
772
|
capsule: {
|
|
773
|
+
schemaVersion: string;
|
|
773
774
|
includes: {
|
|
774
775
|
procedural: boolean;
|
|
775
776
|
taxonomy: boolean;
|
|
776
777
|
identityAnchors: boolean;
|
|
777
778
|
peerProfiles: boolean;
|
|
778
779
|
};
|
|
779
|
-
schemaVersion: string;
|
|
780
780
|
id: string;
|
|
781
781
|
description: string;
|
|
782
782
|
version: string;
|
|
@@ -815,13 +815,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
815
815
|
pluginVersion: string;
|
|
816
816
|
includesTranscripts: boolean;
|
|
817
817
|
capsule: {
|
|
818
|
+
schemaVersion: string;
|
|
818
819
|
includes: {
|
|
819
820
|
procedural: boolean;
|
|
820
821
|
taxonomy: boolean;
|
|
821
822
|
identityAnchors: boolean;
|
|
822
823
|
peerProfiles: boolean;
|
|
823
824
|
};
|
|
824
|
-
schemaVersion: string;
|
|
825
825
|
id: string;
|
|
826
826
|
description: string;
|
|
827
827
|
version: string;
|
|
@@ -854,13 +854,13 @@ declare const ExportBundleV2Schema: z.ZodObject<{
|
|
|
854
854
|
pluginVersion: string;
|
|
855
855
|
includesTranscripts: boolean;
|
|
856
856
|
capsule: {
|
|
857
|
+
schemaVersion: string;
|
|
857
858
|
includes: {
|
|
858
859
|
procedural: boolean;
|
|
859
860
|
taxonomy: boolean;
|
|
860
861
|
identityAnchors: boolean;
|
|
861
862
|
peerProfiles: boolean;
|
|
862
863
|
};
|
|
863
|
-
schemaVersion: string;
|
|
864
864
|
id: string;
|
|
865
865
|
description: string;
|
|
866
866
|
version: string;
|
package/dist/verified-recall.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
searchVerifiedEpisodes
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CCNZM5UM.js";
|
|
4
4
|
import "./chunk-HQ6NIBL6.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-DXBCNDVD.js";
|
|
6
6
|
import "./chunk-5UZXUTVO.js";
|
|
7
7
|
import "./chunk-4H5ZJHEN.js";
|
|
8
8
|
import "./chunk-4R4KTDIE.js";
|
|
9
9
|
import "./chunk-RULE4VG5.js";
|
|
10
10
|
import "./chunk-SCU65EZI.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-MB5RSUW6.js";
|
|
12
|
+
import "./chunk-6KYMPV2O.js";
|
|
13
13
|
import "./chunk-CPPS65WS.js";
|
|
14
14
|
import "./chunk-DM2T26WE.js";
|
|
15
15
|
import "./chunk-QSVPYQPG.js";
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -2537,6 +2537,11 @@ export function parseConfig(raw: unknown): PluginConfig {
|
|
|
2537
2537
|
|
|
2538
2538
|
// v3.0 namespaces (default off)
|
|
2539
2539
|
namespacesEnabled: cfg.namespacesEnabled === true,
|
|
2540
|
+
// NOTE: namespace identifiers are intentionally NOT sanitized here — the
|
|
2541
|
+
// codebase rejects unsafe namespaces at the point of use (see
|
|
2542
|
+
// codex-materialize-runner and NamespaceStorageRouter / resolveNamespaceDir),
|
|
2543
|
+
// so a "../x" value is surfaced as an explicit error rather than silently
|
|
2544
|
+
// rewritten. Containment is enforced at the filesystem sinks.
|
|
2540
2545
|
defaultNamespace:
|
|
2541
2546
|
typeof cfg.defaultNamespace === "string" && cfg.defaultNamespace.length > 0 ? cfg.defaultNamespace : "default",
|
|
2542
2547
|
sharedNamespace:
|
|
@@ -32,6 +32,28 @@ async function hasStoredEntries(p: string): Promise<boolean> {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// Build a per-namespace directory under `<memoryDir>/namespaces` and assert the
|
|
36
|
+
// resolved path stays inside that base. Namespace identifiers can originate from
|
|
37
|
+
// operator config (config.defaultNamespace) and request-derived routing, so this
|
|
38
|
+
// containment check prevents directory traversal (CodeQL js/path-injection).
|
|
39
|
+
// For safe segments this returns exactly `path.join(base, segment)`, so there is
|
|
40
|
+
// no behavioral change for valid namespaces.
|
|
41
|
+
function resolveNamespaceDir(memoryDir: string, segment: string): string {
|
|
42
|
+
// Mirror isSafeRouteNamespace's separator/parent-ref rejection (without its
|
|
43
|
+
// 64-char cap, so identity tokens still pass). Rejecting separators and ".."
|
|
44
|
+
// up front keeps the value a single contained child of <memoryDir>/namespaces.
|
|
45
|
+
if (
|
|
46
|
+
segment.length === 0 ||
|
|
47
|
+
segment.includes("/") ||
|
|
48
|
+
segment.includes("\\") ||
|
|
49
|
+
segment.includes("..") ||
|
|
50
|
+
path.isAbsolute(segment)
|
|
51
|
+
) {
|
|
52
|
+
throw new Error(`unsafe namespace path segment: ${segment}`);
|
|
53
|
+
}
|
|
54
|
+
return path.join(memoryDir, "namespaces", segment);
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
const LEGACY_NAMESPACE_CONTENT_CHILDREN = [
|
|
36
58
|
...ALL_CATEGORY_DIRS,
|
|
37
59
|
"entities",
|
|
@@ -94,10 +116,9 @@ export class NamespaceStorageRouter {
|
|
|
94
116
|
return this.defaultNsRootResolved;
|
|
95
117
|
}
|
|
96
118
|
|
|
97
|
-
const legacyNsDir =
|
|
98
|
-
const tokenizedNsDir =
|
|
119
|
+
const legacyNsDir = resolveNamespaceDir(this.config.memoryDir, this.config.defaultNamespace);
|
|
120
|
+
const tokenizedNsDir = resolveNamespaceDir(
|
|
99
121
|
this.config.memoryDir,
|
|
100
|
-
"namespaces",
|
|
101
122
|
namespaceIdentityToken(this.config.defaultNamespace),
|
|
102
123
|
);
|
|
103
124
|
const tokenizedHasData =
|
|
@@ -118,8 +139,8 @@ export class NamespaceStorageRouter {
|
|
|
118
139
|
if (namespace === this.config.defaultNamespace) {
|
|
119
140
|
return this.defaultNsRootResolved ?? this.config.memoryDir;
|
|
120
141
|
}
|
|
121
|
-
const legacyRoot =
|
|
122
|
-
const tokenizedRoot =
|
|
142
|
+
const legacyRoot = resolveNamespaceDir(this.config.memoryDir, namespace);
|
|
143
|
+
const tokenizedRoot = resolveNamespaceDir(this.config.memoryDir, namespaceIdentityToken(namespace));
|
|
123
144
|
if ((await exists(tokenizedRoot)) && (await hasAnyNamespaceStorageMarker(tokenizedRoot, { includeRuntimeState: true }))) {
|
|
124
145
|
return tokenizedRoot;
|
|
125
146
|
}
|
|
@@ -131,6 +152,10 @@ export class NamespaceStorageRouter {
|
|
|
131
152
|
if (ns !== this.config.defaultNamespace && !isSafeRouteNamespace(ns)) {
|
|
132
153
|
throw new Error(`unsafe namespace: ${ns}`);
|
|
133
154
|
}
|
|
155
|
+
// Even when the default namespace is exempt from the check above, every
|
|
156
|
+
// on-disk path is built through resolveNamespaceDir(), which rejects
|
|
157
|
+
// traversal segments — so an unsafe configured default still cannot escape
|
|
158
|
+
// <memoryDir>/namespaces (CodeQL js/path-injection).
|
|
134
159
|
|
|
135
160
|
let root: string;
|
|
136
161
|
if (ns === this.config.defaultNamespace) {
|
package/src/objective-state.ts
CHANGED
|
@@ -86,6 +86,20 @@ function validateMetadata(raw: unknown): Record<string, string> | undefined {
|
|
|
86
86
|
return validateStringRecord(raw, "metadata");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
// Assert that a built path stays inside the expected base directory before it is
|
|
90
|
+
// used in a filesystem write. snapshotId/recordedAt are already validated by
|
|
91
|
+
// validateObjectiveStateSnapshot, so for valid data this is a defense-in-depth
|
|
92
|
+
// barrier (and makes the containment provable to CodeQL js/path-injection).
|
|
93
|
+
function assertWithinDir(baseDir: string, candidate: string): string {
|
|
94
|
+
const resolvedBase = path.resolve(baseDir);
|
|
95
|
+
const resolved = path.resolve(candidate);
|
|
96
|
+
const rel = path.relative(resolvedBase, resolved);
|
|
97
|
+
if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
|
|
98
|
+
throw new Error("objective-state path escapes the snapshots directory");
|
|
99
|
+
}
|
|
100
|
+
return resolved;
|
|
101
|
+
}
|
|
102
|
+
|
|
89
103
|
export function resolveObjectiveStateStoreDir(memoryDir: string, overrideDir?: string): string {
|
|
90
104
|
if (typeof overrideDir === "string" && overrideDir.trim().length > 0) {
|
|
91
105
|
return overrideDir.trim();
|
|
@@ -163,8 +177,9 @@ export async function recordObjectiveStateSnapshot(options: {
|
|
|
163
177
|
const rootDir = resolveObjectiveStateStoreDir(options.memoryDir, options.objectiveStateStoreDir);
|
|
164
178
|
const validated = validateObjectiveStateSnapshot(options.snapshot);
|
|
165
179
|
const day = recordStoreDay(validated.recordedAt);
|
|
166
|
-
const
|
|
167
|
-
const
|
|
180
|
+
const snapshotsRoot = path.join(rootDir, "snapshots");
|
|
181
|
+
const snapshotsDir = assertWithinDir(snapshotsRoot, path.join(snapshotsRoot, day));
|
|
182
|
+
const filePath = assertWithinDir(snapshotsDir, path.join(snapshotsDir, `${validated.snapshotId}.json`));
|
|
168
183
|
await mkdir(snapshotsDir, { recursive: true });
|
|
169
184
|
await writeFile(filePath, JSON.stringify(validated, null, 2), "utf8");
|
|
170
185
|
return filePath;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import {
|
|
4
|
+
displayErrorDetail,
|
|
4
5
|
isLikelyBetterSqlite3NativeBindingError,
|
|
5
6
|
openBetterSqlite3,
|
|
6
7
|
} from "./better-sqlite.js";
|
|
@@ -21,6 +22,34 @@ test("isLikelyBetterSqlite3NativeBindingError recognizes missing and mismatched
|
|
|
21
22
|
assert.equal(isLikelyBetterSqlite3NativeBindingError(new Error("SQLITE_BUSY: database is locked")), false);
|
|
22
23
|
});
|
|
23
24
|
|
|
25
|
+
test("displayErrorDetail surfaces only error class + code, never the raw message (CodeQL js/stack-trace-exposure)", () => {
|
|
26
|
+
// MODULE_NOT_FOUND messages embed an absolute "Require stack:" path block.
|
|
27
|
+
const moduleNotFound = Object.assign(
|
|
28
|
+
new Error("Cannot find module 'better-sqlite3'\nRequire stack:\n- /home/app/node_modules/x/index.js"),
|
|
29
|
+
{ code: "MODULE_NOT_FOUND" },
|
|
30
|
+
);
|
|
31
|
+
const d1 = displayErrorDetail(moduleNotFound);
|
|
32
|
+
assert.equal(d1, "Error (MODULE_NOT_FOUND)");
|
|
33
|
+
assert.ok(!d1.includes("/home/app") && !d1.includes("Require stack"));
|
|
34
|
+
|
|
35
|
+
// Native loader failures can embed an absolute path (even with spaces) in the
|
|
36
|
+
// message; we never surface it.
|
|
37
|
+
const dlopen = Object.assign(
|
|
38
|
+
new Error("/Users/Jane Doe/app/node_modules/better-sqlite3/build/Release/better_sqlite3.node: file too short"),
|
|
39
|
+
{ code: "ERR_DLOPEN_FAILED" },
|
|
40
|
+
);
|
|
41
|
+
const d2 = displayErrorDetail(dlopen);
|
|
42
|
+
assert.equal(d2, "Error (ERR_DLOPEN_FAILED)");
|
|
43
|
+
assert.ok(!d2.includes("/Users/Jane Doe") && !d2.includes(".node"));
|
|
44
|
+
|
|
45
|
+
// No code → class name only. error.stack is never read.
|
|
46
|
+
const noCode = new Error("boom");
|
|
47
|
+
noCode.stack = "boom\n at /home/app/secret.js:1:1";
|
|
48
|
+
assert.equal(displayErrorDetail(noCode), "Error");
|
|
49
|
+
|
|
50
|
+
assert.equal(displayErrorDetail("not an error"), "");
|
|
51
|
+
});
|
|
52
|
+
|
|
24
53
|
test("openBetterSqlite3 can open an in-memory database after install verification", () => {
|
|
25
54
|
const db = openBetterSqlite3(":memory:");
|
|
26
55
|
try {
|
|
@@ -41,8 +41,19 @@ function requireBetterSqlite3Ctor(require: RuntimeRequire): BetterSqlite3Ctor {
|
|
|
41
41
|
return ctor;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Raw, unredacted message — used ONLY for internal classification (detecting a
|
|
45
|
+
// native-binding mismatch). Never returned to a user-facing surface, because it
|
|
46
|
+
// can contain absolute paths. Native-binding markers (better_sqlite3.node,
|
|
47
|
+
// NODE_MODULE_VERSION, "was compiled against a different Node.js version") live
|
|
48
|
+
// in error.message, so message text is sufficient and we never read .stack.
|
|
49
|
+
function rawErrorMessage(error: unknown): string {
|
|
50
|
+
return error instanceof Error ? error.message : String(error ?? "");
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
export function isLikelyBetterSqlite3NativeBindingError(error: unknown): boolean {
|
|
45
|
-
|
|
54
|
+
// Classify on the RAW message so redaction can't strip detection markers
|
|
55
|
+
// (e.g. the path containing "better_sqlite3.node").
|
|
56
|
+
const detail = rawErrorMessage(error);
|
|
46
57
|
return (
|
|
47
58
|
detail.includes("Could not locate the bindings file") ||
|
|
48
59
|
detail.includes("better_sqlite3.node") ||
|
|
@@ -53,7 +64,7 @@ export function isLikelyBetterSqlite3NativeBindingError(error: unknown): boolean
|
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
function unavailableError(error: unknown): Error {
|
|
56
|
-
const detail =
|
|
67
|
+
const detail = displayErrorDetail(error);
|
|
57
68
|
const nativeBindingHint = isLikelyBetterSqlite3NativeBindingError(error)
|
|
58
69
|
? " This usually means the better-sqlite3 native binding was not compiled for this Node.js/platform combination. " +
|
|
59
70
|
"Run `node scripts/ensure-better-sqlite3.mjs` from the Remnic install directory, or run " +
|
|
@@ -67,10 +78,21 @@ function unavailableError(error: unknown): Error {
|
|
|
67
78
|
);
|
|
68
79
|
}
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
// Sanitized, user-facing error detail. This string becomes the message of the
|
|
82
|
+
// Error thrown by unavailableError(), which propagates to user-facing surfaces
|
|
83
|
+
// (HTTP error bodies, MCP tool errors — access-http.ts / access-mcp.ts return
|
|
84
|
+
// err.message). We must not leak server internals (CodeQL js/stack-trace-exposure):
|
|
85
|
+
// - error.stack is never read.
|
|
86
|
+
// We deliberately surface only the error's class name and Node error code —
|
|
87
|
+
// never the raw message. Node module-load failures embed absolute server paths
|
|
88
|
+
// directly in error.message (the "Require stack:" block, and unquoted native
|
|
89
|
+
// loader paths that may even contain spaces), which no regex can redact
|
|
90
|
+
// reliably. The error code (MODULE_NOT_FOUND, ERR_DLOPEN_FAILED, …) is a stable,
|
|
91
|
+
// path-free identifier that, together with the native-binding hint, is enough
|
|
92
|
+
// for a user to act on. The full original error stays on the `cause` chain and
|
|
93
|
+
// is logged with its stack elsewhere.
|
|
94
|
+
export function displayErrorDetail(error: unknown): string {
|
|
95
|
+
if (!(error instanceof Error)) return "";
|
|
96
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
97
|
+
return typeof code === "string" && code.length > 0 ? `${error.name} (${code})` : error.name;
|
|
76
98
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/better-sqlite.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport type BetterSqlite3 from \"better-sqlite3\";\n\nexport type BetterSqlite3Database = BetterSqlite3.Database;\ntype BetterSqlite3Ctor = typeof BetterSqlite3;\ntype RuntimeRequire = ReturnType<typeof createRequire>;\n\nlet cachedCtor: BetterSqlite3Ctor | null = null;\n\nfunction loadBetterSqlite3(): BetterSqlite3Ctor {\n if (cachedCtor) return cachedCtor;\n\n const require = createRequire(import.meta.url);\n\n try {\n cachedCtor = requireBetterSqlite3Ctor(require);\n return cachedCtor;\n } catch (error) {\n throw unavailableError(error);\n }\n}\n\nexport function openBetterSqlite3(\n file: string,\n options?: ConstructorParameters<BetterSqlite3Ctor>[1],\n): BetterSqlite3Database {\n const Database = loadBetterSqlite3();\n return new Database(file, options);\n}\n\nfunction requireBetterSqlite3Ctor(require: RuntimeRequire): BetterSqlite3Ctor {\n const loaded = require(\"better-sqlite3\") as\n | BetterSqlite3Ctor\n | { default?: BetterSqlite3Ctor };\n const ctor = typeof loaded === \"function\" ? loaded : loaded.default;\n\n if (typeof ctor !== \"function\") {\n throw new Error(\"module did not export a constructor\");\n }\n\n return ctor;\n}\n\nexport function isLikelyBetterSqlite3NativeBindingError(error: unknown): boolean {\n const detail = errorDetail(error);\n return (\n detail.includes(\"Could not locate the bindings file\") ||\n detail.includes(\"better_sqlite3.node\") ||\n (detail.includes(\"node-v\") && detail.includes(\"better-sqlite3\")) ||\n (detail.includes(\"NODE_MODULE_VERSION\") && detail.includes(\"better-sqlite3\")) ||\n detail.includes(\"was compiled against a different Node.js version\")\n );\n}\n\nfunction unavailableError(error: unknown): Error {\n const detail = errorDetail(error);\n const nativeBindingHint = isLikelyBetterSqlite3NativeBindingError(error)\n ? \" This usually means the better-sqlite3 native binding was not compiled for this Node.js/platform combination. \" +\n \"Run `node scripts/ensure-better-sqlite3.mjs` from the Remnic install directory, or run \" +\n \"`npx node-gyp rebuild --directory=node_modules/better-sqlite3` if the verification script is unavailable.\"\n : \"\";\n return new Error(\n \"better-sqlite3 is unavailable. Remnic attempted to load the native SQLite binding and could not.\" +\n nativeBindingHint +\n (detail ? ` Original error: ${detail}` : \"\"),\n { cause: error instanceof Error ? error : undefined },\n );\n}\n\nfunction errorDetail(error: unknown): string {\n if (error instanceof Error) {\n const stack = error.stack && error.stack !== error.message ? `\\n${error.stack}` : \"\";\n return `${error.message}${stack}`;\n }\n return String(error ?? \"\");\n}\n"],"mappings":";AAAA,SAAS,qBAAqB;AAO9B,IAAI,aAAuC;AAE3C,SAAS,oBAAuC;AAC9C,MAAI,WAAY,QAAO;AAEvB,QAAMA,WAAU,cAAc,YAAY,GAAG;AAE7C,MAAI;AACF,iBAAa,yBAAyBA,QAAO;AAC7C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,iBAAiB,KAAK;AAAA,EAC9B;AACF;AAEO,SAAS,kBACd,MACA,SACuB;AACvB,QAAM,WAAW,kBAAkB;AACnC,SAAO,IAAI,SAAS,MAAM,OAAO;AACnC;AAEA,SAAS,yBAAyBA,UAA4C;AAC5E,QAAM,SAASA,SAAQ,gBAAgB;AAGvC,QAAM,OAAO,OAAO,WAAW,aAAa,SAAS,OAAO;AAE5D,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,SAAO;AACT;AAEO,SAAS,wCAAwC,OAAyB;AAC/E,QAAM,SAAS,YAAY,KAAK;AAChC,SACE,OAAO,SAAS,oCAAoC,KACpD,OAAO,SAAS,qBAAqB,KACpC,OAAO,SAAS,QAAQ,KAAK,OAAO,SAAS,gBAAgB,KAC7D,OAAO,SAAS,qBAAqB,KAAK,OAAO,SAAS,gBAAgB,KAC3E,OAAO,SAAS,kDAAkD;AAEtE;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,QAAM,SAAS,YAAY,KAAK;AAChC,QAAM,oBAAoB,wCAAwC,KAAK,IACnE,mTAGA;AACJ,SAAO,IAAI;AAAA,IACT,qGACE,qBACC,SAAS,oBAAoB,MAAM,KAAK;AAAA,IAC3C,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,OAAU;AAAA,EACtD;AACF;AAEA,SAAS,YAAY,OAAwB;AAC3C,MAAI,iBAAiB,OAAO;AAC1B,UAAM,QAAQ,MAAM,SAAS,MAAM,UAAU,MAAM,UAAU;AAAA,EAAK,MAAM,KAAK,KAAK;AAClF,WAAO,GAAG,MAAM,OAAO,GAAG,KAAK;AAAA,EACjC;AACA,SAAO,OAAO,SAAS,EAAE;AAC3B;","names":["require"]}
|