@synkro-sh/cli 1.6.4 → 1.6.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 +7 -109
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -2232,7 +2232,7 @@ async function main() {
|
|
|
2232
2232
|
'Last user prompt: ' + (lastPrompt || 'none'),
|
|
2233
2233
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
2234
2234
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix \u2014 your job is only to detect violations.',
|
|
2235
|
-
'
|
|
2235
|
+
'The rules shown were pre-selected as the ones relevant to this edit \u2014 every rule here IS relevant, do not label any "not relevant". When passing (ok=true), give a terse, specific reason each rule passes. Format: "R003: no hardcoded secrets in file. R005: in-repo path only." Cover every rule shown.',
|
|
2236
2236
|
].join('\\n');
|
|
2237
2237
|
|
|
2238
2238
|
let gradeResp: string;
|
|
@@ -3000,7 +3000,7 @@ import {
|
|
|
3000
3000
|
parseVerdict, dispatchCapture, dispatchFinding, ruleMode, postWithRetry, readStdin,
|
|
3001
3001
|
extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
|
|
3002
3002
|
outputJson, outputEmpty, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,
|
|
3003
|
-
logGraderUnavailable,
|
|
3003
|
+
logGraderUnavailable, filterRules, normalizeMode, appendLocalTelemetry, isSafeInRepoRead,
|
|
3004
3004
|
type HookConfig, type Rule,
|
|
3005
3005
|
} from './_synkro-common.ts';
|
|
3006
3006
|
|
|
@@ -3061,108 +3061,6 @@ async function main() {
|
|
|
3061
3061
|
return;
|
|
3062
3062
|
}
|
|
3063
3063
|
|
|
3064
|
-
// ─── Hook-side short-circuit for safe in-repo reads ───
|
|
3065
|
-
// The judge primer already deterministically allows these, but the round
|
|
3066
|
-
// trip + batch queue still costs 1–25s per call. Skipping the grade for
|
|
3067
|
-
// unambiguously read-only operations removes that latency for ~half of
|
|
3068
|
-
// typical commands (cat/grep/git status/ls/etc.) and unblocks the worker
|
|
3069
|
-
// pool to grade the operations that actually need judgment.
|
|
3070
|
-
// Returning FALSE just means "don't short-circuit — let the LLM grade it."
|
|
3071
|
-
// Never blocks. The judge sees the command, applies rules, returns its
|
|
3072
|
-
// own verdict. Path scoping below: STRICT, only short-circuit when every
|
|
3073
|
-
// absolute path is under the linked repo root.
|
|
3074
|
-
function isSafeBashSegment(seg: string, repoRoot: string): boolean {
|
|
3075
|
-
const UNSAFE_CHARS = ['>', ';', '&', '\`'];
|
|
3076
|
-
for (const ch of UNSAFE_CHARS) { if (seg.indexOf(ch) !== -1) return false; }
|
|
3077
|
-
const padded = ' ' + seg + ' ';
|
|
3078
|
-
const UNSAFE_WORDS = [
|
|
3079
|
-
' sudo ', ' su ', ' rm ', ' mv ', ' cp ', ' chmod ', ' chown ',
|
|
3080
|
-
' tee ', ' kill ', ' sed -i', ' sed --in-place',
|
|
3081
|
-
' sh -c', ' bash -c', ' zsh -c', ' eval ', ' exec ',
|
|
3082
|
-
'\$(',
|
|
3083
|
-
];
|
|
3084
|
-
for (const w of UNSAFE_WORDS) { if (padded.indexOf(w) !== -1) return false; }
|
|
3085
|
-
|
|
3086
|
-
// Narrowed verb set. Removed:
|
|
3087
|
-
// awk: has system() / |& shell-spawn
|
|
3088
|
-
// env: \`env FOO=bar evil_cmd\` runs evil_cmd
|
|
3089
|
-
// sed: scripting + -i write capability; not worth parsing
|
|
3090
|
-
const SAFE_VERBS = new Set([
|
|
3091
|
-
'cat','head','tail','less','more','grep','egrep','fgrep','rg','ag',
|
|
3092
|
-
'find','fd','ls','wc','cmp','diff','file','stat','which','whereis','type',
|
|
3093
|
-
'pwd','whoami','id','date','echo','printf','true','false',
|
|
3094
|
-
'jq','yq','sort','uniq','cut','tr','xxd','hexdump','od','column',
|
|
3095
|
-
'node','npm','pnpm','yarn','bun','python','python3','ruby','go','rustc','cargo',
|
|
3096
|
-
'git',
|
|
3097
|
-
]);
|
|
3098
|
-
const tokens = seg.trim().split(' ').filter(t => t.length > 0);
|
|
3099
|
-
const verb = tokens[0] || '';
|
|
3100
|
-
if (!SAFE_VERBS.has(verb)) return false;
|
|
3101
|
-
|
|
3102
|
-
// find/fd: reject any execution / mutation action flag.
|
|
3103
|
-
if (verb === 'find' || verb === 'fd') {
|
|
3104
|
-
const BAD = new Set([
|
|
3105
|
-
'-exec','-execdir','-ok','-okdir','-delete',
|
|
3106
|
-
'-fprint','-fprintf','-fprint0','-fls',
|
|
3107
|
-
'--exec','--exec-batch',
|
|
3108
|
-
]);
|
|
3109
|
-
for (const t of tokens) { if (BAD.has(t)) return false; }
|
|
3110
|
-
}
|
|
3111
|
-
|
|
3112
|
-
// git: only pure-read subcommands. branch/tag/remote/config dropped —
|
|
3113
|
-
// each has flag combinations that mutate state.
|
|
3114
|
-
if (verb === 'git') {
|
|
3115
|
-
const SAFE_GIT = new Set([
|
|
3116
|
-
'log','show','diff','blame','status','rev-parse',
|
|
3117
|
-
'ls-files','ls-tree','cat-file','shortlog','reflog',
|
|
3118
|
-
'describe','symbolic-ref','--version',
|
|
3119
|
-
]);
|
|
3120
|
-
const sub = tokens[1] || '';
|
|
3121
|
-
if (!SAFE_GIT.has(sub)) return false;
|
|
3122
|
-
} else if (['npm','pnpm','yarn','bun','cargo','go'].includes(verb)) {
|
|
3123
|
-
const sub = tokens[1] || '';
|
|
3124
|
-
const SAFE_PKG = new Set([
|
|
3125
|
-
'--version','-v','version','list','ls','why','view','show','info','outdated',
|
|
3126
|
-
'-h','--help','help',
|
|
3127
|
-
]);
|
|
3128
|
-
if (!SAFE_PKG.has(sub)) return false;
|
|
3129
|
-
} else if (['node','python','python3','ruby','rustc'].includes(verb)) {
|
|
3130
|
-
const sub = tokens[1] || '';
|
|
3131
|
-
if (sub !== '--version' && sub !== '-v' && sub !== '-V') return false;
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
|
-
// STRICT path scoping. Absolute paths MUST resolve under repoRoot.
|
|
3135
|
-
// Home-relative (~/...) paths fall through to the LLM. Relative paths
|
|
3136
|
-
// are implicitly under cwd which is the repo root for the agent session.
|
|
3137
|
-
if (!repoRoot) return false;
|
|
3138
|
-
for (let i = 1; i < tokens.length; i++) {
|
|
3139
|
-
const t = tokens[i];
|
|
3140
|
-
const stripped = t.replace(/^['"]/, '').replace(/['"]$/, '');
|
|
3141
|
-
if (stripped.startsWith('~')) return false;
|
|
3142
|
-
if (stripped.startsWith('/')) {
|
|
3143
|
-
if (!isPathUnder(stripped, repoRoot)) return false;
|
|
3144
|
-
}
|
|
3145
|
-
}
|
|
3146
|
-
return true;
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
function isSafeInRepoRead(tName: string, cmd: string, repoRoot: string): boolean {
|
|
3150
|
-
if (tName === 'Read' || tName === 'Grep' || tName === 'Glob') return true;
|
|
3151
|
-
if (tName !== 'Bash' && tName !== 'Shell' && tName !== 'terminal' &&
|
|
3152
|
-
tName !== 'run_terminal_cmd' && tName !== 'execute_command') return false;
|
|
3153
|
-
if (!cmd || !repoRoot) return false;
|
|
3154
|
-
// Allow pipes only if EVERY segment is safe on its own. Catches
|
|
3155
|
-
// \`grep ... | head\`, \`cat foo | wc -l\`, \`git log | less\`, etc.
|
|
3156
|
-
// Empty segments (from \`||\`) cause rejection.
|
|
3157
|
-
const segments = cmd.split('|');
|
|
3158
|
-
for (const seg of segments) {
|
|
3159
|
-
const t = seg.trim();
|
|
3160
|
-
if (t.length === 0) return false;
|
|
3161
|
-
if (!isSafeBashSegment(t, repoRoot)) return false;
|
|
3162
|
-
}
|
|
3163
|
-
return true;
|
|
3164
|
-
}
|
|
3165
|
-
|
|
3166
3064
|
if (isSafeInRepoRead(toolName, command, cwd)) {
|
|
3167
3065
|
log('bashGuard ' + cmdShort + ' → instant allow (safe in-repo read)');
|
|
3168
3066
|
appendLocalTelemetry({
|
|
@@ -3309,7 +3207,7 @@ async function main() {
|
|
|
3309
3207
|
'Last user prompt: ' + (lastPrompt || 'none'),
|
|
3310
3208
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
3311
3209
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix — your job is only to detect violations.',
|
|
3312
|
-
'
|
|
3210
|
+
'The rules shown were pre-selected as the ones relevant to this command — every rule here IS relevant, do not label any "not relevant". When passing (ok=true), give a terse, specific reason each rule passes. Format: "R003: no secrets in grep args. R005: in-repo path only." Cover every rule shown.',
|
|
3313
3211
|
'Rules with preconditions (e.g. "run X before Y") are CONSUMED after the protected action completes. Use the session history timestamps to determine ordering: a precondition satisfied before the last occurrence of the protected action does NOT satisfy the next occurrence. Each new protected action needs its precondition re-satisfied.',
|
|
3314
3212
|
].filter(Boolean).join('\\n');
|
|
3315
3213
|
|
|
@@ -4363,7 +4261,7 @@ async function main() {
|
|
|
4363
4261
|
'Last user prompt: ' + (lastPrompt || 'none'),
|
|
4364
4262
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
4365
4263
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix \u2014 your job is only to detect violations.',
|
|
4366
|
-
'
|
|
4264
|
+
'The rules shown were pre-selected as the ones relevant to this command \u2014 every rule here IS relevant, do not label any "not relevant". When passing (ok=true), give a terse, specific reason each rule passes. Format: "R003: no secrets in grep args. R005: in-repo path only." Cover every rule shown.',
|
|
4367
4265
|
'Rules with preconditions (e.g. "run X before Y") are CONSUMED after the protected action completes. Use the session history timestamps to determine ordering: a precondition satisfied before the last occurrence of the protected action does NOT satisfy the next occurrence. Each new protected action needs its precondition re-satisfied.',
|
|
4368
4266
|
].filter(Boolean).join('\\n');
|
|
4369
4267
|
|
|
@@ -4398,7 +4296,7 @@ async function main() {
|
|
|
4398
4296
|
});
|
|
4399
4297
|
} else {
|
|
4400
4298
|
dispatchCapture(jwt, 'bash', 'pass', 'clean', verdict.category || 'clean',
|
|
4401
|
-
'Bash',
|
|
4299
|
+
'Bash', repo, sessionId, config.captureDepth, {
|
|
4402
4300
|
command, reasoning: verdict.reason || 'no policy violations detected',
|
|
4403
4301
|
rulesChecked: config.rules, violatedRules: [],
|
|
4404
4302
|
ccModel: model,
|
|
@@ -6429,7 +6327,7 @@ function writeConfigEnv(opts) {
|
|
|
6429
6327
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
6430
6328
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
6431
6329
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
6432
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
6330
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.5")}`
|
|
6433
6331
|
];
|
|
6434
6332
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
6435
6333
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -7916,7 +7814,7 @@ var args = process.argv.slice(2);
|
|
|
7916
7814
|
var cmd = args[0] || "";
|
|
7917
7815
|
var subArgs = args.slice(1);
|
|
7918
7816
|
function printVersion() {
|
|
7919
|
-
console.log("1.6.
|
|
7817
|
+
console.log("1.6.5");
|
|
7920
7818
|
}
|
|
7921
7819
|
function printHelp() {
|
|
7922
7820
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|