This is the record of an abcd-managed repository

abcdBetav0.16.2 · 2026-11-06

Development record

GHSA-m2r8-fx7r-rq34 (draft advisory, severity medium; CWE-184, Incomplete List of Disallowed Inputs): the guard allows a git hazard whose subcommand git rewrites from in-process config the matcher steps over. `operands` (internal/core/guard/match.go) advances past the value of every flag in an entry's value_flags without reading it, and every bundled git entry lists `-c` and `--config-env` there (gh-299); `commandOf` steps `NAME=VALUE` prefixes the same way. So the alias body is the token stepped over and the attacker-chosen name is what reaches operand 0: `git -c alias.p='push --force' p origin main`, `FORCE_ALIAS='push --force' git --config-env alias.p=FORCE_ALIAS p origin main`, `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=alias.p GIT_CONFIG_VALUE_0='push --force' git p origin main`, the unlisted sibling `GIT_CONFIG_PARAMETERS="'alias.p=push --force'" git p origin main` (verified live on git 2.52), and the bang-form `git -c alias.p='!<shell command>' p` are all silent allows while git runs the rewritten command. The fix must add a core pre-pass in Check that, for a segment whose command is git, collects the in-segment alias declarations from those four sources and, when operand 0 names one, appends a synthetic segment with the alias expanded (the original global flags, the body's tokens, the arguments after operand 0) so Tier 1 matches it with the existing entries; a bang-form body is handed to shellInspect exactly as an exec-string payload; nested aliases are followed a bounded number of hops. What the string cannot decide: `GIT_CONFIG_GLOBAL=file`, `GIT_CONFIG_SYSTEM=file`, `-c include.path=file` and includeIf deliver the alias body in a FILE, the same class as adr-42's permanently-invisible residual (make targets, shell functions, aliases) — the directive is visible, so each raises a loud synthetic warn (`git-config-rewrite-unread`, also raised for a `--config-env` whose variable is not assigned in the segment), measured against the corpus warn ceiling, which carries zero such lines. Hiding-alias regression decided on the side of fewer moving parts: git ignores an alias that names a builtin, so `git -c alias.push='push --force' push origin main` is a harmless push that the rewrite over-blocks; that over-block is accepted as the fail-safe direction and pinned as a known false positive, rather than shipping a probe-synced 146-name builtin table. `git -c alias.st=status st` allows under the rewrite because its expansion is benign. Unswept family, out of this advisory's scope: the same stepped-over value running a command on a benign subcommand — `-c core.pager=<cmd>`, core.editor, core.sshCommand, credential.helper=!…, diff.external, filter.*.clean — which no entry names; and the registry has no bare `rm -rf /` entry, so the advisory's bang-form example inspects cleanly and allows on the registry's terms (adr-42 decision 5), the mechanism being what this fixes.

issueresolvedmajoriss-2609012040016236

GHSA-m2r8-fx7r-rq34 (draft advisory, severity medium; CWE-184, Incomplete List of Disallowed Inputs): the guard allows a git hazard whose subcommand git rewrites from in-process config the matcher steps over. operands (internal/core/guard/match.go) advances past the value of every flag in an entry's value_flags without reading it, and every bundled git entry lists -c and --config-env there (gh-299); commandOf steps NAME=VALUE prefixes the same way. So the alias body is the token stepped over and the attacker-chosen name is what reaches operand 0: git -c alias.p='push --force' p origin main, FORCE_ALIAS='push --force' git --config-env alias.p=FORCE_ALIAS p origin main, GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=alias.p GIT_CONFIG_VALUE_0='push --force' git p origin main, the unlisted sibling GIT_CONFIG_PARAMETERS="'alias.p=push --force'" git p origin main (verified live on git 2.52), and the bang-form git -c alias.p='!<shell command>' p are all silent allows while git runs the rewritten command. The fix must add a core pre-pass in Check that, for a segment whose command is git, collects the in-segment alias declarations from those four sources and, when operand 0 names one, appends a synthetic segment with the alias expanded (the original global flags, the body's tokens, the arguments after operand 0) so Tier 1 matches it with the existing entries; a bang-form body is handed to shellInspect exactly as an exec-string payload; nested aliases are followed a bounded number of hops. What the string cannot decide: GIT_CONFIG_GLOBAL=file, GIT_CONFIG_SYSTEM=file, -c include.path=file and includeIf deliver the alias body in a FILE, the same class as adr-42's permanently-invisible residual (make targets, shell functions, aliases) — the directive is visible, so each raises a loud synthetic warn (git-config-rewrite-unread, also raised for a --config-env whose variable is not assigned in the segment), measured against the corpus warn ceiling, which carries zero such lines. Hiding-alias regression decided on the side of fewer moving parts: git ignores an alias that names a builtin, so git -c alias.push='push --force' push origin main is a harmless push that the rewrite over-blocks; that over-block is accepted as the fail-safe direction and pinned as a known false positive, rather than shipping a probe-synced 146-name builtin table. git -c alias.st=status st allows under the rewrite because its expansion is benign. Unswept family, out of this advisory's scope: the same stepped-over value running a command on a benign subcommand — -c core.pager=<cmd>, core.editor, core.sshCommand, credential.helper=!…, diff.external, filter.*.clean — which no entry names; and the registry has no bare rm -rf / entry, so the advisory's bang-form example inspects cleanly and allows on the registry's terms (adr-42 decision 5), the mechanism being what this fixes.

Grounds

  • pursued: every value this reads is already in the string the matcher holds and already tokenised, so reading it adds no enumeration and no filesystem access — the defect is a value the registry deliberately consumes and never inspects, inside adr-42's mistake filter rather than in its abandoned-completeness residual; this would be shown wrong by the repo corpus starting to warn or by an ordinary git idiom blocking, which the corpus ceiling and the pinned allow rows would surface

Frontmatter

idiss-2609012040016236
typeissue
lifecycleresolved
severitymajor

mentions1