Download Latest Version dcg-x86_64-pc-windows-msvc.zip (6.3 MB) Google Add to Preferred Sources
Home / v0.15.3
Name Modified Size InfoDownloads / Week
Parent folder
dcg-aarch64-apple-darwin.tar.xz.sigstore.json 2026-10-08 3.7 kB
dcg-aarch64-pc-windows-msvc.zip.sigstore.json 2026-10-08 3.9 kB
dcg-aarch64-unknown-linux-gnu.tar.xz.sigstore.json 2026-10-08 3.8 kB
dcg-x86_64-apple-darwin.tar.xz.sigstore.json 2026-10-08 3.7 kB
dcg-x86_64-pc-windows-msvc.zip.sigstore.json 2026-10-08 3.7 kB
dcg-x86_64-unknown-linux-musl.tar.xz.sigstore.json 2026-10-08 3.8 kB
install.ps1.sigstore.json 2026-10-08 3.8 kB
install.sh.sigstore.json 2026-10-08 3.8 kB
SHA256SUMS.minisig 2026-10-08 389 Bytes
SHA256SUMS 2026-10-08 751 Bytes
install.sh.minisig 2026-10-08 390 Bytes
install.sh.sha256 2026-10-08 77 Bytes
install.sh 2026-10-08 160.3 kB
install.ps1.minisig 2026-10-08 390 Bytes
install.ps1.sha256 2026-10-08 78 Bytes
install.ps1 2026-10-08 105.0 kB
dcg-aarch64-pc-windows-msvc.zip.minisig 2026-10-08 393 Bytes
dcg-aarch64-pc-windows-msvc.zip.sha256 2026-10-08 98 Bytes
dcg-aarch64-pc-windows-msvc.zip 2026-10-08 6.1 MB
dcg-x86_64-pc-windows-msvc.zip.minisig 2026-10-08 393 Bytes
dcg-x86_64-pc-windows-msvc.zip.sha256 2026-10-08 97 Bytes
dcg-x86_64-pc-windows-msvc.zip 2026-10-08 6.3 MB
dcg-aarch64-unknown-linux-gnu.tar.xz.minisig 2026-10-08 391 Bytes
dcg-aarch64-unknown-linux-gnu.tar.xz.sha256 2026-10-08 103 Bytes
dcg-aarch64-unknown-linux-gnu.tar.xz 2026-10-08 4.8 MB
dcg-x86_64-unknown-linux-musl.tar.xz.minisig 2026-10-08 391 Bytes
dcg-x86_64-unknown-linux-musl.tar.xz.sha256 2026-10-08 103 Bytes
dcg-x86_64-unknown-linux-musl.tar.xz 2026-10-08 5.2 MB
dcg-aarch64-apple-darwin.tar.xz.minisig 2026-10-08 392 Bytes
dcg-aarch64-apple-darwin.tar.xz.sha256 2026-10-08 98 Bytes
dcg-aarch64-apple-darwin.tar.xz 2026-10-08 4.5 MB
dcg-x86_64-apple-darwin.tar.xz.minisig 2026-10-08 392 Bytes
dcg-x86_64-apple-darwin.tar.xz.sha256 2026-10-08 97 Bytes
dcg-x86_64-apple-darwin.tar.xz 2026-10-08 4.8 MB
README.md 2026-10-08 11.6 kB
v0.15.3 source code.tar.gz 2026-10-08 8.9 MB
v0.15.3 source code.zip 2026-10-08 9.3 MB
Totals: 37 Items   50.2 MB 7

This release closes commands that ran unguarded (Claude Code's Monitor tool, wrapper and remote-shell redirects, GNU tar helper programs, archive extraction into credential or .git paths) and a long list of false positives reported against 0.15.2.

Claude Code users: refresh the hook after upgrading. The hook matcher is now Bash|PowerShell|Monitor. Run dcg install (or dcg doctor --fix); hook self-healing does the same on its next run. Existing Bash or Bash|PowerShell dcg entries are migrated in place, and unrelated hooks keep their matchers.

Behaviour changes you may notice

  • A temporary-path variable proof stops at an unmodelled assignment. 0.15.2 proved > $SP/out.txt safe from an earlier SP=/tmp/... even when a later assignment took its value from a command dcg does not model (N=$(python3 -c ...), N=$(basename "$BIN" | cut -c1-16)). Such redirects are now denied as core.filesystem:redirect-truncate-dynamic-path. Redirect before that assignment, or write the literal path. In a replay of 6,000 real agent commands this changed three verdicts from allow to deny; the [#536] fixes below changed one from deny to allow.
  • GNU tar helper programs are judged. --checkpoint-action=exec=, --to-command, --use-compress-program/-I, --info-script/-F, --new-volume-script and --rmt-command run code. Their payloads now go through the packs; a helper dcg cannot verify is denied as core.filesystem:tar-exec-unverified.
  • rm of a path inside .git names the real risk. rm .git/index.lock is still denied under core.filesystem:rm-protected-file, but the reason now says it deletes repository state, not a credential or login-startup file, and how to clear a stale index.lock safely. A command that also removes a credential keeps the credential reason (#533).
  • git branch -d is reported as core.git:branch-delete; see Git branch rules below (#509).

Agent integrations

  • Claude Code: Monitor scripts are judged (#529). Claude Code's Monitor tool runs its command as a POSIX shell script. The hook matcher did not include it, so those scripts never reached dcg. dcg install, the installers, dcg doctor --fix and hook self-healing now write Bash|PowerShell|Monitor.
  • Codex: exact-command review for denials (#537). A Codex denial now carries its allow-once code in permissionDecisionReason, keeping Codex's three-field denial JSON. The grant matches only the exact original request, is single-use, and cannot be consumed by a nested payload or an altered wrapper, substitution or whitespace.
  • pi: the recipe evaluates with --dialect posix (#532). Without it, pi's commands were read with zsh-only rules and some were denied falsely.
  • OpenCode: the plugin loads again (#516). OpenCode 1.3.4 and later read a default export that has an id as a plugin module and refuse it without server(). The plugin generated by 0.15.0 through 0.15.2 exported { id, setup } for OpenCode v2, so current OpenCode (1.18.33 in the report) refused the whole file at startup and ran every bash command unguarded. The default export now also carries server(), which returns the same hook map as the named DcgGuard export; v2 ignores the extra key. Verified against a real OpenCode 1.18.33 run. Regenerate the plugin (dcg install --opencode --force, or dcg update) and restart OpenCode; dcg doctor reports an old plugin as outdated.
  • Cursor: the bridge fails closed (#517). The beforeShellExecution bridge allowed every command it could not check. On Windows that was every command: cursor-agent sends a UTF-8 BOM, Windows PowerShell 5.1 decoded it with the OEM code page, the JSON parse failed, and the bridge allowed. Both bridges (PowerShell and the Unix Python one) now read stdin as bytes and drop a BOM, send dcg UTF-8, and ask dcg for an explicit allow, so an unreadable payload or a dcg that ran without a verdict (killed, timed out, non-zero exit, no answer) is denied. DCG_BRIDGE_CRASH_DECISION=allow restores fail-open for those; a dcg that cannot be started at all is still allowed, with a notice on stderr. Re-run the installer to regenerate the bridge.
  • Cursor: Claude Code hooks see Shell (#518). Cursor runs the PreToolUse hooks in ~/.claude/settings.json and renames Claude Code's Bash tool to Shell. dcg did not know the name and allowed every such command; it now judges them and answers in the Claude shape Cursor reads. The other supported agents' tool names were audited and needed no change.

Command history

  • dcg history analyze on an empty history now says there is nothing to analyze and how to enable [history], instead of green "no coverage gaps" checks and advice to disable packs, core first, drawn from zero commands. The JSON output gains has_data. With data, a pack that never matched is still listed but is no longer recommended for removal: a guard pack that stays quiet is working (#513).
  • History rows now record the machine's hostname; it was always NULL (#514).
  • The docs and dcg history analyze --help now state that exit_code is NULL on every row the hook writes, because the hook runs before the command and cannot know how it ended. NULL means unknown, not success (#515).

Commands that ran unguarded

  • A wrapper no longer swallows a redirect (#531, [#521]). nice -n > ~/.bashrc was allowed because the wrapper parser read > as the value of -n; it is now judged as the credential-file write it is. A wrapper option with no operand (nice -n >~/x) panicked; it is now rejected without a panic.
  • A redirect in an ssh script is judged for the remote host (#534). A literal script piped or fed to ssh or to an interactive container exec shell was judged against the local disk, so a local file's absence could prove a remote overwrite safe. Such scripts are now judged in the receiving environment's filesystem scope; an unknown producer or unbounded nested dispatch fails closed.
  • Local files are not evidence after a wrapper changes context. Input files opened after a recognised wrapper changes directory, user, home or environment (sudo -u, env -C, remote and container shells) were checked against the caller's files. Those inputs are now treated as unverified.
  • Archive extraction into credentials or .git. GNU/BSD tar, unzip and 7z extracting into an explicit credential or .git destination (including bundled C options such as tar xfC and unknown member lists) is denied; list, create and stdout modes are unaffected.
  • Complete chmod/chown/chgrp parsing in the permissions pack. The prefix-only exemption could hide a later recursive flag, a protected target, or a different operation whose option value contained safe-looking text. Every option and target is now checked.
  • Windows Git redirects keep their own rules (#492). Allowlisting redirect-truncate-dynamic-path, or one Git redirect, could conceal a separate Git or credential write spelled with Windows separators in the same command. Every truncate/append hit is now kept and judged under its own rule.
  • An executable whose argument count cannot be bounded now fails closed instead of having its arguments shifted.

False positives fixed

  • A JavaScript arrow function (=> !x) in a quoted heredoc is not read as a redirect (#519).
  • A Python heredoc containing a regex no longer switches macOS commands to Windows parsing (#520), and ^ in a python3 - heredoc no longer widens the dialect into a PowerShell scan (#523).
  • A quoted heredoc piped to python3, node, ruby or perl is analysed as that language, not as POSIX shell (#522).
  • A multi-line for loop with a quoted |^ in sed (#524), and a quoted heredoc appended to a file after sed read it (#525), are no longer denied as heredoc.shell:launcher-unverified.
  • A provable loop variable after a literal path prefix (#526), and provable /tmp values from an && assignment, a chained variable, $$, or mktemp -d with a /tmp template (#536), satisfy redirect-truncate-dynamic-path. Resolution is bounded (eight binding levels, 4096 expanded bytes, 256 segments) and still denies traversal, protected names and mutated bindings.
  • An embedded process call with a non-literal argument keeps its words in place; dropping it shifted the rest and produced false core.git:git-alias-semantic-unverified denials (#527).
  • --command "<string>" after a variable-path flowctl gate check/gate receipt is treated as receipt data, not an inline launcher. Only the fixed flowctl gate grammar is exempt; unknown executables with --command stay denied (#538).

Embedded code and heredocs

  • An inline-shell launcher quoted as prose inside another command's argument (tracker comment 1 "example: bash -c 'git reset --hard' is refused") is no longer judged as if it ran. A launcher that opens the quoted text, follows a separator or wrapper inside it, sits in a substitution, or is in a string that eval/ssh/watch run is still judged (#510).
  • awk printing into a shell that reads its stdin (print "git reset --hard" | "sh") now has the printed text judged as the command it is, including through a variable the program assigns one literal. A computed print into such a shell is denied as heredoc.posix:pipeline-consumer, like the shell-level awk '…' | sh (#511).
  • A single string given to a shell sink is now judged by every pack, not only the few rm/git payloads the per-language catalogue knows: perl -e 'system("git push --force origin main")' and system("find . -delete") are denied, as are the same strings through Python os.system, Ruby system, PHP system/shell_exec and Node child_process.execSync. Perl backtick commands go the same way, and qx is recognised with any delimiter (qx{…}, qx(…), qx[…], …), not only qx/…/. Payloads the catalogue already reports keep their heredoc.* rule ids (#512).

CLI

  • dcg config (text and --format json) now reports every effective [general] setting, including unverified_decision, update_pin, check_updates, max_hook_input_bytes, max_command_bytes and max_findings_per_command, with environment overrides applied (#530).
  • dcg doctor judges the Claude hook only on the keys dcg owns (type, command, shell). A hook entry with an extra key Claude Code supports, such as timeout, is no longer reported as misconfigured, and dcg install --force keeps it. An async hook is still an error, because Claude Code would not wait for its verdict (#528).

Git branch rules

  • git branch -d / --delete without force is now reported as core.git:branch-delete. -D, --delete --force, -df, -f, -M and -C stay on core.git:branch-force-delete. Both are still denied by default. Setting [policy.rules] "core.git:branch-delete" = "ask" lets an agent propose merged-branch cleanup for approval while forced deletion stays denied, which no configuration could express before (#509).
  • Existing configuration keeps its meaning: a [policy.rules] or allowlist entry for core.git:branch-force-delete still covers -d unless core.git:branch-delete has an entry of its own, which then wins. Tools that read ruleId from deny output or history will see the new id for -d.
Source: README.md, updated 2026-10-08