@wrongstack/tools 0.306.2 → 0.306.4

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/scaffold.js CHANGED
@@ -4,6 +4,7 @@ import * as path2 from "node:path";
4
4
  import { atomicWrite } from "@wrongstack/core/utils";
5
5
 
6
6
  // src/_util.ts
7
+ import * as fsp from "node:fs/promises";
7
8
  import * as path from "node:path";
8
9
  import * as Core from "@wrongstack/core/utils";
9
10
  function resolvePath(input, ctx) {
@@ -27,6 +28,40 @@ function ensureInsideRoot(absPath, ctx) {
27
28
  function safeResolve(input, ctx) {
28
29
  return ensureInsideRoot(resolvePath(input, ctx), ctx);
29
30
  }
31
+ async function resolveRealInsideRoot(absPath, ctx) {
32
+ if (ctx.allowOutsideProjectRoot) return absPath;
33
+ const realRoots = await Promise.all(
34
+ allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r)))
35
+ );
36
+ let probe = absPath;
37
+ const pendingTail = [];
38
+ for (; ; ) {
39
+ let real;
40
+ try {
41
+ real = await fsp.realpath(probe);
42
+ } catch (err) {
43
+ if (err.code === "ENOENT") {
44
+ const parent = path.dirname(probe);
45
+ if (parent === probe) return absPath;
46
+ pendingTail.unshift(path.basename(probe));
47
+ probe = parent;
48
+ continue;
49
+ }
50
+ throw err;
51
+ }
52
+ const candidate = pendingTail.length > 0 ? path.join(real, ...pendingTail) : real;
53
+ if (isInsideAny(candidate, realRoots)) {
54
+ return candidate;
55
+ }
56
+ throw new Error(
57
+ `Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
58
+ );
59
+ }
60
+ }
61
+ async function safeResolveReal(input, ctx) {
62
+ const abs = safeResolve(input, ctx);
63
+ return await resolveRealInsideRoot(abs, ctx);
64
+ }
30
65
 
31
66
  // src/scaffold.ts
32
67
  var BUILT_IN_TEMPLATES = {
@@ -156,7 +191,7 @@ var scaffoldTool = {
156
191
  required: ["template", "name"]
157
192
  },
158
193
  async execute(input, ctx) {
159
- const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
194
+ const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
160
195
  const name = input.name;
161
196
  const vars = { name, ...input.vars };
162
197
  const builtIn = BUILT_IN_TEMPLATES[input.template];
package/dist/test.js CHANGED
@@ -1044,6 +1044,7 @@ function utf8Prefix(text, maxBytes) {
1044
1044
  }
1045
1045
 
1046
1046
  // src/_util.ts
1047
+ import * as fsp2 from "node:fs/promises";
1047
1048
  import * as path3 from "node:path";
1048
1049
  import * as Core from "@wrongstack/core/utils";
1049
1050
  function resolvePath(input, ctx) {
@@ -1067,6 +1068,40 @@ function ensureInsideRoot(absPath, ctx) {
1067
1068
  function safeResolve(input, ctx) {
1068
1069
  return ensureInsideRoot(resolvePath(input, ctx), ctx);
1069
1070
  }
1071
+ async function resolveRealInsideRoot(absPath, ctx) {
1072
+ if (ctx.allowOutsideProjectRoot) return absPath;
1073
+ const realRoots = await Promise.all(
1074
+ allowedRoots(ctx).map((r) => fsp2.realpath(r).catch(() => path3.resolve(r)))
1075
+ );
1076
+ let probe = absPath;
1077
+ const pendingTail = [];
1078
+ for (; ; ) {
1079
+ let real;
1080
+ try {
1081
+ real = await fsp2.realpath(probe);
1082
+ } catch (err) {
1083
+ if (err.code === "ENOENT") {
1084
+ const parent = path3.dirname(probe);
1085
+ if (parent === probe) return absPath;
1086
+ pendingTail.unshift(path3.basename(probe));
1087
+ probe = parent;
1088
+ continue;
1089
+ }
1090
+ throw err;
1091
+ }
1092
+ const candidate = pendingTail.length > 0 ? path3.join(real, ...pendingTail) : real;
1093
+ if (isInsideAny(candidate, realRoots)) {
1094
+ return candidate;
1095
+ }
1096
+ throw new Error(
1097
+ `Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
1098
+ );
1099
+ }
1100
+ }
1101
+ async function safeResolveReal(input, ctx) {
1102
+ const abs = safeResolve(input, ctx);
1103
+ return await resolveRealInsideRoot(abs, ctx);
1104
+ }
1070
1105
  var COMMAND_OUTPUT_MAX_BYTES = 32768;
1071
1106
  var REPEAT_RUN_THRESHOLD = 3;
1072
1107
  function collapseCarriageReturns(text) {
@@ -3594,7 +3629,7 @@ var testTool = {
3594
3629
  return final;
3595
3630
  },
3596
3631
  async *executeStream(input, ctx, opts) {
3597
- const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
3632
+ const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
3598
3633
  const runner = input.runner ?? "auto";
3599
3634
  if (runner === "auto") {
3600
3635
  const bridge = await tryLegacyCodeOperation("test", {