Skip to content

sec(gitleaks): add a gating full-history pass beside the working-tree pass - #756

Merged
hyperpolymath merged 2 commits into
mainfrom
sec/gitleaks-history-pass
Sep 9, 2026
Merged

sec(gitleaks): add a gating full-history pass beside the working-tree pass#756
hyperpolymath merged 2 commits into
mainfrom
sec/gitleaks-history-pass

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

What changed

The estate PR secret gate scanned only the checked-out working tree, so a
secret that was committed and later deleted was permanently invisible to
it. Owner ruling 2026-09-09: "Make the PR gate full-history now."

This adds a second gating pass rather than converting the existing one.

Why both passes, not one

The premise behind "just drop --no-git" is wrong, and it was measured, not
assumed. A fixture built with this exact pinned binary (gitleaks 8.18.4), the
same secret placed two ways:

placement working tree (--no-git) git history
committed, then deleted MISSED FOUND
untracked file FOUND MISSED

git mode scans commit diffs, so it cannot see a file that was never
committed. --no-git walks the checked-out tree, so it cannot see a blob that
is no longer in it. Dropping either pass removes coverage. Both gate: a
finding in either exits non-zero.

The three edits, all inside the existing gitleaks job

  1. fetch-depth: 0 on the checkout. Without it the history pass would
    walk a single commit and report a pass.
  2. The stale comment that literally instructed the reader to remove
    --no-git is replaced with the two-population explanation.
  3. A new step, Gitleaks secret scan — git history (gating), placed
    immediately after the existing pass, which is left byte-identical.

Controls, so the new gate can actually fail

  • Vacuity control. The step asserts git rev-parse --is-shallow-repository is false and refuses to report a pass otherwise.
    If fetch-depth: 0 ever regresses, this fails loudly instead of scanning
    one commit and going green. gitleaks' own "scanned N commits" counter
    under-reports and is not proof of clone depth, so it is not used here.
  • test -s "$CONFIG". A silently-failed baseline staging step would
    otherwise leave gitleaks running a reduced config over almost nothing and
    calling the result green.

Check-name identity is preserved

Callers have secret-scan / gitleaks as a required context. A renamed job
would make that context go absent, and an absence is never in a failure set.
Verified on the parsed YAML tree, not by eye:

assertion result
job keys identical gitleaks, rust-secrets, shell-secrets
job name: fields identical all three unset, unchanged
steps in gitleaks 9 → 10; exactly one added, zero removed
existing Gitleaks secret scan (gating) step parsed-identical before/after
only pre-existing step delta with: on the checkout
new step position immediately after the working-tree pass
--no-git argument lines 2 (working tree + AsciiDoc mirror), unchanged
rust-secrets / shell-secrets jobs entirely unchanged

Known gap, stated rather than implied

The AsciiDoc mirror pass copies files into a scratch directory and scans
it with --no-git, so fetch-depth: 0 cannot reach it. An AsciiDoc secret
that was committed and later deleted stays invisible. "The gate is
full-history"
holds for every file class but that one. This is written into
the workflow file itself, not just here.

Pre-existing defect surfaced, NOT fixed here

Stage estate gitleaks baseline copies the baseline unconditionally,
while Estate baseline — install copies it again gated on
steps.estate_baseline.outputs.referenced. The comment claiming it is
"staged ONLY when actually referenced" is contradicted by the unconditional
cp. Out of scope for this PR; recorded so it is not lost.

Propagation, honestly

standards calls its own reusable by path (uses: ./.github/workflows/secret-scanner-reusable.yml), so it auto-follows on
merge. The six stapeln-ecosystem repos are pinned at three SHAs and each needs
a re-pin:

  • stapeln, svalinnfad242d35291de1898242d6737ba02b74a59a2f2
  • rokur, vordr, selura3308ea8d0cc513727c8ad149b1132317a886c98
  • cerro-torre257869d3061d5a8ed1529bf34225d90a2416d51a

The remaining ~280 callers land it whenever the existing pin-bump campaign
reaches them. This is not an estate-wide flip on merge.

Self-merged under the standing --admin grant once required ∩ {failed, never
ran} is empty.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FmGveHYaPLDUZVKGR1rLaK

… pass

The PR secret gate scanned only the checked-out working tree, so a secret
that was committed and later deleted was permanently invisible to it. Owner
ruling 2026-09-09: "Make the PR gate full-history now."

This adds a SECOND gating pass rather than converting the existing one,
because the two passes see different populations. Measured on a fixture with
this exact pinned binary (8.18.4), the same secret in two placements:

    committed then deleted    working tree: MISSED    history: FOUND
    untracked file            working tree: FOUND     history: MISSED

git mode scans commit diffs and cannot see a file that was never committed;
--no-git walks the checked-out tree and cannot see a blob no longer in it.
Dropping either pass REMOVES coverage, so both gate.

Three changes, all inside the existing `gitleaks` job:

1. fetch-depth: 0 on the checkout. Without it the history pass would walk a
   single commit and report a pass.
2. The stale comment that told readers to remove --no-git is replaced with
   the two-population explanation.
3. A new step, "Gitleaks secret scan - git history (gating)", placed
   immediately after the existing pass, which is left byte-identical.

The new step carries its own vacuity control: it asserts
`git rev-parse --is-shallow-repository` is false and refuses to report a
pass otherwise, because gitleaks' own "scanned N commits" counter
under-reports and is not proof of clone depth. It also asserts
`test -s "$CONFIG"`, so a silently-failed baseline staging step cannot leave
gitleaks scanning almost nothing and calling it green.

CHECK-NAME IDENTITY PRESERVED. Callers have `secret-scan / gitleaks` as a
required context; a renamed job would make that context go absent, and an
absence is never in a failure set. Verified structurally on the parsed YAML
tree, not by eye:

  job keys identical            gitleaks, rust-secrets, shell-secrets
  job name: fields identical    all three nil (unchanged)
  steps                         9 -> 10, exactly one added, zero removed
  existing gating step          parsed-identical before and after
  only pre-existing delta       `with:` on the checkout
  new step position             immediately after the working-tree pass
  --no-git argument lines       2 (working tree + AsciiDoc mirror), unchanged
  rust-secrets / shell-secrets  entirely unchanged

KNOWN GAP, stated in the file rather than implied: the AsciiDoc mirror pass
copies files to a scratch directory and scans it with --no-git, so
fetch-depth: 0 cannot reach it. An AsciiDoc secret that was committed and
later deleted stays invisible. "The gate is full-history" holds for every
file class but that one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FmGveHYaPLDUZVKGR1rLaK
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c2386b22-4340-459d-a555-d1033884e120

📥 Commits

Reviewing files that changed from the base of the PR and between 8f2ee50 and 65c796a.

📒 Files selected for processing (1)
  • .github/workflows/secret-scanner-reusable.yml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Repo self-tests
⚠️ CI failures not shown inline (6)

GitHub Actions: Secret Scanner / 0_scan _ rust-secrets.txt: sec(gitleaks): add a gating full-history pass beside the working-tree pass

Conclusion: failure

View job details

##[group]Run TODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"
 �[36;1mTODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"�[0m
 �[36;1m�[0m
 �[36;1m# An unparseable cutoff would pick the warn branch forever, silently�[0m
 �[36;1m# disarming the widened scan. Refuse to run instead.�[0m
 �[36;1mrequire_date() {�[0m
 �[36;1m  case "$2" in�[0m
 �[36;1m    [0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]) : ;;�[0m
 �[36;1m    *) echo "::error::rust-secrets: $1='$2' is not YYYY-MM-DD."�[0m

GitHub Actions: Secret Scanner / scan _ rust-secrets: sec(gitleaks): add a gating full-history pass beside the working-tree pass

Conclusion: failure

View job details

##[group]Run TODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"
 �[36;1mTODAY="${RUST_TODAY:-$(date -u +%Y-%m-%d)}"�[0m
 �[36;1m�[0m
 �[36;1m# An unparseable cutoff would pick the warn branch forever, silently�[0m
 �[36;1m# disarming the widened scan. Refuse to run instead.�[0m
 �[36;1mrequire_date() {�[0m
 �[36;1m  case "$2" in�[0m
 �[36;1m    [0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]) : ;;�[0m
 �[36;1m    *) echo "::error::rust-secrets: $1='$2' is not YYYY-MM-DD."�[0m

GitHub Actions: Secret Scanner / 1_scan _ shell-secrets.txt: sec(gitleaks): add a gating full-history pass beside the working-tree pass

Conclusion: failure

View job details

##[group]Run # Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.
 �[36;1m# Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.�[0m
 �[36;1m# Restricted to *_TOKEN / *_KEY / *_SECRET / PASSWORD to keep false-positives low.�[0m
 �[36;1mPATTERNS=(�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*TOKEN[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*API_KEY[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*SECRET[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{16,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?***"'"'"'][^"'"'"']{6,}["'"'"']'�[0m
 �[36;1m)�[0m
 �[36;1m�[0m
 �[36;1m# Inline pragma patterns — suppress a hit when found on the same or�[0m
 �[36;1m# immediately preceding line.�[0m
 �[36;1mPRAGMA_RE='(scanner-allow:[[:space:]]*shell-secrets|hypatia:[[:space:]]*allow[[:space:]]+security_errors/secret_detected)'�[0m
 �[36;1m�[0m
 �[36;1m# Param-expansion RHS pattern — assignments whose value is a variable�[0m
 �[36;1m# reference rather than a literal are never real secrets.�[0m
 �[36;1m# Matches: ="$VAR"  ="${VAR}"  ="${VAR:-…}"  ="${VAR:?…}"  ='${VAR}'  =$VAR�[0m
 �[36;1mPARAM_EXPANSION_RE='=['"'"'"'"'"']?\$\{?[A-Za-z_][A-Za-z0-9_]*(:[?-][^}]*)?\}?['"'"'"'"'"']?[[:space:]]*(#.*)?$'�[0m
 �[36;1m�[0m
 �[36;1m# Load per-repo ignore globs from .shell-secrets-ignore if present.�[0m
 �[36;1mIGNORE_GLOBS=()�[0m
 �[36;1mif [[ -f .shell-secrets-ignore ]]; then�[0m
 �[36;1m  while IFS= read -r line || [[ -n "$line" ]]; do�[0m
 �[36;1m    # Skip blank lines and comments�[0m
 �[36;1m    [[ -z "$line" || "$line" == \#* ]] && continue�[0m
 �[36;1m    IGNORE_GLOBS+=("$line")�[0m
 �[36;1m  done < .shell-secrets-ignore�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1m# is_ignored <filepath> — returns 0 (true) if path matches any ignore glob.�[0m
 �[36;1mis_ignored() {�[0m
 �[36;1m  local path="$1"�[0m
 �[36;1m  for glob in "${IGNORE_GLOBS[@]}"; do�[0m
 �[36;1m    #...

GitHub Actions: Secret Scanner / scan _ shell-secrets: sec(gitleaks): add a gating full-history pass beside the working-tree pass

Conclusion: failure

View job details

##[group]Run # Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.
 �[36;1m# Patterns: an `export FOO=` or `FOO=` with a quoted literal of meaningful length.�[0m
 �[36;1m# Restricted to *_TOKEN / *_KEY / *_SECRET / PASSWORD to keep false-positives low.�[0m
 �[36;1mPATTERNS=(�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*TOKEN[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*API_KEY[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{20,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?[A-Z_]*SECRET[A-Z_]*=["'"'"'][A-Za-z0-9_./+=-]{16,}["'"'"']'�[0m
 �[36;1m  '(export[[:space:]]+)?***"'"'"'][^"'"'"']{6,}["'"'"']'�[0m
 �[36;1m)�[0m
 �[36;1m�[0m
 �[36;1m# Inline pragma patterns — suppress a hit when found on the same or�[0m
 �[36;1m# immediately preceding line.�[0m
 �[36;1mPRAGMA_RE='(scanner-allow:[[:space:]]*shell-secrets|hypatia:[[:space:]]*allow[[:space:]]+security_errors/secret_detected)'�[0m
 �[36;1m�[0m
 �[36;1m# Param-expansion RHS pattern — assignments whose value is a variable�[0m
 �[36;1m# reference rather than a literal are never real secrets.�[0m
 �[36;1m# Matches: ="$VAR"  ="${VAR}"  ="${VAR:-…}"  ="${VAR:?…}"  ='${VAR}'  =$VAR�[0m
 �[36;1mPARAM_EXPANSION_RE='=['"'"'"'"'"']?\$\{?[A-Za-z_][A-Za-z0-9_]*(:[?-][^}]*)?\}?['"'"'"'"'"']?[[:space:]]*(#.*)?$'�[0m
 �[36;1m�[0m
 �[36;1m# Load per-repo ignore globs from .shell-secrets-ignore if present.�[0m
 �[36;1mIGNORE_GLOBS=()�[0m
 �[36;1mif [[ -f .shell-secrets-ignore ]]; then�[0m
 �[36;1m  while IFS= read -r line || [[ -n "$line" ]]; do�[0m
 �[36;1m    # Skip blank lines and comments�[0m
 �[36;1m    [[ -z "$line" || "$line" == \#* ]] && continue�[0m
 �[36;1m    IGNORE_GLOBS+=("$line")�[0m
 �[36;1m  done < .shell-secrets-ignore�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1m# is_ignored <filepath> — returns 0 (true) if path matches any ignore glob.�[0m
 �[36;1mis_ignored() {�[0m
 �[36;1m  local path="$1"�[0m
 �[36;1m  for glob in "${IGNORE_GLOBS[@]}"; do�[0m
 �[36;1m    #...

GitHub Actions: Secret Scanner / 2_scan _ gitleaks.txt: sec(gitleaks): add a gating full-history pass beside the working-tree pass

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1m�[0m
 �[36;1m# fetch-depth: 0 on the checkout is load-bearing HERE. If it ever�[0m
 �[36;1m# regresses to the default depth-1 clone, detect would walk a single�[0m
 �[36;1m# commit, find nothing and report a pass — a gate that cannot fail.�[0m
 �[36;1m# Assert completeness from git itself: gitleaks' own "scanned N�[0m
 �[36;1m# commits" line under-reports and is not proof of depth.�[0m
 �[36;1mif [ "$(git rev-parse --is-shallow-repository)" != "false" ]; then�[0m
 �[36;1m  echo "::error::checkout is shallow -- a history scan here would be vacuous; refusing to report a pass"�[0m

GitHub Actions: Secret Scanner / scan _ gitleaks: sec(gitleaks): add a gating full-history pass beside the working-tree pass

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1m�[0m
 �[36;1m# fetch-depth: 0 on the checkout is load-bearing HERE. If it ever�[0m
 �[36;1m# regresses to the default depth-1 clone, detect would walk a single�[0m
 �[36;1m# commit, find nothing and report a pass — a gate that cannot fail.�[0m
 �[36;1m# Assert completeness from git itself: gitleaks' own "scanned N�[0m
 �[36;1m# commits" line under-reports and is not proof of depth.�[0m
 �[36;1mif [ "$(git rev-parse --is-shallow-repository)" != "false" ]; then�[0m
 �[36;1m  echo "::error::checkout is shallow -- a history scan here would be vacuous; refusing to report a pass"�[0m
🧰 Additional context used
🪛 zizmor (1.29.0)
.github/workflows/secret-scanner-reusable.yml

[warning] 114-128: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🔇 Additional comments (1)
.github/workflows/secret-scanner-reusable.yml (1)

115-120: LGTM!

Also applies to: 219-227, 324-373


📝 Summary

Summary by CodeRabbit

  • New Features
    • Secret scanning now checks the complete Git history as well as the current working tree.
    • Pull requests and changes are blocked when secrets are detected in either scan.
    • Scanning validates that the repository history and detection configuration are available before proceeding.

Walkthrough

The reusable secret-scanner workflow now fetches full history and runs two gating scans: one for the working tree and one for Git history. The history scan validates repository depth, selects a non-empty configuration, and runs the pinned Gitleaks binary.

Changes

Secret scanning workflow

Layer / File(s) Summary
Full-history Gitleaks gate
.github/workflows/secret-scanner-reusable.yml
The checkout fetches full history. The workflow documents complementary working-tree and Git-history scans. A Git-mode Gitleaks step verifies repository depth, selects the repository or estate configuration, rejects empty configuration, and scans Git history.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 6c248

Secret scanning now gates on both working-tree content and complete Git history, with safeguards for incomplete checkouts and empty configuration. The change is ready to merge.

Sequence Diagram(s)

sequenceDiagram
  participant Workflow
  participant Checkout as actions/checkout
  participant Git
  participant Gitleaks
  Workflow->>Checkout: Fetch full repository history
  Workflow->>Git: Verify repository is not shallow
  Workflow->>Workflow: Select repository or estate Gitleaks configuration
  Workflow->>Gitleaks: Scan Git history with the selected configuration
  Gitleaks-->>Workflow: Return scan status
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a gating full-history Gitleaks scan alongside the existing working-tree scan.
Description check ✅ Passed The description is directly related to the changes. It explains the dual-scan design, validation controls, preserved job identity, known coverage gap, and scope boundaries.
✨ Finishing Touches
🛠️ Fix failing CI checks
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

I am a rabbit guarding the gate
Full history makes secrets wait
Working files and old commits
Gitleaks checks the hidden bits
Clean scans let the workflow run straight

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Coding Agent task started: View task and status

The task will inspect the CI failures, validate its fix, and open a stacked fix pull request automatically.

Note: Fixing CI failures is a beta feature and may encounter errors. Expect some limitations and changes as we gather feedback and continue to improve it.

@hyperpolymath

Copy link
Copy Markdown
Owner Author

⏸ HELD by owner ruling, 2026-09-09 — do not merge

The owner has ruled that this PR stays unmerged until they confirm the leaked
Cloudflare credential is dead
. This comment records the hold so no other actor
merges it in the meantime.

Why it is red, and why that is correct

The single failing check is scan / gitleaks, and it is the new gate in this
PR doing its job
. The full-history pass added here finds a real Cloudflare API
token in this repository's own git history, at avow-protocol/deploy-repos.sh:5,
introduced by the 2740-file bulk auto-commit 55ad5333 (2026-02-22). That is the
same credential PR #161 identified on 2026-05-21 and asked to be rotated out of
band. The working-tree removal landed; the rotation is unrecorded and the history
rewrite PR #161 also planned never happened.

The remaining two findings are avow-protocol/avow-lib/examples/python/stamp_example.py
lines 237 and 251 — demo arguments in a file deleted as banned Python.

Why the allowlist is not in this PR yet

.github/workflows/secret-scanner.yml:47 invokes the reusable by local relative
path
(uses: ./.github/workflows/secret-scanner-reusable.yml), not by pinned SHA.
There is no pin to stage behind, so this repository is a zero-latency consumer of
its own reusable. Merging the gate alone reddens main immediately, which would
turn every later merge here into an --admin bypass and destroy the gate's signal.

The gate and its allowlist are therefore one bundled decision, not two sequenced
ones
. The allowlist must ride this same PR.

What is already built and measured, held off-branch

A 27-line paths addition to the existing [allowlist] block in .gitleaks.toml,
naming only the two dead avow-protocol paths, with a comment recording why a
paths entry is used here against this file's own stated preference for value
entries: a real credential's value must never be written into a public config.

Measured on this branch at 65c796a, full-depth clone, with the pinned gitleaks
8.18.4 binary:

scan findings
PR head, current config 3
PR head, with the paths entry 0
canary planted at an allowlisted path MISSED — the documented residual
canary planted at an unlisted path FOUND — gate still armed

The two-sided canary is the load-bearing part. Three-to-zero alone cannot
distinguish a scoped exemption from a malformed config that exempts everything.

Documented residual: a paths entry also blinds the working-tree pass for that
path. That is acceptable here only because both files are deleted and would have to
be deliberately re-created.

The one action that unblocks this

On the Cloudflare account whose id appears alongside the token in that same script,
under Manage Account → Account API Tokens, check whether the token PR #161 names
is listed.

  • Listed — it is account-owned. Delete it there. No membership change, no zone risk.
  • Not listed — it is a user token. Revoke the member it belongs to; a user token
    inherits only that user's current permissions and becomes inert.

Zones belong to the account, not to any member, so neither route disconnects a site.
Managing members requires Super Administrator with a verified email address.

Honest scope of the fix

Revocation is the cure; a history rewrite is not. PR #161 says so in its own words:
"The repo is public; assume the secret is burned regardless of any history rewrite."
An external party reported this token, so assume it was harvested. A rewrite also
cannot reach forks, existing clones, or orphaned-object storage, and would break
every SHA pin of this repository across the estate.

Resume condition: the owner states the credential is dead. Then the allowlist is
pushed onto this branch and gate plus allowlist merge together.

@hyperpolymath
hyperpolymath marked this pull request as draft September 9, 2026 07:37
@hyperpolymath
hyperpolymath marked this pull request as ready for review September 9, 2026 09:40
@sonarqubecloud

sonarqubecloud Bot commented Sep 9, 2026

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath merged commit 619713b into main Sep 9, 2026
48 of 49 checks passed
@hyperpolymath
hyperpolymath deleted the sec/gitleaks-history-pass branch September 9, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant