mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-24 13:08:11 +08:00
ROADMAP #115: claw init hardcodes 'defaultMode: dontAsk' alias for danger-full-access; init output zero security signal; JSON wraps prose
Dogfooded 2026-04-18 on main HEAD ca09b6b from /tmp/cdPP.
Three compounding issues in one finding:
1. claw init generates .claw.json with dangerous default:
$ claw init && cat .claw.json
{"permissions":{"defaultMode":"dontAsk"}}
$ claw status | grep permission_mode
permission_mode: danger-full-access
2. The 'dontAsk' alias obscures the actual security posture:
config.rs:858 "dontAsk" | "danger-full-access" =>
Ok(ResolvedPermissionMode::DangerFullAccess)
User reads 'dontAsk' as 'skip confirmations I'd otherwise see'
— NOT 'grant every tool unconditional access'. But the two
parse identically. Alias name dilutes severity.
3. claw init --output-format json wraps prose in message field:
{
"kind": "init",
"message": "Init\n Project /private/tmp/cdPP\n
.claw/ created\n..."
}
Claws orchestrating setup must string-parse \n-prose to
know what got created. No files_created[], no
resolved_permission_mode, no security_posture.
Zero mention of 'danger', 'permission', or 'access' anywhere
in init output. The init report says 'Review and tailor the
generated guidance' — implying there's something benign to tailor.
Trace:
rusty-claude-cli/src/init.rs:4-9 STARTER_CLAW_JSON constant:
hardcoded {"permissions":{"defaultMode":"dontAsk"}}
runtime/src/config.rs:858 alias resolution:
"dontAsk" | "danger-full-access" => DangerFullAccess
rusty-claude-cli/src/init.rs:370 JSON-output also emits
'defaultMode': 'dontAsk' literal.
grep 'dontAsk' rust/crates/ → 4 matches. None explain that
dontAsk == danger-full-access anywhere user-facing.
Fix shape (~60 lines):
- STARTER_CLAW_JSON default → 'default' (explicit safe). Users
wanting danger-full-access opt in. ~5 lines.
- init output warns when effective mode is DangerFullAccess:
'security: danger-full-access (unconditional tool approval).'
~15 lines.
- Structure the init JSON:
{kind, files:[{path,action}], resolved_permission_mode,
permission_mode_source, security_warnings:[]}
~30 lines.
- Deprecate 'dontAsk' alias OR log warning at parse: 'alias for
danger-full-access; grants unconditional tool access'. ~8 lines.
- Regression tests per outcome.
Builds on #87 and amplifies it:
#87: absence-of-config default = danger-full-access
#101: fail-OPEN on bad RUSTY_CLAUDE_PERMISSION_MODE env var
#115: init actively generates the dangerous default
Three sequential compounding permission-posture failures.
Joins Permission-audit/tool-allow-list (#94, #97, #101, #106)
as 5th member — init-time anchor of the permission problem.
Joins Silent-flag/documented-but-unenforced on silent-setting
axis. Cross-cluster with Reporting-surface/config-hygiene
(prose-wrapped JSON) and Truth-audit (misleading 'Next step'
phrasing).
Natural bundle: #87 + #101 + #115 — 'permission drift at every
boundary': absence default + env-var bypass + init-generated.
Flagship permission-audit sweep grows 7-way:
#50 + #87 + #91 + #94 + #97 + #101 + #115
Filed in response to Clawhip pinpoint nudge 1494917922076889139
in #clawcode-building-in-public.
This commit is contained in:
parent
ca09b6b374
commit
ad02761918
97
ROADMAP.md
97
ROADMAP.md
@ -3512,3 +3512,100 @@ ear], /color [scheme], /effort [low|medium|high], /fast, /summary, /tag [label],
|
||||
**Blocker.** None. The fix is symmetric code-path alignment. Option A for `/clear` is a ~20-line change. Total ~90 lines + tests.
|
||||
|
||||
**Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdNN` and `/tmp/cdOO` on main HEAD `43eac4d` in response to Clawhip pinpoint nudge at `1494895272936079493`. Joins **Session-handling** (#93, #112, #113) — now 4 items: reference-resolution semantics (#93), concurrent-modification (#112), programmatic management gap (#113), and reference/enumeration asymmetry (#114). Complete session-handling cluster. Joins **Truth-audit / diagnostic-integrity** on the `/session list` output being factually wrong. Cross-cluster with **Parallel-entry-point asymmetry** (#91, #101, #104, #105, #108) — #114 adds "entry points that read the same underlying data produce mutually inconsistent identifiers." Natural bundle: **#93 + #112 + #113 + #114** (session-handling quartet — complete coverage). Alternative: **#104 + #114** — /clear filename semantics + /export filename semantics both hide session identity in the filename rather than the content. Session tally: ROADMAP #114.
|
||||
|
||||
115. **`claw init` generates `.claw.json` with `"permissions": {"defaultMode": "dontAsk"}` — where "dontAsk" is an alias for `danger-full-access`, hardcoded in `rust/crates/runtime/src/config.rs:858`. The init output is prose-only with zero mention of "danger", "permission", or "access" — a claw (or human) running `claw init` in a fresh project gets no signal that the generated config turns permissions off. `claw init --output-format json` returns `{kind: "init", message: "<multi-line prose with \n literals>"}` instead of structured `{files_created: [...], defaultMode: "dontAsk", security_posture: "danger-full-access"}`. The alias choice itself ("dontAsk") obscures the behavior: a user seeing `"defaultMode": "dontAsk"` in their new repo naturally reads it as "don't ask me to confirm" — NOT "grant every tool every permission unconditionally" — but the two are identical per the parser at `config.rs:858`. `claw init` is effectively a silent bootstrap to maximum-permissions mode** — dogfooded 2026-04-18 on main HEAD `ca09b6b` from `/tmp/cdPP`.
|
||||
|
||||
**Concrete repro.**
|
||||
```
|
||||
$ cd /tmp/cdPP && git init -q .
|
||||
$ claw init
|
||||
Init
|
||||
Project /private/tmp/cdPP
|
||||
.claw/ created
|
||||
.claw.json created
|
||||
.gitignore created
|
||||
CLAUDE.md created
|
||||
Next step Review and tailor the generated guidance
|
||||
# No mention of security posture, permission mode, or "danger".
|
||||
|
||||
$ claw init --output-format json
|
||||
# Actually: claw init produces its own structured output:
|
||||
{
|
||||
"kind": "init",
|
||||
"message": "Init\n Project /private/tmp/cdPP\n .claw/ created\n .claw.json created\n..."
|
||||
}
|
||||
# The entire init report is a \n-embedded prose blob inside `message`.
|
||||
|
||||
$ cat .claw.json
|
||||
{
|
||||
"permissions": {
|
||||
"defaultMode": "dontAsk"
|
||||
}
|
||||
}
|
||||
|
||||
$ claw status --output-format json | python3 -c "import json,sys; d=json.load(sys.stdin); print('permission_mode:', d['permission_mode'])"
|
||||
permission_mode: danger-full-access
|
||||
# "dontAsk" in .claw.json resolves to danger-full-access at load time.
|
||||
|
||||
$ claw init 2>&1 | grep -iE "danger|permission|access"
|
||||
(nothing)
|
||||
# Zero warning anywhere in the init output.
|
||||
```
|
||||
|
||||
**Trace path.**
|
||||
- `rust/crates/rusty-claude-cli/src/init.rs:4-9` — `STARTER_CLAW_JSON` constant:
|
||||
```rust
|
||||
const STARTER_CLAW_JSON: &str = concat!(
|
||||
"{\n",
|
||||
" \"permissions\": {\n",
|
||||
" \"defaultMode\": \"dontAsk\"\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
);
|
||||
```
|
||||
Hardcoded dangerous default. No audit hook. No template choice. No "safe by default" option.
|
||||
- `rust/crates/runtime/src/config.rs:858` — alias resolution:
|
||||
```rust
|
||||
"dontAsk" | "danger-full-access" => Ok(ResolvedPermissionMode::DangerFullAccess),
|
||||
```
|
||||
"dontAsk" is semantically identical to "danger-full-access." The alias is the fig leaf; the effect is identical.
|
||||
- `rust/crates/rusty-claude-cli/src/init.rs:370` — the JSON-output path also emits `"defaultMode": "dontAsk"` literally. Prose path and JSON path agree on the payload; both produce the dangerous default.
|
||||
- `rust/crates/rusty-claude-cli/src/init.rs` init runner — returns `InitReport` that becomes `{kind: "init", message: "<multi-line prose>"}`. No `files_created: [...]`, no `resolved_permission_mode`, no `security_posture` field.
|
||||
- `grep -rn "dontAsk" rust/crates/` — only four matches: `tools/src/lib.rs:5677` (option enumeration for a help string), `runtime/src/config.rs:858` (alias resolution), and two entries in `rusty-claude-cli/src/init.rs`. No UI string anywhere explains that dontAsk equals danger-full-access.
|
||||
|
||||
**Why this is specifically a clawability gap.**
|
||||
1. *Silent security-posture drift at bootstrap.* A claw (or a user) running `claw init` in a fresh repo gets handed an unconditionally-permissive workspace with no in-band signal. The only way to learn the security posture is to read the config file yourself and cross-reference it against the parser's alias table.
|
||||
2. *Alias naming conceals severity.* `dontAsk` is a user-friendly phrase that reads as "skip the confirmations I would otherwise see." It hides what's actually happening: *every tool unconditionally approved, no audit trail, no sandbox*. If the literal key were `"danger-full-access"`, users would recognize what they're signing up for. The alias dilutes the warning.
|
||||
3. *Init is the onboarding moment.* Whatever `init` generates is what users paste into git, commit, share with colleagues, and inherit across branches. A dangerous default here propagates through every downstream workspace.
|
||||
4. *JSON output is prose-wrapped.* `claw init --output-format json` returns `{kind: "init", message: "<prose with \n>"}`. A claw orchestrating project setup must string-parse `"` `\n" "separated lines"` to learn what got created. No `files_created: [...]`, no `resolved_permission_mode`, no `security_posture`. This joins #107 / #109 (structured-data-crammed-into-a-prose-field) as yet another machine-readable surface that regresses on structure.
|
||||
5. *Builds on #87 and amplifies it.* #87 identified that a workspace with no config silently defaults to danger-full-access. #115 identifies that `claw init` actively GENERATES a config that keeps that default, and obscures the name ("dontAsk"), and surfaces it via a prose-only init report. Three compounding failures on the same axis.
|
||||
6. *Joins truth-audit.* The init report says "Next step: Review and tailor the generated guidance" — implying there is something to tailor that is not a trap. A truthful message would say "`claw init` configured permissions.defaultMode = 'dontAsk' (alias for danger-full-access). This grants all tools unconditional access. Consider changing to 'default' or 'plan' for stricter prompting."
|
||||
7. *Joins silent-flag / documented-but-unenforced cluster.* Help / docs do not clarify that "dontAsk" is a rename of "danger-full-access." The mode string is user-facing; its effect is not.
|
||||
|
||||
**Fix shape — change the default, expose the resolution, structure the JSON.**
|
||||
1. *Change `STARTER_CLAW_JSON` default.* Options: (a) `"defaultMode": "default"` (prompt for destructive actions). (b) `"defaultMode": "plan"` (plan-first). (c) Leave permissions block out entirely and fall back to whatever the unconfigured-default should be (currently #87's gap). **Recommendation: (a) — explicit safe default. Users who WANT danger-full-access can opt in.** ~5-line change.
|
||||
2. *Warn in init output when the generated config implies elevated permissions.* If the effective mode resolves to `DangerFullAccess`, the init summary should include a one-line security annotation: `security: danger-full-access (unconditional tool approval). Change .claw.json permissions.defaultMode to 'default' to require prompting.` ~15 lines.
|
||||
3. *Structure the init JSON output.* Replace the prose `message` field with:
|
||||
```json
|
||||
{
|
||||
"kind": "init",
|
||||
"files": [
|
||||
{"path": ".claw/", "action": "created"},
|
||||
{"path": ".claw.json", "action": "created"},
|
||||
{"path": ".gitignore", "action": "created"},
|
||||
{"path": "CLAUDE.md", "action": "created"}
|
||||
],
|
||||
"resolved_permission_mode": "danger-full-access",
|
||||
"permission_mode_source": "init-default",
|
||||
"security_warnings": ["permission mode resolves to danger-full-access via 'dontAsk' alias"]
|
||||
}
|
||||
```
|
||||
Claws can consume this directly. Keep a `message` field for the prose, but sole source of truth for structure is the fields. ~30 lines.
|
||||
4. *Deprecate the "dontAsk" alias OR add an explicit audit-log when it resolves.* Either remove the alias entirely (callers pick the literal `"danger-full-access"`) or log a warning at parse time: `permission mode "dontAsk" is an alias for "danger-full-access"; grants unconditional tool access`. ~8 lines.
|
||||
5. *Regression test.* `claw init` followed by `claw status --output-format json` where the test expects either `permission_mode != danger-full-access` (after changing default) OR the init output includes a visible security warning (if the dangerous default is kept).
|
||||
|
||||
**Acceptance.** `claw init` in a fresh repo no longer silently configures `danger-full-access`. Either (a) the default is safe, or (b) if the dangerous default remains, the init output — both prose and JSON — carries an explicit `security_warnings: [...]` field that a claw can parse. The alias "dontAsk" either becomes a warning at parse time or resolves to a safer mode.
|
||||
|
||||
**Blocker.** Product decision: is `init`-default `danger-full-access` intentional (for low-friction onboarding) or accidental? If intentional, the fix is warning-only. If accidental, the fix is a safer default.
|
||||
|
||||
**Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdPP` on main HEAD `ca09b6b` in response to Clawhip pinpoint nudge at `1494917922076889139`. Joins **Permission-audit / tool-allow-list** (#94, #97, #101, #106) as 5th member — this is the init-time ANCHOR of the permission-posture problem: #87 is absence-of-config, #101 is fail-OPEN on bad env var, **#115** is the init-generated dangerous default. Joins **Silent-flag / documented-but-unenforced** (#96–#101, #104, #108, #111) on the third axis: not a silent flag, but a silent setting (the generated config's security implications are silent in the init output). Cross-cluster with **Reporting-surface / config-hygiene** (#90, #91, #92, #110) on the structured-data-vs-prose axis: `claw init --output-format json` wraps all structure inside `message`. Cross-cluster with **Truth-audit** on "Next step: Review and tailor the generated guidance" phrasing — misleads by omission. Natural bundle: **#87 + #101 + #115** — "permission drift at every boundary": absence default + env-var bypass + init-generated default. Also: **#50 + #87 + #91 + #94 + #97 + #101 + #115** — flagship permission-audit sweep now 7-way. Session tally: ROADMAP #115.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user