@statewalker/webrun-storage 0.2.2 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"files-blob-store.d.ts","sourceRoot":"","sources":["../src/files-blob-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAkDvF"}
1
+ {"version":3,"file":"files-blob-store.d.ts","sourceRoot":"","sources":["../src/files-blob-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,SAAS,CA4DvF"}
package/dist/index.js CHANGED
@@ -42,13 +42,19 @@ async function assertVerified(id, chunks, verify) {
42
42
  }
43
43
  //#endregion
44
44
  //#region src/files-blob-store.ts
45
+ const isDotSegment = (segment) => segment === "." || segment === "..";
45
46
  /**
46
47
  * {@link BlobStore} backed by a {@link FilesApi}. Ids map to git-style sharded
47
48
  * paths (`ab/cdef…`) so a large id space never lands in one flat directory.
48
49
  */
49
50
  function filesBlobStore(files, opts) {
50
51
  const base = (opts?.root ?? "/").replace(/\/+$/, "");
51
- const pathFor = (id) => `${base}/${id.slice(0, 2)}/${id.slice(2)}`;
52
+ const pathFor = (id) => {
53
+ const shard = id.slice(0, 2);
54
+ const rest = id.slice(2);
55
+ if (id === "" || /[/\\]/.test(id) || isDotSegment(shard) || isDotSegment(rest)) throw new Error(`Invalid blob id: ${JSON.stringify(id)}`);
56
+ return `${base}/${shard}/${rest}`;
57
+ };
52
58
  const idFrom = (relative) => relative.replace("/", "");
53
59
  return {
54
60
  async put(id, bytes, opts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewalker/webrun-storage",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Domain-neutral byte-persistence seam (BlobStore + KvStore with CAS) over the webrun-files FilesApi",
5
5
  "bugs": {
6
6
  "url": "https://github.com/statewalker/webrun-sync/issues"
@@ -31,10 +31,10 @@
31
31
  "src"
32
32
  ],
33
33
  "dependencies": {
34
- "@statewalker/webrun-files": "^0.10.0"
34
+ "@statewalker/webrun-files": "^0.12.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@statewalker/webrun-files-mem": "^0.10.0",
37
+ "@statewalker/webrun-files-mem": "^0.12.0",
38
38
  "rolldown": "^1.2.12",
39
39
  "vitest": "^5.0.3",
40
40
  "typescript": "^7.0.2",
@@ -49,6 +49,7 @@
49
49
  "format": "biome format --write .",
50
50
  "format:check": "biome format .",
51
51
  "format:fix": "biome check --write --unsafe .",
52
- "typecheck": "tsc --noEmit"
52
+ "typecheck": "tsc --noEmit",
53
+ "typecheck:tests": "tsc -p tsconfig.tests.json"
53
54
  }
54
55
  }
@@ -7,6 +7,8 @@ export interface FilesBlobStoreOptions {
7
7
  root?: string;
8
8
  }
9
9
 
10
+ const isDotSegment = (segment: string): boolean => segment === "." || segment === "..";
11
+
10
12
  /**
11
13
  * {@link BlobStore} backed by a {@link FilesApi}. Ids map to git-style sharded
12
14
  * paths (`ab/cdef…`) so a large id space never lands in one flat directory.
@@ -14,7 +16,17 @@ export interface FilesBlobStoreOptions {
14
16
  export function filesBlobStore(files: FilesApi, opts?: FilesBlobStoreOptions): BlobStore {
15
17
  const base = (opts?.root ?? "/").replace(/\/+$/, "");
16
18
 
17
- const pathFor = (id: string): string => `${base}/${id.slice(0, 2)}/${id.slice(2)}`;
19
+ const pathFor = (id: string): string => {
20
+ const shard = id.slice(0, 2);
21
+ const rest = id.slice(2);
22
+ // webrun-files resolves `..` before any mount or guard sees the path, so an
23
+ // id carrying a separator or a dot segment would land outside `root`. Ids
24
+ // come from remote peers (content-transfer's serve-store), so refuse them.
25
+ if (id === "" || /[/\\]/.test(id) || isDotSegment(shard) || isDotSegment(rest)) {
26
+ throw new Error(`Invalid blob id: ${JSON.stringify(id)}`);
27
+ }
28
+ return `${base}/${shard}/${rest}`;
29
+ };
18
30
  const idFrom = (relative: string): string => relative.replace("/", "");
19
31
 
20
32
  return {