@wenrwa/marketplace-cli 0.8.9 → 0.8.10

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.
@@ -215,6 +215,22 @@ describe('PreviewRunner (tp-05) — real git, real processes', () => {
215
215
  expect(await runner.update({ workspaceId: 'ws-1', repoUrl: fx.remote })).toMatchObject({ status: 'running', runningSha: fixed });
216
216
  expect(JSON.parse(await get(port)).message).toBe('v3 fixed');
217
217
  });
218
+ it('tp-17: the log reply never carries a .env value, even one printed bare by the app', async () => {
219
+ // A value no token pattern recognises, logged on its own line by merged app code.
220
+ fs.appendFileSync(path.join(fx.workingCopy, '.env'), 'PLAIN_SECRET=plainvalue9f3a2b7c41\nSHORT=on\n');
221
+ pushChange(fx, {
222
+ 'server.js': `console.log(process.env.PLAIN_SECRET); console.log('using ' + process.env.OWNER_SECRET + ' and ' + process.env.SHORT);\n${SERVER_JS}`,
223
+ }, 'logs a key');
224
+ const r = await runner.start({ workspaceId: 'ws-1', repoUrl: fx.remote });
225
+ expect(r.status).toBe('running');
226
+ await new Promise((res) => setTimeout(res, 300));
227
+ const { log } = runner.logs({ workspaceId: 'ws-1' });
228
+ expect(log).toContain('using ');
229
+ expect(log).not.toContain('plainvalue9f3a2b7c41');
230
+ expect(log).not.toContain('sk_live_owner_value');
231
+ // Short values (flags, ports) stay readable: masking them would blank the log.
232
+ expect(log).toContain(' and on');
233
+ });
218
234
  it('pins to the remote default branch — another branch pushed to the remote never runs', async () => {
219
235
  git(fx.author, 'checkout', '-b', 'feature');
220
236
  fs.writeFileSync(path.join(fx.author, 'message.txt'), 'feature branch\n');
@@ -61,6 +61,18 @@ export interface PreviewRunnerOpts {
61
61
  /** The only address a preview app may listen on. */
62
62
  export declare const PREVIEW_BIND_HOST = "127.0.0.1";
63
63
  export declare const PREVIEW_PASSTHROUGH_ENV: readonly ["PATH", "HOME", "USER", "LANG", "LC_ALL", "TZ", "TMPDIR", "SHELL"];
64
+ /**
65
+ * tp-17 — `.env` values this long or longer are masked in the log reply by
66
+ * VALUE, whatever their shape. Shorter ones (ports, flags, `production`) stay:
67
+ * masking them would blank half the log and they are not secrets.
68
+ */
69
+ export declare const LOG_MASK_MIN_VALUE_LENGTH = 8;
70
+ /**
71
+ * tp-17 — mask every given value in `text`, longest first (so a value that
72
+ * contains another is masked whole). Pattern redaction (`redact`) misses a key
73
+ * the app prints bare with no recognisable shape; this never does.
74
+ */
75
+ export declare function maskEnvValues(text: string, values: Iterable<string>): string;
64
76
  /**
65
77
  * The preview env: allowlisted process names < the owner's repo `.env`
66
78
  * (reserved names already removed by loadRepoEnv) < the fixed PORT.
@@ -85,6 +97,8 @@ export declare function installCommand(files: (name: string) => boolean): [strin
85
97
  export declare class PreviewRunner {
86
98
  private readonly opts;
87
99
  private readonly states;
100
+ /** tp-17 — the `.env` values each workspace's last deploy ran with (log masking). */
101
+ private readonly deployedEnvValues;
88
102
  /** One port, so one operation at a time across the whole box. */
89
103
  private chain;
90
104
  private orphanSwept;
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.PreviewRunner = exports.PREVIEW_PASSTHROUGH_ENV = exports.PREVIEW_BIND_HOST = void 0;
36
+ exports.PreviewRunner = exports.LOG_MASK_MIN_VALUE_LENGTH = exports.PREVIEW_PASSTHROUGH_ENV = exports.PREVIEW_BIND_HOST = void 0;
37
+ exports.maskEnvValues = maskEnvValues;
37
38
  exports.buildPreviewEnv = buildPreviewEnv;
38
39
  exports.loopbackArgs = loopbackArgs;
39
40
  exports.machineExternalAddresses = machineExternalAddresses;
@@ -116,6 +117,24 @@ const redact_1 = require("../secrets/redact");
116
117
  /** The only address a preview app may listen on. */
117
118
  exports.PREVIEW_BIND_HOST = '127.0.0.1';
118
119
  exports.PREVIEW_PASSTHROUGH_ENV = ['PATH', 'HOME', 'USER', 'LANG', 'LC_ALL', 'TZ', 'TMPDIR', 'SHELL'];
120
+ /**
121
+ * tp-17 — `.env` values this long or longer are masked in the log reply by
122
+ * VALUE, whatever their shape. Shorter ones (ports, flags, `production`) stay:
123
+ * masking them would blank half the log and they are not secrets.
124
+ */
125
+ exports.LOG_MASK_MIN_VALUE_LENGTH = 8;
126
+ /**
127
+ * tp-17 — mask every given value in `text`, longest first (so a value that
128
+ * contains another is masked whole). Pattern redaction (`redact`) misses a key
129
+ * the app prints bare with no recognisable shape; this never does.
130
+ */
131
+ function maskEnvValues(text, values) {
132
+ const list = [...new Set([...values].filter((v) => v.length >= exports.LOG_MASK_MIN_VALUE_LENGTH))].sort((a, b) => b.length - a.length);
133
+ let out = text;
134
+ for (const v of list)
135
+ out = out.split(v).join('*'.repeat(v.length));
136
+ return out;
137
+ }
119
138
  const DEFAULT_READY_TIMEOUT = { built: 120_000, dev: 300_000 };
120
139
  const DEFAULT_STEP_TIMEOUT_MS = 15 * 60_000;
121
140
  const LOG_TAIL_LINES = 200;
@@ -204,6 +223,8 @@ function runnerFor(files) {
204
223
  class PreviewRunner {
205
224
  opts;
206
225
  states = new Map();
226
+ /** tp-17 — the `.env` values each workspace's last deploy ran with (log masking). */
227
+ deployedEnvValues = new Map();
207
228
  /** One port, so one operation at a time across the whole box. */
208
229
  chain = Promise.resolve();
209
230
  orphanSwept = false;
@@ -280,7 +301,13 @@ class PreviewRunner {
280
301
  out = out.slice(-LOG_TAIL_MAX_CHARS);
281
302
  truncated = true;
282
303
  }
283
- return { log: (0, redact_1.redact)(out), truncated };
304
+ // Pattern masking, then the owner's own .env values by value — the ones
305
+ // the last deploy ran with and the ones on disk now (an edited .env).
306
+ const values = [
307
+ ...(this.deployedEnvValues.get(req.workspaceId) ?? []),
308
+ ...Object.values((0, repoEnv_1.loadRepoEnv)(this.opts.workingCopyDir(req.workspaceId))),
309
+ ];
310
+ return { log: maskEnvValues((0, redact_1.redact)(out), values), truncated };
284
311
  }
285
312
  status(workspaceId) {
286
313
  return this.snapshot(workspaceId, {});
@@ -424,7 +451,9 @@ class PreviewRunner {
424
451
  message: `No "${runScript}" script in package.json.`,
425
452
  });
426
453
  }
427
- const env = buildPreviewEnv(this.opts.baseEnv, (0, repoEnv_1.loadRepoEnv)(this.opts.workingCopyDir(workspaceId)), this.opts.port);
454
+ const repoEnv = (0, repoEnv_1.loadRepoEnv)(this.opts.workingCopyDir(workspaceId));
455
+ this.deployedEnvValues.set(workspaceId, Object.values(repoEnv));
456
+ const env = buildPreviewEnv(this.opts.baseEnv, repoEnv, this.opts.port);
428
457
  // 2. Install, then (built) build — beside whatever is serving.
429
458
  const [installCmd, installArgs] = installCommand(has);
430
459
  const installed = await this.runStep(installCmd, installArgs, slotDir, env, logFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenrwa/marketplace-cli",
3
- "version": "0.8.9",
3
+ "version": "0.8.10",
4
4
  "description": "CLI tool for the Wenrwa Agent Marketplace — monitor bounties, agents, workspaces, and treasury from your terminal",
5
5
  "author": "Wenrwa <dev@wenrwa.com>",
6
6
  "license": "MIT",