/** * Static, non-blocking risk scanner for pre-existing .claude/settings.json * content that `ruflo init`/`ruflo init --upgrade` carry forward unexamined. * * `mergeSettingsForUpgrade()` and `writeSettings()` both read a target * project's *existing* settings.json off disk and spread its `hooks` / * `permissions.allow` entries into the merged output verbatim (see * executor.ts:352-353 and :901-912) โ€” the same trust shape as the publicly * disclosed CVE-2025-59536 class (a settings.json hook payload achieving * command execution with no review step). Ruflo's own hook dispatch is a * closed set of internal handlers (hook-handler.cjs), so this isn't * remotely exploitable through Ruflo itself โ€” but a malicious fork, PR, or * compromised dependency that plants a bad settings.json before a user * runs `ruflo init`/`--upgrade` in that directory would have its payload * silently preserved and reported as a normal "merged"/"updated" result, * with zero visibility. * * This scanner is advisory-only: it never mutates its input and never * blocks a merge/write. Callers decide what to do with the findings * (surfaced as a CLI warning today). * * KNOWN LIMITATION (found by an independent adversarial review the same * night this was written โ€” see docs/dream-cycle/dream-gist-2026-08-16.md * ยง5): this is a best-effort, static, blocklist-style heuristic, NOT a * security boundary. It catches copy-pasted proof-of-concept payload * shapes; it does not and cannot catch every obfuscation a motivated * adversary could construct (string-built commands, uncommon interpreters, * novel exfil channels, etc). Treat findings as "worth a second look," * never as a guarantee of safety. */ export interface SettingsRiskFinding { location: string; snippet: string; reason: string; } /** Reasons a single hook `command` string looks risky (empty = clean). */ export declare function scanCommandStringForRisk(command: string): string[]; /** Reasons a single `permissions.allow` rule string looks risky (empty = clean). */ export declare function scanAllowRuleForRisk(rule: string): string[]; /** * Scans a settings.json-shaped object's `hooks` and `permissions.allow` * for content matching known-dangerous patterns. */ export declare function scanSettingsForRisk(settings: Record): SettingsRiskFinding[]; /** Formats findings as short, human-readable CLI warning lines. */ export declare function formatRiskFindingsAsWarnings(findings: SettingsRiskFinding[]): string[]; /** What a finding points at, precisely enough for dropAutoApproveKeys to remove only that. */ export type AutoApproveTarget = { kind: 'enableAllProjectMcpServers'; } | { kind: 'enabledMcpjsonServers'; } | { kind: 'defaultMode'; } | { kind: 'allow'; index: number; } | { kind: 'hook'; event: string; group: number; hook: number; } /** A project `.claude/helpers/` that is not ruflo's signed copy (init keeps it without --force). */ | { kind: 'helper'; name: string; }; export interface AutoApproveFinding extends SettingsRiskFinding { target: AutoApproveTarget; } /** * The auto-approve keys of a PROJECT settings file (`.claude/settings.json` or * `.claude/settings.local.json`). `isRufloHook` decides which hook commands are * ruflo's own helpers (injected so this module stays dependency-free). */ export declare function scanProjectAutoApproveKeys(settings: Record, isRufloHook: (command: string) => boolean): AutoApproveFinding[]; /** A deep copy of `settings` without the keys/entries the findings point at. Never mutates its input. */ export declare function dropAutoApproveKeys(settings: Record, findings: readonly AutoApproveFinding[]): Record; /** What `init` does when the existing settings carry auto-approve keys. */ export type RiskySettingsMode = 'stop' | 'keep' | 'drop'; export declare const RISKY_SETTINGS_HINT = "Re-run with --drop-risky-settings to write the merged settings without these keys (and replace any flagged helper with ruflo's copy), or --keep-risky-settings to keep them."; //# sourceMappingURL=settings-risk-scanner.d.ts.map