@wrongstack/core 0.309.0 → 0.309.1
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/coordination/director.d.ts +7 -0
- package/dist/coordination/explore-companion.d.ts +9 -6
- package/dist/coordination/index.js +331 -59
- package/dist/coordination/mutation-engine.d.ts +5 -3
- package/dist/core/index.js +39 -9
- package/dist/defaults/index.js +479 -101
- package/dist/execution/index.js +27 -9
- package/dist/hq/index.js +45 -5
- package/dist/index.js +640 -131
- package/dist/infrastructure/index.js +22 -3
- package/dist/observability/index.js +1 -1
- package/dist/plugin/index.js +113 -12
- package/dist/prompts/index.js +360 -3
- package/dist/security/auto-approve-policy.d.ts +2 -2
- package/dist/security/index.js +177 -50
- package/dist/security/permission-helpers.d.ts +11 -0
- package/dist/security/permission-policy.d.ts +10 -1
- package/dist/security/yolo-risk.d.ts +17 -0
- package/dist/session-catalog/index.js +32 -2
- package/dist/session-catalog/project-server.js +32 -2
- package/dist/skills/index.js +39 -6
- package/dist/storage/index.js +44 -3
- package/dist/types/tool.d.ts +15 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +54 -4
- package/dist/utils/terminal-sanitize.d.ts +41 -0
- package/dist/utils/tool-subject.d.ts +1 -1
- package/instructions/agents/chaos-monkey.md +5 -1
- package/package.json +4 -4
package/dist/skills/index.js
CHANGED
|
@@ -888,6 +888,32 @@ function numField(rec, key) {
|
|
|
888
888
|
import { spawn } from "node:child_process";
|
|
889
889
|
import * as fs3 from "node:fs/promises";
|
|
890
890
|
import * as path3 from "node:path";
|
|
891
|
+
|
|
892
|
+
// src/utils/win32-cmd.ts
|
|
893
|
+
var WIN32_CMD_META = /[&|<>"%\r\n\0]/;
|
|
894
|
+
function buildWin32CmdShimInvocation(command, args = []) {
|
|
895
|
+
assertSafeWin32CmdArgs([command, ...args]);
|
|
896
|
+
const line = ["call", quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(" ");
|
|
897
|
+
return {
|
|
898
|
+
command: process.env["COMSPEC"] ?? "cmd.exe",
|
|
899
|
+
args: ["/d", "/c", line],
|
|
900
|
+
windowsVerbatimArguments: true
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function assertSafeWin32CmdArgs(args) {
|
|
904
|
+
for (const arg of args) {
|
|
905
|
+
if (typeof arg === "string" && WIN32_CMD_META.test(arg)) {
|
|
906
|
+
throw new Error(
|
|
907
|
+
'win32 cmd shim spawn: argument contains a shell metacharacter (one of & | < > ", or a newline) that could enable command injection through the .cmd/.bat wrapper - refusing to run. Offending argument: ' + JSON.stringify(arg)
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
function quoteWin32CmdArg(arg) {
|
|
913
|
+
return `"${arg}"`;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/skills/skill-generator.ts
|
|
891
917
|
async function validateSkillNameAvailable(name, loader) {
|
|
892
918
|
const formatViolations = validateSkillName(name);
|
|
893
919
|
const conflicts = loader ? (await loader.listEntries()).filter((e) => e.name === name) : [];
|
|
@@ -989,14 +1015,21 @@ async function openInEditor(filePath, env = process.env) {
|
|
|
989
1015
|
context: { filePath }
|
|
990
1016
|
});
|
|
991
1017
|
}
|
|
992
|
-
const
|
|
1018
|
+
const editorArgs = [...parts.slice(1), filePath];
|
|
1019
|
+
const child = shell ? (() => {
|
|
1020
|
+
const inv = buildWin32CmdShimInvocation(parts[0], editorArgs);
|
|
1021
|
+
return spawn(inv.command, inv.args, {
|
|
1022
|
+
stdio: "ignore",
|
|
1023
|
+
detached: true,
|
|
1024
|
+
windowsVerbatimArguments: inv.windowsVerbatimArguments,
|
|
1025
|
+
// Suppresses the console flash the cmd.exe wrapper would otherwise
|
|
1026
|
+
// show before the editor appears. Repo convention — see
|
|
1027
|
+
// `core/tests/architecture/spawn-convention.test.ts`.
|
|
1028
|
+
windowsHide: true
|
|
1029
|
+
});
|
|
1030
|
+
})() : spawn(parts[0], editorArgs, {
|
|
993
1031
|
stdio: "ignore",
|
|
994
1032
|
detached: true,
|
|
995
|
-
shell,
|
|
996
|
-
// `shell` routes through cmd.exe on win32, which flashes a console window
|
|
997
|
-
// before the editor appears. A GUI editor is unaffected by the flag; the
|
|
998
|
-
// shell wrapper is what it suppresses. Repo convention — see
|
|
999
|
-
// `core/tests/architecture/spawn-convention.test.ts`.
|
|
1000
1033
|
windowsHide: true
|
|
1001
1034
|
});
|
|
1002
1035
|
child.unref();
|
package/dist/storage/index.js
CHANGED
|
@@ -1738,6 +1738,14 @@ var PATTERNS = [
|
|
|
1738
1738
|
anchor: "sk-ant-"
|
|
1739
1739
|
},
|
|
1740
1740
|
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g, anchor: "sk-" },
|
|
1741
|
+
{
|
|
1742
|
+
// `xai` is a first-class provider in this codebase, but its key shape was
|
|
1743
|
+
// absent here — so the one credential format WrongStack itself hands users
|
|
1744
|
+
// was the one the scrubber could not recognize (audit 2026-08-20).
|
|
1745
|
+
type: "xai_key",
|
|
1746
|
+
regex: /(?<![A-Za-z0-9])xai-[A-Za-z0-9]{20,}(?![A-Za-z0-9])/g,
|
|
1747
|
+
anchor: "xai-"
|
|
1748
|
+
},
|
|
1741
1749
|
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g, anchor: "ghp_" },
|
|
1742
1750
|
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g, anchor: "github_pat_" },
|
|
1743
1751
|
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g, anchor: "AKIA" },
|
|
@@ -1828,8 +1836,8 @@ var PATTERNS = [
|
|
|
1828
1836
|
// replacement so the separator between adjacent secrets is preserved
|
|
1829
1837
|
// rather than collapsed. Capture groups are therefore: 1=leading
|
|
1830
1838
|
// delimiter, 2=key name, 3=value.
|
|
1831
|
-
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
1832
|
-
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD"]
|
|
1839
|
+
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD|PASSPHRASE))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
1840
|
+
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD", "PASSPHRASE"]
|
|
1833
1841
|
},
|
|
1834
1842
|
{
|
|
1835
1843
|
type: "json_credential_key",
|
|
@@ -1946,6 +1954,27 @@ var JSON_CREDENTIAL_REGEX = PATTERNS.find((p) => p.type === "json_credential_key
|
|
|
1946
1954
|
var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
|
|
1947
1955
|
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
1948
1956
|
var SCRUB_OVERLAP_BYTES = 1024;
|
|
1957
|
+
var PEM_PRIVATE_KEY_BEGIN_RE = /-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----/;
|
|
1958
|
+
var PEM_END_MARKER = "-----END";
|
|
1959
|
+
var MAX_PEM_BLOCK_BYTES = 64 * 1024;
|
|
1960
|
+
var PEM_END_LINE_TOLERANCE = 64;
|
|
1961
|
+
function extendChunkBoundaryPastPem(text, chunkStart, proposedEnd) {
|
|
1962
|
+
const head = text.slice(chunkStart, proposedEnd);
|
|
1963
|
+
const lastBegin = head.lastIndexOf("-----BEGIN ");
|
|
1964
|
+
if (lastBegin === -1) return proposedEnd;
|
|
1965
|
+
const fromBegin = text.slice(chunkStart + lastBegin);
|
|
1966
|
+
const marker = PEM_PRIVATE_KEY_BEGIN_RE.exec(fromBegin);
|
|
1967
|
+
if (!marker || marker.index !== 0) return proposedEnd;
|
|
1968
|
+
const bodyStart = marker[0].length;
|
|
1969
|
+
const cap = Math.min(text.length, chunkStart + lastBegin + MAX_PEM_BLOCK_BYTES);
|
|
1970
|
+
const closeIdx = fromBegin.indexOf(PEM_END_MARKER, bodyStart);
|
|
1971
|
+
if (closeIdx === -1 || chunkStart + lastBegin + closeIdx >= cap + PEM_END_LINE_TOLERANCE) {
|
|
1972
|
+
return proposedEnd;
|
|
1973
|
+
}
|
|
1974
|
+
const lineEnd = fromBegin.indexOf("\n", closeIdx);
|
|
1975
|
+
const end = lineEnd === -1 ? text.length : chunkStart + lastBegin + lineEnd + 1;
|
|
1976
|
+
return Math.max(proposedEnd, end);
|
|
1977
|
+
}
|
|
1949
1978
|
var PATTERN_ANCHORS = [
|
|
1950
1979
|
...new Set(
|
|
1951
1980
|
PATTERNS.flatMap(
|
|
@@ -1982,6 +2011,7 @@ var DefaultSecretScrubber = class {
|
|
|
1982
2011
|
}
|
|
1983
2012
|
}
|
|
1984
2013
|
end = safe === -1 ? end : safe + 1;
|
|
2014
|
+
end = extendChunkBoundaryPastPem(text, i, end);
|
|
1985
2015
|
}
|
|
1986
2016
|
out.push(this.scrubOne(text.slice(i, end)));
|
|
1987
2017
|
i = end;
|
|
@@ -4639,7 +4669,7 @@ function walk(node, vault, transform) {
|
|
|
4639
4669
|
}
|
|
4640
4670
|
return out;
|
|
4641
4671
|
}
|
|
4642
|
-
var SECRET_KEY_PATTERN = /(?:
|
|
4672
|
+
var SECRET_KEY_PATTERN = /(?:api[-_]?key|auth[-_]?token|authorization|proxy-authorization|cookie|bearer|secret|password|passwd|pwd|refresh[-_]?token|session[-_]?key|access[_-]?token|private[_-]?key|token\b)/i;
|
|
4643
4673
|
var NON_SECRET_OVERRIDES = /* @__PURE__ */ new Set(["publickey", "public_key"]);
|
|
4644
4674
|
function isSecretField(name) {
|
|
4645
4675
|
const lc = name.toLowerCase();
|
|
@@ -5344,6 +5374,17 @@ var IN_PROJECT_DENIED_PATHS = [
|
|
|
5344
5374
|
// See discover-mailbox-bridge.ts:findWorkspaceCliEntry.
|
|
5345
5375
|
path: "features.mailboxBridge",
|
|
5346
5376
|
reason: "Enables the mailbox bridge, whose CLI-entry resolution walks up from the project root \u2014 a repo-supplied packages/cli/dist/index.js would be spawned on WebUI boot."
|
|
5377
|
+
},
|
|
5378
|
+
{
|
|
5379
|
+
// `plugins` is already denied above, so a repo cannot ADD a plugin. This
|
|
5380
|
+
// closes the other half: a repo could previously ship
|
|
5381
|
+
// `{"features":{"pluginsTrust":false}}` and switch off the integrity gate
|
|
5382
|
+
// for plugins the user had ALREADY installed globally — disarming the
|
|
5383
|
+
// trust-on-first-use pin that exists to catch a supply-chain update
|
|
5384
|
+
// rewriting a plugin's entry file. Same operator-owned class as the
|
|
5385
|
+
// switches above.
|
|
5386
|
+
path: "features.pluginsTrust",
|
|
5387
|
+
reason: "Disables the plugin trust-on-first-use integrity gate, re-trusting changed code in already-installed global plugins."
|
|
5347
5388
|
}
|
|
5348
5389
|
];
|
|
5349
5390
|
function deleteNestedPath(target, path36) {
|
package/dist/types/tool.d.ts
CHANGED
|
@@ -101,6 +101,21 @@ export interface Tool<I = unknown, O = unknown> {
|
|
|
101
101
|
* fall back to the heuristic.
|
|
102
102
|
*/
|
|
103
103
|
subjectKey?: string | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Extra input fields folded into the permission subject, after `subjectKey`.
|
|
106
|
+
*
|
|
107
|
+
* For a shell-style tool the subject is the whole command line, so the trust
|
|
108
|
+
* rule is as specific as the invocation. A tool whose parameters are NAMED
|
|
109
|
+
* FIELDS rather than an argv array has no such luck: `git` sets
|
|
110
|
+
* `subjectKey: 'command'`, whose value is an enum subcommand, so every
|
|
111
|
+
* `git push` — any branch, `force` or not — rendered to the subject `"push"`
|
|
112
|
+
* and one "always allow" covered them all (audit 2026-08-20).
|
|
113
|
+
*
|
|
114
|
+
* List the fields that change what the call actually does. Absent fields are
|
|
115
|
+
* skipped, so adding a field only narrows existing rules (they degrade to a
|
|
116
|
+
* confirm prompt) and never silently widens one.
|
|
117
|
+
*/
|
|
118
|
+
subjectFields?: readonly string[] | undefined;
|
|
104
119
|
maxOutputBytes?: number | undefined;
|
|
105
120
|
timeoutMs?: number | undefined;
|
|
106
121
|
/**
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export { withSqliteExperimentalWarningSuppressed } from './sqlite-warning.js';
|
|
|
44
44
|
export * from './string.js';
|
|
45
45
|
export * from './task-format.js';
|
|
46
46
|
export { buildSgrSequence, buildTitleSequence, type ColorDepth, detectTerminal, ESCAPE_TERMINATOR, type EscapeEmitResult, type EscapeSequence, isStdinTTY, type MouseProtocol, onResize, safeEmit, setOutputLineGuard, setRawMode, setTitle, type TerminalCapability, TerminalLifecycle, writeErr, writeOut, } from './term.js';
|
|
47
|
+
export { sanitizeTerminalPreview, sanitizeTerminalText } from './terminal-sanitize.js';
|
|
47
48
|
export * from './todos-format.js';
|
|
48
49
|
export { computeMessageTokens, estimateMessageTokens, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, getCalibrationState, type RequestTokenBreakdown, recordActualUsage, resetCalibration, } from './token-estimate.js';
|
|
49
50
|
export { applyToolDescriptionModes, applyToolDescriptionModeToTool, DEFAULT_TOOL_DESCRIPTION_MODE, getToolDescriptionMode, normalizeToolDescriptionMode, resolveToolDescriptionMode, setToolDescriptionMode, simplifyToolDescription, type ToolDescriptionRegistryLike, } from './tool-description-mode.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -4710,6 +4710,42 @@ function formatTaskList(tasks) {
|
|
|
4710
4710
|
return lines.join("\n");
|
|
4711
4711
|
}
|
|
4712
4712
|
|
|
4713
|
+
// src/utils/terminal-sanitize.ts
|
|
4714
|
+
var ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
4715
|
+
var ANSI_OSC_RE = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
|
|
4716
|
+
var ANSI_CONTROL_STRING_RE = /\x1b[P^_X][\s\S]*?\x1b\\/g;
|
|
4717
|
+
var ANSI_ESCAPE_RE = /\x1b[ -/]*[@-~]/g;
|
|
4718
|
+
var BIDI_AND_ZERO_WIDTH_RE = /[---]/g;
|
|
4719
|
+
function sanitizeTerminalText(value, tabWidth = 2) {
|
|
4720
|
+
const tab = " ".repeat(Math.max(1, Math.min(8, Math.floor(tabWidth))));
|
|
4721
|
+
const withoutEscapes = value.replace(ANSI_OSC_RE, "").replace(ANSI_CONTROL_STRING_RE, "").replace(ANSI_RE, "").replace(ANSI_ESCAPE_RE, "").replace(BIDI_AND_ZERO_WIDTH_RE, "").replace(/\t/g, tab).replace(/\r/g, "");
|
|
4722
|
+
let safe = "";
|
|
4723
|
+
for (const char of withoutEscapes) {
|
|
4724
|
+
const code = char.codePointAt(0) ?? 0;
|
|
4725
|
+
if (char === "\n" || code >= 32 && code !== 127 && !(code >= 128 && code <= 159)) {
|
|
4726
|
+
safe += char;
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
return safe;
|
|
4730
|
+
}
|
|
4731
|
+
function sanitizeTerminalPreview(value, opts = {}) {
|
|
4732
|
+
const maxLines = opts.maxLines ?? 40;
|
|
4733
|
+
const maxChars = opts.maxChars ?? 8e3;
|
|
4734
|
+
const safe = sanitizeTerminalText(value, opts.tabWidth);
|
|
4735
|
+
let truncated = false;
|
|
4736
|
+
let clipped = safe;
|
|
4737
|
+
if (clipped.length > maxChars) {
|
|
4738
|
+
clipped = clipped.slice(0, maxChars);
|
|
4739
|
+
truncated = true;
|
|
4740
|
+
}
|
|
4741
|
+
const lines = clipped.split("\n");
|
|
4742
|
+
if (lines.length > maxLines) {
|
|
4743
|
+
clipped = lines.slice(0, maxLines).join("\n");
|
|
4744
|
+
truncated = true;
|
|
4745
|
+
}
|
|
4746
|
+
return { text: clipped, truncated };
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4713
4749
|
// src/utils/tool-description-mode.ts
|
|
4714
4750
|
var DEFAULT_TOOL_DESCRIPTION_MODE = "extend";
|
|
4715
4751
|
var ORIGINAL_TOOL_DESCRIPTION = /* @__PURE__ */ Symbol.for("wrongstack.tool.originalDescription");
|
|
@@ -5589,9 +5625,21 @@ function renderCommandLine(command, args) {
|
|
|
5589
5625
|
});
|
|
5590
5626
|
return [command, ...rendered].join(" ");
|
|
5591
5627
|
}
|
|
5592
|
-
function
|
|
5628
|
+
function renderSubjectFields(obj, fields) {
|
|
5629
|
+
const parts = [];
|
|
5630
|
+
for (const field of fields) {
|
|
5631
|
+
const value = obj[field];
|
|
5632
|
+
if (value === void 0 || value === null || value === "" || value === false) continue;
|
|
5633
|
+
const str = String(value);
|
|
5634
|
+
parts.push(`${field}=${/\s/.test(str) ? `"${str.replace(/"/g, '\\"')}"` : str}`);
|
|
5635
|
+
}
|
|
5636
|
+
return parts.join(" ");
|
|
5637
|
+
}
|
|
5638
|
+
function subjectForToolInput(toolName, input, subjectKey, subjectFields) {
|
|
5593
5639
|
if (!input || typeof input !== "object") return void 0;
|
|
5594
5640
|
const obj = input;
|
|
5641
|
+
const extra = subjectFields && subjectFields.length > 0 ? renderSubjectFields(obj, subjectFields) : "";
|
|
5642
|
+
const withExtra = (base) => extra ? `${base} ${extra}` : base;
|
|
5595
5643
|
if (subjectKey) {
|
|
5596
5644
|
const value = obj[subjectKey];
|
|
5597
5645
|
if (Array.isArray(value)) {
|
|
@@ -5607,9 +5655,9 @@ function subjectForToolInput(toolName, input, subjectKey) {
|
|
|
5607
5655
|
if (subjectKey === "command") {
|
|
5608
5656
|
const rendered = renderCommandLine(value, obj["args"]);
|
|
5609
5657
|
if (value === "commit" && obj["dry_run"] === true) {
|
|
5610
|
-
return `${escapeGlobSubject(rendered)}:dry-run`;
|
|
5658
|
+
return `${escapeGlobSubject(withExtra(rendered))}:dry-run`;
|
|
5611
5659
|
}
|
|
5612
|
-
return escapeGlobSubject(rendered);
|
|
5660
|
+
return escapeGlobSubject(withExtra(rendered));
|
|
5613
5661
|
}
|
|
5614
5662
|
if (subjectKey === "directory" && obj["dry_run"] === true) {
|
|
5615
5663
|
return `${escapeGlobSubject(value)}:dry-run`;
|
|
@@ -5662,7 +5710,7 @@ var DEFAULT_WALK_IGNORE_DIRS = Object.freeze([
|
|
|
5662
5710
|
var DEFAULT_WALK_IGNORE_SET = new Set(DEFAULT_WALK_IGNORE_DIRS);
|
|
5663
5711
|
|
|
5664
5712
|
// src/utils/win32-cmd.ts
|
|
5665
|
-
var WIN32_CMD_META = /[&|<>"
|
|
5713
|
+
var WIN32_CMD_META = /[&|<>"%\r\n\0]/;
|
|
5666
5714
|
function buildWin32CmdShimInvocation(command, args = []) {
|
|
5667
5715
|
assertSafeWin32CmdArgs([command, ...args]);
|
|
5668
5716
|
const line = ["call", quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(" ");
|
|
@@ -5893,6 +5941,8 @@ export {
|
|
|
5893
5941
|
sanitizeMemoryEvidenceBody,
|
|
5894
5942
|
sanitizeMemoryEvidenceSource,
|
|
5895
5943
|
sanitizeNodeOptions,
|
|
5944
|
+
sanitizeTerminalPreview,
|
|
5945
|
+
sanitizeTerminalText,
|
|
5896
5946
|
sanitizeWireToolName,
|
|
5897
5947
|
sessionScopedPath,
|
|
5898
5948
|
setJsonPath,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize untrusted text before it is written to a terminal.
|
|
3
|
+
*
|
|
4
|
+
* Any surface that renders model-supplied, file-supplied or MCP-supplied text
|
|
5
|
+
* into a TTY must run it through here first. Escape sequences in that text can
|
|
6
|
+
* paint outside the region that owns it: `\x1b[2J\x1b[H` clears the screen and
|
|
7
|
+
* homes the cursor, which lets a payload erase a permission prompt's header and
|
|
8
|
+
* repaint a convincing fake above the genuine key prompt. The user then answers
|
|
9
|
+
* the real prompt while reading the attacker's body.
|
|
10
|
+
*
|
|
11
|
+
* Bidi and zero-width controls are stripped for the same reason at a different
|
|
12
|
+
* layer: they reorder or hide characters so the rendered string differs from
|
|
13
|
+
* the string that will actually be executed (the "Trojan Source" class).
|
|
14
|
+
*
|
|
15
|
+
* This is the single source. `@wrongstack/tui` has its own copy for layout
|
|
16
|
+
* measurement; the CLI permission prompt and diff renderer call this one.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Strip terminal escapes, bidi/zero-width controls and non-printable characters
|
|
20
|
+
* from `value`, normalizing tabs to a fixed-width separator.
|
|
21
|
+
*
|
|
22
|
+
* Newlines are preserved; carriage returns are removed so a payload cannot
|
|
23
|
+
* return to the start of a line and overwrite what was already drawn.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sanitizeTerminalText(value: string, tabWidth?: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* Sanitize and hard-cap untrusted text destined for a terminal preview.
|
|
28
|
+
*
|
|
29
|
+
* A line cap alone is not a bound: a single 200,000-character line passes a
|
|
30
|
+
* 40-line limit untouched and can scroll a prompt off screen. Callers that show
|
|
31
|
+
* a preview of attacker-influenced content should bound both dimensions.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sanitizeTerminalPreview(value: string, opts?: {
|
|
34
|
+
maxLines?: number;
|
|
35
|
+
maxChars?: number;
|
|
36
|
+
tabWidth?: number;
|
|
37
|
+
}): {
|
|
38
|
+
text: string;
|
|
39
|
+
truncated: boolean;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=terminal-sanitize.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare function escapeGlobSubject(value: string): string;
|
|
2
2
|
export declare function normalizePathSubject(value: string): string;
|
|
3
3
|
export declare function isPathSubjectKey(subjectKey: string): boolean;
|
|
4
|
-
export declare function subjectForToolInput(toolName: string, input: unknown, subjectKey?: string): string | undefined;
|
|
4
|
+
export declare function subjectForToolInput(toolName: string, input: unknown, subjectKey?: string, subjectFields?: readonly string[]): string | undefined;
|
|
5
5
|
//# sourceMappingURL=tool-subject.d.ts.map
|
|
@@ -23,6 +23,10 @@ execution order and diagnosis, never the mutation set.
|
|
|
23
23
|
3. Record the outcome:
|
|
24
24
|
- Tests fail → mutant `killed` (quote the first failing assertion).
|
|
25
25
|
- Tests pass → mutant `survived` (this is a weak-test finding, not your failure).
|
|
26
|
+
- The test command times out or is aborted → mutant `killed-by-hang`
|
|
27
|
+
(the mutation broke the suite by non-termination — a kill, NOT a
|
|
28
|
+
survivor; record the timeout as evidence). Never report a hung
|
|
29
|
+
command as `survived`.
|
|
26
30
|
4. Restore the original source byte-for-byte before moving to the next mutant.
|
|
27
31
|
The suite is only honest if every mutant ran against pristine code except
|
|
28
32
|
its own single mutation.
|
|
@@ -47,7 +51,7 @@ Submit via `submit_result`, then repeat it as your final text (fenced JSON):
|
|
|
47
51
|
"summary": "<one line: N killed / M survived / K skipped>",
|
|
48
52
|
"mutants": [
|
|
49
53
|
{ "id": "<plan id>", "file": "...", "line": 0, "kind": "...",
|
|
50
|
-
"status": "killed | survived | skipped",
|
|
54
|
+
"status": "killed | survived | skipped | killed-by-hang",
|
|
51
55
|
"evidence": "<failing assertion, or 'suite green' for survivors>" }
|
|
52
56
|
]
|
|
53
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/core",
|
|
3
|
-
"version": "0.309.
|
|
3
|
+
"version": "0.309.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
|
|
6
6
|
"repository": {
|
|
@@ -182,8 +182,8 @@
|
|
|
182
182
|
"wrongstackApiVersion": "0.1.10",
|
|
183
183
|
"dependencies": {
|
|
184
184
|
"zod": "4.4.3",
|
|
185
|
-
"@wrongstack/kanban": "0.309.
|
|
186
|
-
"@wrongstack/persistence": "0.309.
|
|
185
|
+
"@wrongstack/kanban": "0.309.1",
|
|
186
|
+
"@wrongstack/persistence": "0.309.1"
|
|
187
187
|
},
|
|
188
188
|
"devDependencies": {
|
|
189
189
|
"@types/node": "^26.2.0",
|
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
"access": "public"
|
|
194
194
|
},
|
|
195
195
|
"optionalDependencies": {
|
|
196
|
-
"@datadog/pprof": "5.18.
|
|
196
|
+
"@datadog/pprof": "5.18.1"
|
|
197
197
|
},
|
|
198
198
|
"scripts": {
|
|
199
199
|
"build": "node ../../scripts/build-package.mjs",
|