@openparachute/vault 0.7.0-rc.7 → 0.7.0-rc.9
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/core/src/core.test.ts +895 -0
- package/core/src/cursor.ts +1 -0
- package/core/src/mcp.ts +439 -49
- package/core/src/notes.ts +39 -4
- package/core/src/seed-packs.test.ts +14 -0
- package/core/src/seed-packs.ts +23 -0
- package/core/src/store.ts +22 -15
- package/core/src/tag-schemas.ts +115 -19
- package/core/src/types.ts +9 -0
- package/core/src/wikilinks.test.ts +113 -1
- package/core/src/wikilinks.ts +230 -21
- package/package.json +1 -1
- package/src/contract-errors.test.ts +73 -0
- package/src/mcp-http.test.ts +140 -0
- package/src/mcp-http.ts +73 -16
- package/src/mcp-tools.ts +36 -3
- package/src/routes.ts +363 -39
- package/src/tag-field-conflict-scope.test.ts +44 -0
- package/src/tag-scope.ts +60 -0
- package/src/vault.test.ts +745 -4
package/src/tag-scope.ts
CHANGED
|
@@ -83,6 +83,66 @@ export function filterNotesByTagScope<T extends Note>(
|
|
|
83
83
|
return notes.filter((n) => noteWithinTagScope(n, allowed, rawRoots));
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Is a SINGLE tag name visible to this token? The per-tag core of
|
|
88
|
+
* `noteWithinTagScope` — exact allowlist membership OR string-form root
|
|
89
|
+
* match — pulled out so the `validation_status` scrub below (which reasons
|
|
90
|
+
* about a warning's `schema`/`loser_schema` tag NAMES, not a note's whole
|
|
91
|
+
* tag set) uses the identical visibility rule. `rawRoots === null` (unscoped)
|
|
92
|
+
* → always visible.
|
|
93
|
+
*/
|
|
94
|
+
function tagVisibleInScope(
|
|
95
|
+
tag: string,
|
|
96
|
+
allowed: Set<string> | null,
|
|
97
|
+
rawRoots: string[] | null,
|
|
98
|
+
): boolean {
|
|
99
|
+
if (rawRoots === null) return true;
|
|
100
|
+
if (allowed && allowed.has(tag)) return true;
|
|
101
|
+
const root = tag.split("/")[0];
|
|
102
|
+
return !!root && rawRoots.includes(root);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Scrub a note's `validation_status` so a tag-scoped caller can't learn the
|
|
107
|
+
* SCHEMA SHAPE (field name, type, enum values) of an OUT-OF-SCOPE tag that
|
|
108
|
+
* happens to co-tag a note it can otherwise see (vault#555 auth review;
|
|
109
|
+
* the #560 leak class). Repro: a scoped caller reading a note tagged both
|
|
110
|
+
* `mine` (in scope) and `project-manhattan` (out of scope) received
|
|
111
|
+
* `validation_status.warnings: [{ schema: "project-manhattan", message:
|
|
112
|
+
* "'codeword' must be one of [fizzbuzz] ...", ... }]` and
|
|
113
|
+
* `schemas: ["project-manhattan"]` — leaking that tag's field/enum.
|
|
114
|
+
*
|
|
115
|
+
* Core produces the FULL status (scope-unaware by architecture, same as
|
|
116
|
+
* every other core surface); this server-layer scrub — mirroring
|
|
117
|
+
* `scrubTagFieldViolationsByScope` — drops any warning whose declaring
|
|
118
|
+
* tag (`schema`, and for a `schema_conflict` the overridden `loser_schema`)
|
|
119
|
+
* is out of scope, and filters the `schemas` array to visible tags. When
|
|
120
|
+
* nothing in-scope remains (the note's only schema-declaring tag was
|
|
121
|
+
* out-of-scope), returns `undefined` so the caller omits `validation_status`
|
|
122
|
+
* entirely — byte-identical to a note with no applicable schema. Unscoped
|
|
123
|
+
* callers (`rawRoots === null`) get the status untouched.
|
|
124
|
+
*
|
|
125
|
+
* Applied at every point a scoped caller receives `validation_status`: the
|
|
126
|
+
* MCP `query-notes` wrapper and the REST `GET /notes[/{id}]` read paths.
|
|
127
|
+
*/
|
|
128
|
+
export function scrubValidationStatusByScope<
|
|
129
|
+
S extends { schemas: string[]; warnings: Array<{ schema: string; loser_schema?: string }> },
|
|
130
|
+
>(
|
|
131
|
+
status: S | null | undefined,
|
|
132
|
+
allowed: Set<string> | null,
|
|
133
|
+
rawRoots: string[] | null,
|
|
134
|
+
): S | undefined {
|
|
135
|
+
if (!status || rawRoots === null) return status ?? undefined;
|
|
136
|
+
const schemas = status.schemas.filter((s) => tagVisibleInScope(s, allowed, rawRoots));
|
|
137
|
+
const warnings = status.warnings.filter(
|
|
138
|
+
(w) =>
|
|
139
|
+
tagVisibleInScope(w.schema, allowed, rawRoots) &&
|
|
140
|
+
(w.loser_schema === undefined || tagVisibleInScope(w.loser_schema, allowed, rawRoots)),
|
|
141
|
+
);
|
|
142
|
+
if (schemas.length === 0 && warnings.length === 0) return undefined;
|
|
143
|
+
return { ...status, schemas, warnings };
|
|
144
|
+
}
|
|
145
|
+
|
|
86
146
|
/**
|
|
87
147
|
* For write paths: a note being created/updated must end up carrying at
|
|
88
148
|
* least one tag inside the allowlist. `tags` is the post-write tag set
|