@shrkcrft/generator 0.1.0-alpha.16 → 0.1.0-alpha.17

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":"folder-apply.d.ts","sourceRoot":"","sources":["../src/folder-apply.ts"],"names":[],"mappings":"AAcA,OAAO,EAAuB,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAMD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,cAAc,EAAE,EAC9B,OAAO,EAAE,gBAAgB,GACxB,oBAAoB,CAsGtB"}
1
+ {"version":3,"file":"folder-apply.d.ts","sourceRoot":"","sources":["../src/folder-apply.ts"],"names":[],"mappings":"AAeA,OAAO,EAAuB,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAMD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,cAAc,EAAE,EAC9B,OAAO,EAAE,gBAAgB,GACxB,oBAAoB,CAwHtB"}
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { existsSync, renameSync, rmSync } from 'node:fs';
14
14
  import * as nodePath from 'node:path';
15
+ import { safeResolveTargetPath } from '@shrkcrft/core';
15
16
  import { checkFolderOpSafety, FolderOpSafety } from "./folder-safety.js";
16
17
  function resolveAbs(projectRoot, p) {
17
18
  return nodePath.isAbsolute(p) ? p : nodePath.resolve(projectRoot, p);
@@ -60,7 +61,26 @@ export function applyFolderOps(ops, options) {
60
61
  });
61
62
  continue;
62
63
  }
63
- if (existsSync(absNewPath)) {
64
+ // The rename DESTINATION is never validated by checkFolderOpSafety (which
65
+ // only checks targetPath), and resolveAbs even allows absolute newPaths —
66
+ // so `newPath: '../sibling'` or '/etc/x' would move the folder OUTSIDE the
67
+ // project root. Enforce containment through the same chokepoint as writes.
68
+ let safeNew;
69
+ try {
70
+ safeNew = safeResolveTargetPath(op.newPath, options.projectRoot);
71
+ }
72
+ catch (e) {
73
+ const pathErr = e;
74
+ rejected.push({
75
+ ...baseResult,
76
+ applied: false,
77
+ safety: FolderOpSafety.Unsafe,
78
+ reason: `rename-folder destination "${op.newPath}" is outside the project root (${pathErr.code}).`,
79
+ });
80
+ continue;
81
+ }
82
+ const safeAbsNewPath = safeNew.absolutePath;
83
+ if (existsSync(safeAbsNewPath)) {
64
84
  rejected.push({
65
85
  ...baseResult,
66
86
  applied: false,
@@ -74,7 +94,7 @@ export function applyFolderOps(ops, options) {
74
94
  continue;
75
95
  }
76
96
  try {
77
- renameSync(absTarget, absNewPath);
97
+ renameSync(absTarget, safeAbsNewPath);
78
98
  applied.push({ ...baseResult, applied: true });
79
99
  }
80
100
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"synthetic-plan.d.ts","sourceRoot":"","sources":["../src/synthetic-plan.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,MAAM,GAClB,eAAe,CAsCjB;AAuBD,OAAO,EAAsC,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;CACjC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,GACpB,MAAM,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAsCzC"}
1
+ {"version":3,"file":"synthetic-plan.d.ts","sourceRoot":"","sources":["../src/synthetic-plan.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,MAAM,GAClB,eAAe,CAsCjB;AAwCD,OAAO,EAAsC,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;CACjC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,GACpB,MAAM,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAsCzC"}
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { existsSync, readFileSync } from 'node:fs';
17
17
  import * as nodePath from 'node:path';
18
+ import { safeResolveTargetPath } from '@shrkcrft/core';
18
19
  import { evaluatePlannedChange } from "./planned-change.js";
19
20
  import { FileChangeType } from "./file-change.js";
20
21
  export const SYNTHETIC_TEMPLATE_PREFIX = '__';
@@ -62,12 +63,30 @@ export function evaluateSavedPlanInPlace(plan, projectRoot) {
62
63
  return out;
63
64
  }
64
65
  function applyOperation(projectRoot, relativePath, operation) {
65
- const absolutePath = nodePath.resolve(projectRoot, relativePath);
66
- const existing = existsSync(absolutePath) ? readFileSync(absolutePath, 'utf8') : null;
66
+ // Route through the single generator chokepoint instead of a bare resolve, so
67
+ // a traversal / absolute `relativePath` in a hand-crafted or tampered plan
68
+ // can't write OUTSIDE the project root. An unsafe path becomes a Conflict,
69
+ // which writeSyntheticPlan refuses (matching the template path in dry-run.ts).
70
+ let safe;
71
+ try {
72
+ safe = safeResolveTargetPath(relativePath, projectRoot);
73
+ }
74
+ catch (e) {
75
+ const pathErr = e;
76
+ return {
77
+ type: FileChangeType.Conflict,
78
+ absolutePath: pathErr.rawPath,
79
+ relativePath: pathErr.rawPath,
80
+ contents: '',
81
+ reason: `Refused unsafe target path (${pathErr.code}): ${pathErr.message}`,
82
+ sizeBytes: 0,
83
+ };
84
+ }
85
+ const existing = existsSync(safe.absolutePath) ? readFileSync(safe.absolutePath, 'utf8') : null;
67
86
  return evaluatePlannedChange({
68
- change: { targetPath: relativePath, operation },
69
- absolutePath,
70
- relativePath,
87
+ change: { targetPath: safe.relativePath, operation },
88
+ absolutePath: safe.absolutePath,
89
+ relativePath: safe.relativePath,
71
90
  existing,
72
91
  });
73
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/generator",
3
- "version": "0.1.0-alpha.16",
3
+ "version": "0.1.0-alpha.17",
4
4
  "description": "SharkCraft plan-first generator: GenerationPlan, FileChange, dry-run, safe writes.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -43,10 +43,10 @@
43
43
  "typecheck": "tsc --noEmit -p tsconfig.json"
44
44
  },
45
45
  "dependencies": {
46
- "@shrkcrft/core": "^0.1.0-alpha.16",
47
- "@shrkcrft/templates": "^0.1.0-alpha.16",
48
- "@shrkcrft/rules": "^0.1.0-alpha.16",
49
- "@shrkcrft/paths": "^0.1.0-alpha.16"
46
+ "@shrkcrft/core": "^0.1.0-alpha.17",
47
+ "@shrkcrft/templates": "^0.1.0-alpha.17",
48
+ "@shrkcrft/rules": "^0.1.0-alpha.17",
49
+ "@shrkcrft/paths": "^0.1.0-alpha.17"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"