Skip to content

refactor(gitleaks): port scanner task orchestration to Go #814

Description

@tbhb-agent

Problem

Part of #588. This is the existing gitleaks task-orchestration slice of #329.

At origin/main (a0347a715fab580ece45edfcb3d13509620459a8), devtools:gitleaks is still an inline shell task that runs the pinned scanner, preserves its status and rejects an unexplained zero-commit scan (.devtools/tasks/security.toml:24). Its current invocation is gitleaks git --verbose --redact . (.devtools/tasks/security.toml:33), with scanner version 8.30.1 pinned at .config/mise/conf.d/10-devtools-tools.toml:48, matching #551's examined scanner contract. Move that orchestration to tested Go; keep #551's separate scan-boundary change explicit.

Acceptance criteria

  1. AC1: The migrated task retains the current scanner invocation and repository configuration behavior, including all-ref history coverage; this port cannot narrow coverage to the checked-out branch.
  2. AC2: Scanner output remains redacted and retains the current rule, repository-relative path, line and permitted verbose metadata, with the scanner's nonzero status preserved.
  3. AC3: A scan without a positive scanned-commit count fails with the current guard diagnostic unless HEAD is readable and its validated history has no patch content; that legitimate contentless case retains the scanner status and current explanatory notice.
  4. AC4: Missing repository, unborn HEAD, failed Git validation and nonzero scanner/start/read failures cannot become a successful guard result, and a scanner's partial output cannot replace its nonzero exit status.
  5. AC5: Existing scanner rules and exact fixture exceptions retain their current results; default private-key detection remains enabled outside an explicitly accepted exception.
  6. AC6: Existing standalone, aggregate, commit, PR and reusable-workflow callers retain their task name, argument behavior and failure propagation, including extra task arguments currently ignored by the frozen body; this port adds no direct merge-time or pre-push scan and changes no workflow trigger or history checkout.
  7. AC7: Non-functional: the task ends with a thin shell wrapper that probes Go subcommand support without writes or consuming input, uses the frozen task shell when binary or capability is absent, and preserves supported execution's streams and status without fallback or a repeated scan after failure.
  8. AC8: Non-functional: native orchestration works on Linux, macOS and Windows without an N-2 configuration or binary-floor change.
  9. AC9: Help and adoption documentation describe the unchanged default scope, zero-count guard, private-scanner distinction, fallback and rollback, without claiming fix(leaks): scope local gates to branch history and the staged tree #551's local mode already exists.
  10. AC10: Non-functional: orchestration leaves repository refs, index and working files unchanged.

Scope and non-goals

In scope: only the task orchestration and its consumer wrapper, caller compatibility, documentation and tests. Current callers include mise.toml:748, mise.toml:813, packages/agents/source/skills/commit/shared/scripts/commit.sh:165, packages/agents/source/skills/pr/shared/scripts/create-pr.sh:191 and .github/workflows/scan-gitleaks.yml:37. Scanner-specific regressions are in tests/tasks/gitleaks.bats:16, tests/tasks/gitleaks.bats:32, tests/tasks/gitleaks.bats:55, tests/tasks/gitleaks.bats:62, tests/tasks/gitleaks.bats:75, tests/tasks/gitleaks.bats:89, tests/tasks/gitleaks.bats:98 and tests/tasks/gitleaks.bats:128.

Overlap: AC1 absorbs the unchanged all-ref CI contract of #551 AC6; AC2 and AC5 absorb the existing redaction and rule/exception preservation part of #551 AC8; AC3 and AC4 absorb only the already-existing status/zero-count guard part of #551 AC5. #551 still owns explicit local/ci modes, strict origin-base selection, branch-added history, full-index scanning, its caller-mode changes, additional zero-exit diagnostic hardening, documentation of those new modes and exact-head approval of reduced local coverage. #573 is its implementation, not evidence that those changes are already on this baseline. This port neither implements nor closes #551. Future #551 behavior must not extend this port's frozen fallback.

No #501 source-loading AC is absorbed by gitleaks: AC6 preserves the independent private-scanner gate alongside it. All private-list lookup, notices and provisioning remain #501, and this issue does not close it.

Non-goals: implementing the scanner, changing/upgrading its rules or version, changing allowed markers, adding baselines, parsing allow-marker justification, changing the private-strings scanner, narrowing local history or CI coverage, or changing workflow triggers. This port does not silently adopt #551's intentional policy change. Compatibility fallback removal is a separate N-2 follow-up under D9.

Approach

Move the scoped orchestration to tested Go, with failing cases first. The mechanism is the implementer's choice within these outcomes. Keep the existing task shell frozen for compatibility.

Dependencies

Blocked by #753 before replacement enforcement is activated; cite its merged protection evidence. #329 tracks the migration and #661 governs compatibility. #551 and #573 own the excluded local-mode change; #501 owns private-list sourcing. #333 supplies binary distribution without raising the minimum pin required by this port.

Size

S. One pull request for existing scanner orchestration, its wrapper and regression coverage.

Verify plan

First write failing Go cases for a scanner that prints a clean-looking result then exits nonzero and a contentful history reported as zero scanned commits. Port every case in tests/tasks/gitleaks.bats before reducing shell coverage.

  • AC1: a recording scanner asserts exact argv and configuration discovery. Disposable fixture repositories contain a synthetic finding on the checked-out history, an unrelated local branch, a fetched branch and a tag independently; each retains current all-ref detection. Compare results with the frozen task.
  • AC2: scanner stubs with statuses 0, 1 and another nonzero status and output on both streams, plus a real synthetic-token fixture; compare the task's combined scanner-output stream and status with shell. Assert rule, relative location, line and allowed verbose metadata and absence of synthetic secret bytes.
  • AC3: positive counts, zero counts in contentful history, empty-file root commits, contentless commit histories and scanner nonzero status in a legitimate contentless history; assert exact guard status and diagnostics and retention of scanner failure.
  • AC4: outside-repository, unborn, failed HEAD/history validation, missing scanner, failed output storage/read and output-then-failure cases; none may report clean completion. Compare existing failure statuses and safe diagnostics with the frozen task. Broader zero-exit diagnostic interpretation remains fix(leaks): scope local gates to branch history and the staged tree #551.
  • AC5: run the default private-key fixture, exact synthetic-key exception and neighboring/prefixed/suffixed paths from the current Bats suite; verify default rules still detect findings outside the exact exception.
  • AC6: recording caller fixtures and unchanged workflow assertions prove the same task and arguments run with blocking failure propagation for standalone, aggregates, commit and PR, including unattended forms, and reusable CI. Run extra-argument fixtures against the frozen body and Go wrapper and preserve their ignored-argument behavior; mode parsing remains fix(leaks): scope local gates to branch history and the staged tree #551. Assert no new merge/pre-push scan, trigger or checkout change; keep the independent private-scanner call.
  • AC7: actual task with missing binary, older binary lacking capability, supported success/failure and clean-looking output followed by failure; assert a side-effect-free probe, one scan, exact streams/status and no fallback after supported execution. Probe errors must refuse instead of guessing absent capability.
  • AC8: native platform matrix and unchanged N-2 fixtures; use shell parity where Bash exists. Record 20 warm task invocations using the same recording scanner for shell and Go to isolate orchestration overhead; investigate regressions.
  • AC10: compare refs, index and working files before and after each outcome.
  • AC9: compare help/adoption text with the tested invocation and guard; verify fallback and rollback in a disposable fixture and explicitly check that local-mode support is not claimed.
  • Run the branch implementation through mise run test-hooks tests/tasks/gitleaks.bats and affected caller/workflow regressions, then mise run check, fresh coverage and both coverage floors. Verify build(agents): protect replacement Go enforcement paths #753 protects enforcement and registration. If maintained skill sources change, run generation, token measurement and deployment in order, then mise run check-generated-skills.
  • Definition of done: D1 each AC is verified at the merged head, with the commands and results cited in the pull request's Verification. D2 an independent review approves the exact merged head, and every finding has a recorded disposition. D3 required checks pass with no gate loosened. D4 the leak scans the repository specifies are clean, and every post made for this work (comments, descriptions) passes the per-post leak review before it is posted. D5 every commit is signed. D6 the documentation matches, or the description says No doc impact with the reason. D7 every commit carries an Assisted-by trailer and the pull request carries the provenance:agent-assisted label. D8 the change stays within the pull request size limits. D9 follow-up work, including deferred review findings, is filed as issues, each with a Problem section and numbered acceptance criteria and linking this issue; the pull request's Related section lists them, and any code that defers work links its issue. D10 the merge closes this issue through a closing reference, the project board shows the state Done, and a final Status: comment names the merge commit.

Risks

A lost subprocess status or false zero-count exception can turn an incomplete scan green. This port preserves the current guard rather than claiming #551's broader hardening. The open local-mode implementation must not be overwritten as part of a parity port: if #573 lands first, this body's scope and evidence must be refreshed before implementation to preserve it. Undo: revert this PR to restore the original task body and caller behavior.

Operator authority

standing rule names the operator: #753 must protect replacement enforcement before activation, with its required exact-head owner review. #551 retains the separate owner approval for any local-history reduction; this port grants no such reduction.

Activity

  1. added
    change:featPR adds or changes a feature (Conventional Commit type feat)
    on Oct 26, 2026
  2. tbhb-agent commented on Oct 26, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — devtools:gitleaks Go task-orchestration port is well-scoped, testable, and aligned with origin/main
    Model: gemini-4-argon-high

    Checked

    • Baseline and Problem (origin/main at a0347a715fab580ece45edfcb3d13509620459a8):
      • .devtools/tasks/security.toml:24 defines ["devtools:gitleaks"] with an inline Bash body (.devtools/tasks/security.toml:26-49) that invokes gitleaks git --verbose --redact . (.devtools/tasks/security.toml:33), captures its exit status (.devtools/tasks/security.toml:32-33,48), and fails when the output lacks [1-9][0-9]* commits scanned unless HEAD resolves and git log -p --format= HEAD contains no ^[+-] patch lines (.devtools/tasks/security.toml:36-47).
      • .config/mise/conf.d/10-devtools-tools.toml:48 pins gitleaks = "8.30.1".
      • docs/script-inventory.md:270 and parent issue Port check-leaks and the pre-push installer to agentrepo #329 list .devtools/tasks/security.toml (devtools:gitleaks, 12 source lines) as the gitleaks task-orchestration slice under epic Replace complex shell with tested Go, starting with gates and merging #588.
    • Acceptance criteria (AC1–AC10) and requirements-only rule:
      • AC1–AC6 specify observable behavioral parity for the scanner invocation and all-ref history coverage (AC1), redacted output and nonzero exit-status preservation (AC2), the zero-scanned-commits guard and legitimate contentless-history exception (AC3), failure-closed handling on missing repositories, unborn HEAD, failed Git validation, or partial output before nonzero exit (AC4), default private-key detection and exact .gitleaks.toml path-exception behavior at .devtools/config/gitleaks.toml:1-9 and docs/gitleaks.md:1-9 (AC5), and caller/workflow preservation (AC6).
      • AC7, AC8, and AC10 cover non-functional requirements: side-effect-free capability probing and frozen-shell fallback without double-scanning on failure (AC7), Linux/macOS/Windows execution and N-2 compatibility under docs/compatibility.md:1-35 (AC8), and preservation of repository refs, index, and working tree (AC10).
      • AC9 covers help and adoption documentation (docs/gitleaks.md:1-9, docs/consumer-workflows.md:42) without claiming #551's local mode exists.
      • Neither the ACs nor Approach prescribe internal Go package layout, file paths, or library choices.
    • Scope, callers, and non-goals (#501 and #551/#573 separation):
      • Callers cited in Scope match origin/main: mise.toml:748 ([tasks.security]), mise.toml:813 ([tasks.check-all]), packages/agents/source/skills/commit/shared/scripts/commit.sh:165, packages/agents/source/skills/pr/shared/scripts/create-pr.sh:191, and .github/workflows/scan-gitleaks.yml:37 (with diagnosis routing at packages/agents/source/skills/fix-pr/shared/scripts/diagnose.sh:115).
      • Existing Bats regressions cited in Scope match tests/tasks/gitleaks.bats:16, tests/tasks/gitleaks.bats:32, tests/tasks/gitleaks.bats:55, tests/tasks/gitleaks.bats:62, tests/tasks/gitleaks.bats:75, tests/tasks/gitleaks.bats:89, tests/tasks/gitleaks.bats:98, and tests/tasks/gitleaks.bats:128 (along with the companion fixture-policy tests at tests/tasks/gitleaks.bats:143 and tests/tasks/gitleaks.bats:156, covered by AC5 and Verify plan).
      • fix(leaks): scope local gates to branch history and the staged tree #551 and fix(gitleaks): isolate local publication scans #573 stay non-goals: Scope and non-goals, AC1, AC6, AC9, Verify plan, Risks, and Operator authority explicitly exclude #551's --mode local/--mode ci flags, origin-base resolution, branch-history narrowing, full-index snapshot scanning, caller-mode changes, and local-history coverage reduction (implemented in open PR #573).
      • leak gate runs without private strings in agent worktrees, because the mise ceiling hides the main clone's mise.local.toml #501 stays a non-goal: Scope and non-goals and AC6 explicitly keep .devtools/scripts/check-leaks.sh private-strings loading, source notices, and provisioning in #501, preserving the separate devtools:lint-leaks calls at packages/agents/source/skills/commit/shared/scripts/commit.sh:164 and packages/agents/source/skills/pr/shared/scripts/create-pr.sh:190.
    • Dependencies and prerequisites:
    • Size, Verify plan, Risks, and Operator authority:
      • Size S fits one pull request (replacing a 23-line task body in .devtools/tasks/security.toml:26-49 and porting tests/tasks/gitleaks.bats:1-181 to Go tests while keeping the frozen fallback wrapper).
      • Verify plan maps every AC (AC1–AC10) to concrete, falsifiable Go and Bats checks, plus mise run check, coverage floors, #753 protection verification, and conditional skill generation checks.
      • Risks and Undo cover subprocess status loss, false zero-count exceptions, coordination with open PR #573, and clean revert recovery.
      • Operator authority accurately cites #753's exact-head owner review requirement for protected replacement enforcement paths (.agents/agent-skills.conf:4,8,9) and distinguishes #551's separate owner approval for local-history reduction.

    Gaps

    None

    Split

    None — Size S fits a single pull request.

    Definition of done

    The definition of done for #814 (Feature). Tick each item and give its evidence. (A ticked box indicates planning coverage in the issue body, not completed implementation work.)

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
      • Planning coverage: ## Verify plan defines concrete checks for AC1–AC10 and explicitly requires D1 verification of each AC at the merged head with commands and results cited in the PR's Verification section.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up.
      • Planning coverage: ## Verify plan (D2) requires independent review approval of the exact merged head and a recorded disposition for every finding.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
      • Planning coverage: ## Verify plan requires mise run test-hooks tests/tasks/gitleaks.bats, caller/workflow regressions, mise run check, coverage floors, and D3 (required checks pass with no gate loosened), while ## Operator authority records the exact-head owner review rules under #753 and .agents/agent-skills.conf:4,8,9.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
      • Planning coverage: ## Verify plan (D4) requires repository leak scans and per-post leak review before posting any comments or descriptions.
    • D5 The author signed each commit, the squash included.
      • Planning coverage: ## Verify plan (D5) explicitly requires every commit to be signed.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
      • Planning coverage: AC9 and its ## Verify plan item cover help and adoption documentation (docs/gitleaks.md:1-9, docs/consumer-workflows.md:42), and D6 in ## Verify plan requires matching docs or an explicit No doc impact rationale.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
      • Planning coverage: ## Verify plan (D7) requires an Assisted-by trailer on every commit and the provenance:agent-assisted label on the pull request.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
      • Planning coverage: ## Size scopes the work to a single S pull request, and ## Verify plan (D8) requires staying within pull request size limits.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue.
      • Planning coverage: ## Scope and non-goals identifies compatibility fallback removal after the N-2 window as a follow-up under D9, and ## Verify plan (D9) requires filing follow-up and deferred-finding issues with a Problem section and numbered ACs, listing them in Related, and linking them from any code that defers work.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
      • Planning coverage: ## Verify plan (D10) requires closing the issue via merge reference, setting the project board state to Done, and recording the merge commit in a final Status: comment.

    Round: 1 of 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    change:featPR adds or changes a feature (Conventional Commit type feat)refinement:readyPassed the Definition of Ready; may be picked up

    Fields

    Priority

    None yet

    Work type

    None yet

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions