@synkro-sh/cli 1.5.3 → 1.5.5
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/bootstrap.js +61 -3
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -2849,6 +2849,61 @@ async function main() {
|
|
|
2849
2849
|
const cmdShort = command.slice(0, 80);
|
|
2850
2850
|
log('bashGuard checking: ' + cmdShort);
|
|
2851
2851
|
|
|
2852
|
+
// ─── Hook-side short-circuit for safe in-repo reads ───
|
|
2853
|
+
// The judge primer already deterministically allows these, but the round
|
|
2854
|
+
// trip + batch queue still costs 1–25s per call. Skipping the grade for
|
|
2855
|
+
// unambiguously read-only operations removes that latency for ~half of
|
|
2856
|
+
// typical commands (cat/grep/git status/ls/etc.) and unblocks the worker
|
|
2857
|
+
// pool to grade the operations that actually need judgment.
|
|
2858
|
+
function isSafeInRepoRead(tName: string, cmd: string): boolean {
|
|
2859
|
+
// CC's native read tools are inherently safe.
|
|
2860
|
+
if (tName === 'Read' || tName === 'Grep' || tName === 'Glob') return true;
|
|
2861
|
+
if (tName !== 'Bash' && tName !== 'Shell' && tName !== 'terminal' &&
|
|
2862
|
+
tName !== 'run_terminal_cmd' && tName !== 'execute_command') return false;
|
|
2863
|
+
// Reject any shell metacharacter that could turn a "read" into a write
|
|
2864
|
+
// or chain to an unsafe consumer. Using string includes instead of a
|
|
2865
|
+
// regex because escaping inside a String.raw template literal is a
|
|
2866
|
+
// footgun (we got bitten by an unbalanced-paren regex once already).
|
|
2867
|
+
const UNSAFE_CHARS = ['>', ';', '&', '|', '\`'];
|
|
2868
|
+
for (const ch of UNSAFE_CHARS) { if (cmd.indexOf(ch) !== -1) return false; }
|
|
2869
|
+
const padded = ' ' + cmd + ' ';
|
|
2870
|
+
const UNSAFE_WORDS = [' sudo ', ' su ', ' rm ', ' mv ', ' cp ', ' chmod ', ' chown ', ' tee ', ' kill ', ' sed -i', ' sed --in-place', '\$('];
|
|
2871
|
+
for (const w of UNSAFE_WORDS) { if (padded.indexOf(w) !== -1) return false; }
|
|
2872
|
+
const SAFE_VERBS = new Set([
|
|
2873
|
+
'cat','head','tail','less','more','grep','egrep','fgrep','rg','ag',
|
|
2874
|
+
'find','fd','ls','wc','cmp','diff','file','stat','which','whereis','type',
|
|
2875
|
+
'pwd','whoami','id','date','echo','printf','env','true','false',
|
|
2876
|
+
'jq','yq','awk','sort','uniq','cut','tr','xxd','hexdump','od','column',
|
|
2877
|
+
'node','npm','pnpm','yarn','bun','python','python3','ruby','go','rustc','cargo',
|
|
2878
|
+
'git','sed',
|
|
2879
|
+
]);
|
|
2880
|
+
const tokens = cmd.trim().split(' ').filter(t => t.length > 0);
|
|
2881
|
+
const verb = tokens[0] || '';
|
|
2882
|
+
if (!SAFE_VERBS.has(verb)) return false;
|
|
2883
|
+
if (verb === 'git') {
|
|
2884
|
+
const SAFE_GIT = new Set(['log','show','diff','blame','status','branch','tag','remote','config','rev-parse','ls-files','ls-tree','cat-file','shortlog','reflog','describe','symbolic-ref']);
|
|
2885
|
+
const sub = tokens[1] || '';
|
|
2886
|
+
return SAFE_GIT.has(sub);
|
|
2887
|
+
}
|
|
2888
|
+
if (['npm','pnpm','yarn','bun','cargo','go'].includes(verb)) {
|
|
2889
|
+
const sub = tokens[1] || '';
|
|
2890
|
+
const SAFE_PKG = new Set(['--version','-v','version','list','ls','why','view','show','info','outdated','-h','--help','help']);
|
|
2891
|
+
return SAFE_PKG.has(sub);
|
|
2892
|
+
}
|
|
2893
|
+
if (['node','python','python3','ruby','rustc'].includes(verb)) {
|
|
2894
|
+
const sub = tokens[1] || '';
|
|
2895
|
+
return sub === '--version' || sub === '-v' || sub === '-V';
|
|
2896
|
+
}
|
|
2897
|
+
// sed: only safe without -i (we filtered that above).
|
|
2898
|
+
return true;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
if (isSafeInRepoRead(toolName, command)) {
|
|
2902
|
+
log('bashGuard ' + cmdShort + ' → instant allow (safe in-repo read)');
|
|
2903
|
+
outputJson({ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: 'Synkro: safe in-repo read, deterministic allow.' } });
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2852
2907
|
let jwt = loadJwt();
|
|
2853
2908
|
if (!jwt) { outputEmpty(); return; }
|
|
2854
2909
|
jwt = await ensureFreshJwt(jwt);
|
|
@@ -5570,6 +5625,9 @@ async function dockerInstall(opts = {}) {
|
|
|
5570
5625
|
`${CLAUDE_HOST_STATE_DIR}:/data/claude-host-state:ro`,
|
|
5571
5626
|
"-e",
|
|
5572
5627
|
`WORKERS_PER_POOL=${workers}`,
|
|
5628
|
+
// Pass through the batch-size lever if the operator set it. Defaults
|
|
5629
|
+
// inside the container to 5; clamped to [1, 20] by synkro-server.ts.
|
|
5630
|
+
...process.env.SYNKRO_MAX_BATCH_SIZE ? ["-e", `SYNKRO_MAX_BATCH_SIZE=${process.env.SYNKRO_MAX_BATCH_SIZE}`] : [],
|
|
5573
5631
|
image
|
|
5574
5632
|
];
|
|
5575
5633
|
const run = spawnSync2("docker", args2, { encoding: "utf-8", stdio: "inherit", timeout: 6e4 });
|
|
@@ -5796,7 +5854,7 @@ function writeConfigEnv(opts) {
|
|
|
5796
5854
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
5797
5855
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
5798
5856
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
5799
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.5.
|
|
5857
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.5.5")}`
|
|
5800
5858
|
];
|
|
5801
5859
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
5802
5860
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -6203,7 +6261,7 @@ async function installCommand(opts = {}) {
|
|
|
6203
6261
|
process.exit(1);
|
|
6204
6262
|
}
|
|
6205
6263
|
console.log("Installing Synkro server container...");
|
|
6206
|
-
const workersPerPool = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "
|
|
6264
|
+
const workersPerPool = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
|
|
6207
6265
|
const { image, hostMcpPort, hostGraderPort, hostCwePort } = await dockerInstall({ workersPerPool });
|
|
6208
6266
|
console.log(` \u2713 pulled ${image}`);
|
|
6209
6267
|
console.log(` container started \u2014 MCP=${hostMcpPort} general=${hostGraderPort} CWE=${hostCwePort}`);
|
|
@@ -7186,7 +7244,7 @@ var args = process.argv.slice(2);
|
|
|
7186
7244
|
var cmd = args[0] || "";
|
|
7187
7245
|
var subArgs = args.slice(1);
|
|
7188
7246
|
function printVersion() {
|
|
7189
|
-
console.log("1.5.
|
|
7247
|
+
console.log("1.5.5");
|
|
7190
7248
|
}
|
|
7191
7249
|
function printHelp() {
|
|
7192
7250
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|