Repository navigation
Sync with upstream TruffleHog v3.98.0 - #19
Merged
Merged
Conversation
…ecurity#4928) Non-fatal scanning errors aren't really "errors" - they're consequences of the vicissitudes of scanning a ton of data. They're actionable if you're looking for them but if you're not they're extremely noisy. This commit downgrades them to only show up when you look for them.
* added cloudinary detector * resolved linter issue * improved regex * added dector to defaults.go * resolved comments * Regen protos, Updated desc and ignore secret part in test
…ql v4, auth v3, goreleaser v7, mikepenz v6, jaxxstorm v3.0.0, docker login/qemu v4) (trufflesecurity#4933) Made-with: Cursor
… verification request (trufflesecurity#4947) * added content-type=application/json as default header * improv: set content type header when it is not present in the config * handled canonicalization
…rity#4948) * Update linter to disallow assigning SecretParts later This means all detectors.Result objects must be created with the SecretParts field set. * Update documentation * Migrate existing detectors to always initialize SecretParts
* Add Pinecone API key detector
Adds a new detector for Pinecone vector database API keys (pcsk_* format).
Detection:
- Regex matches the pcsk_{key_id}_{secret} structure with tight bounds
(4+ char key ID, 40+ char secret, word boundaries) to minimize false positives.
- Extracts the embedded key_id from the token unconditionally, which maps to
the key entry in the Pinecone console and aids revocation.
Verification:
- Uses GET /indexes (non-state-changing, read-only) with Api-Key header auth.
- Validates response body structure (requires "indexes" JSON key), not just
status codes, to be resilient against API changes.
- 200: Verified. Extracts project_id from index host, total_indexes, and
metadata for up to 5 indexes (name, host, cloud, region).
- 401: Invalid key (handles both plain text and JSON error bodies).
- 403: Valid key with restricted permissions (DataPlane-only roles).
Marked as verified with permission=restricted metadata.
Uses common.SaneHttpClient() with standard timeouts (5s response, 2s dial,
3s TLS) and io.LimitReader (1MB cap) on response body. No SDK dependencies.
Registered as DetectorType Pinecone = 1048.
Made-with: Cursor
* Align Pinecone detector with SecretParts
Migrate Pinecone verification metadata to SecretParts, address the redundant 200-response JSON parsing Bugbot flagged, and add focused regression coverage for malformed verification responses.
* Refactor Pinecone detector and add tests
---------
Co-authored-by: Dylan Ayrey <dylan@Dylans-MacBook-Pro.local>
Co-authored-by: Dustin Decker <dustin@trufflesec.com>
Co-authored-by: Shahzad Haider <shahzadhaider.se@gmail.com>
Co-authored-by: Shahzad Haider <76992801+shahzadhaider1@users.noreply.github.com>
…trufflesecurity#4892) * adding customizable successRanges and rotatedRanges to customDetector *setting definitive = true in the legacy 200 path so that an earlier ranged verifier's rangesInEffect = true can't trigger a spurious SetVerificationError after the legacy verifier already confirmed the secret as live. * adressed review comments
….go (trufflesecurity#4915) * add test to ensure new detectors are registered in defaults.go * fix lint
…flesecurity#4907) * unify common logic in atlassian data center detectors * initialize url pat once * engine_test fix * remove bitbucketdatacenter from defaults test exclude list
* added gitlab oauth detector * addressed comments; embed detectors.DefaultMultiPartCredentialProvider and use available ctx in tests * made comments more meaningful and break loop when secret is verified against one client id * switch from detectorspb to detector_typepb * populate secretparts * add secretparts to results in gitlaboauth2 detector --------- Co-authored-by: Charlie Gunyon <camgunz@users.noreply.github.com>
…urity#4761) * extract subject id in boxoauth detector * update the detector design for subject id handling * verify subject ID via CCG auth before populating AnalysisInfo Instead of blindly pairing every found subject ID with verified credentials, use the Box CCG token endpoint to verify which subject ID actually works. Emit one result per credential pair with only the verified subject ID in AnalysisInfo. Add gock-based tests for all AnalysisInfo scenarios. * replace analysisinfo with secretparts * [INS-379] Populate SecretParts unconditionally in BoxOauth detector Always set client_id and client_secret in SecretParts regardless of verification status. subject_id is added only when a valid triplet is confirmed via verifySubjectID. Previously SecretParts was only populated for a fully verified triple, leaving unverified results with no credential data stored. * address bugbot comment; Non-deterministic map iteration for subject ID selection --------- Co-authored-by: Charlie Gunyon <camgunz@users.noreply.github.com>
* spectralops-detector * increased context deadline * registered detector in engine * Correct comment on SpectralOps API key format * updated keyword * fixed test * Merged main * Regen protos, Updated desc and ignore secret part in test * updated protos
* Added AWS Appsync Detector * added detector to defaults.go * imprved regex and regen protos * added secretparts * ignored secret parts in test * resolved bugbot comment
* add detector corpora test workflow and script
* only run once per PR, make comment descriptive, add handling for manual runs to get PR issue number
* comment out types to see result on all commits
* uncomment types
* remove table from comment
* comment out types
* Phase 0: add explicit pipefail and capture trufflehog stderr
* Phase 1: differential diffing PR vs main
* DEMO: loosen Stripe regex (will revert)
* DEMO: loosen JDBC regex (will revert)
* Phase 1 fix: add --allow-verification-overlap, fix no-diff detection
The bench uses --no-verification, so the engine's overlap-path dedup
(which exists to protect verifiers from duplicate calls) adds noise
without value here — it causes shifts in unrelated detectors when only
one detector's regex changes. Pair --allow-verification-overlap with
--no-verification so each detector's regex behavior is measured
independently.
Also fix the false 'no diff vs main' claim that triggered when
NEW/REMOVED were zero but total counts differed.
* revert jdbc detector change
* Phase 2: detector scoping, new-detector handling, blast radius, status emoji
* DEMO: loosen JDBC + add fictional acmevault detector
* Phase 2 fix: harden corpus byte counting against early trufflehog exit
awk's END block doesn't run when trufflehog exits before draining stdin
(SIGPIPE kills awk first), leaving the bytes file empty and breaking the
step with a `$((TOTAL_BYTES + ))` syntax error. Read the file with a
default of 0 and validate it's an integer before arithmetic. Also fold
unzstd/jq stderr into STDERR_FILE so benign Broken pipe notices stay
out of CI logs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Phase 3a (1/3): add hack/extract-keywords for detector keyword introspection
Static AST parse of a detector package to extract the strings returned by
its Keywords() method. Used by the upcoming keyword-corpus builder to fan
out per-detector GitHub Code Search queries during the corpora bench.
AST-first because each detector lives in its own package; importing them
dynamically would require codegen or `plugin`. Falls back to a regex over
the function body, then a directory-wide grep, when AST resolution can't
statically resolve the return value (helper calls, build-tagged variants).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Phase 3a (2/3): add Layer 1 keyword corpus builder + workflow integration
build_keyword_corpus.py queries GitHub Code Search for each changed
detector's pre-filter keywords and emits a zstd-compressed JSONL whose
shape matches the existing S3 corpus exactly: each line is
`{"provenance": {...}, "content": "<raw file content>"}`. The corpora
script's existing `unzstd | jq -r .content` pipe handles it unchanged —
provenance is descriptive only and never reaches trufflehog.
Rate-limit policy is header-driven: the search bucket's
X-RateLimit-Remaining and X-RateLimit-Reset headers gate every call,
with a 2.1s floor between requests as belt-and-suspenders. 403/429s
honor Retry-After or fall back to the reset window. Cap is 100 unique
results per detector, deduped on (repo, path, sha), with a per-keyword
sub-cap so one popular keyword can't starve the others.
A sidecar JSON reports per-detector fetch counts and a thin_l1 list of
detectors whose total returned results were zero (or whose keyword
extraction failed). The diff script reads it via a new
--keyword-corpus-meta arg and renders a single contiguous blockquote
callout above the per-detector details — a sidecar instead of an
in-corpus signal because stdin metadata is dropped from trufflehog's
findings output.
Workflow change: a new "Build keyword corpus (Layer 1)" step fires after
detector detection and overwrites DATASETS via $GITHUB_ENV to append the
keyword corpus path. The corpora script picks it up unchanged through
its existing local-file branch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Phase 4 complete - Heatmap visualization
* Phase 4 rework (1/2): emit heatmap-grid.json sidecar from render_heatmap.py
Add a JSON sidecar that captures the same Δ matrix the PNG renders. The
diff script consumes this to render an emoji-bucketed Markdown table —
GitHub's PR-comment Markdown sanitizer strips data: URLs and serves
artifact zips behind auth, so neither inline base64 nor an artifact
<img src> embed actually displays. The PNG stays for artifact archival
and click-through.
Sidecar shape: {detectors, decoders, deltas, _layout}, with _layout
documenting the deltas[i][j] orientation inline so future readers don't
have to reverse-engineer it. Emitted whenever the grid is non-empty,
even if matplotlib import fails — the comment never depends on the PNG.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Phase 4 rework (2/2): replace data-URL embed with emoji-bucketed Markdown table
GitHub's PR-comment Markdown sanitizer strips data: URLs from user
content, so the inline base64 PNG embed shipped in the prior commit
rendered as a broken link in the comment DOM (no <img> tag emitted).
Artifact-zip URLs require auth to download, so a fallback  link
is also a non-starter — it would render as a broken image.
Switch to a per-(detector, decoder) Δ table built from the grid JSON
sidecar render_heatmap.py now emits. Cells use emoji buckets aligned
with the existing status-emoji thresholds so the visual weight matches
the summary table:
🟥 Δ ≥ +6 (matches NEW > 5 → 🔴)
🟧 +1..+5
⬜ 0
🟦 ≤ −1
Renders identically on web, mobile, email notifications, and CI log
replay — every surface a PR comment lands on. The colored matplotlib
PNG stays as a workflow artifact; when --heatmap-artifact-url is
supplied, the table is followed by a click-through link for reviewers
who want the rich version.
Workflow YAML mirrors the rename (--heatmap-png → --heatmap-grid) and
drops the base64-blob log filter — the report is back to plain
human-readable text.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Phase 5 complete - Polish
* cleanup, enable verification
* fix bug
* optimizations
* cache keywords corpus
* rewrite comment message
* cache github api corpus per keyword
* cleanup
* remove github corpus
* revert changes for testing
* move Configure AWS credentials step to run only when detector changes are detected
* revert unnecessary changes
* cleanup + bugbot fixes
* run test with bigger (30gb) dataset, loosen jdbc regex
* optimizations
* bugbot fixes
* revert jdbc changes and bugbot fix
* run only on regex and/or keywords change
* bugbot fixes
* bugbot fix
* incorporate brad's comments, loosen jdbc regex to run a test to ensure everything works as expected
* revert test changes
* fix misleading bench skipped message
* run only once when PR opens
* pipe stderr directly to CI log instead of writing to file, loosen jdbc regex to trigger workflow
* testing: run on all commits
* add archive timeout
* remove bigger dataset
* revert testing changes
---------
Co-authored-by: Shahzad Haider <shahzadhaider.se@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…issues (trufflesecurity#4924) * enable errcheck and staticcheck for golangci-lint v2 and resolve all issues * skip lint on intentional reference of deprecated DetectorType values
…rufflesecurity#4849) Populate ExtraData with parsed fields for all database connection string detectors (MongoDB, PostgreSQL, Redis, JDBC). This surfaces useful metadata about detected credentials. The parsing logic already existed in each detector — this change exposes the extracted values in the result's ExtraData map alongside any pre-existing fields (rotation_guide, sslmode, etc.).
* remove over speculation from corpora ci workflow, change lob detector for testing (will revert) * revert lob changes
…y#4910) * fix line numbers for duplicate secrets within a chunk * fix-test: use cmpopts.IgnoreUnexported instead of cmpopts.IgnoreFields * refactor: unify FragmentLineOffset offset/ignore-tag logic * refactor: extract effectiveSecret helper for chunk-offset callers
trufflesecurity#4961) * Add feature flags for new detectors * Slight rename, ensure the flags are false by default * Missed a name change * Turn on new detectors for OSS user * Instead of only adding flagged detectors when their flag is enabled, remove them when their flag is disabled * Thank you cursorbot * Excempt the flagged detectors from a test
Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Dustin Decker <humanatcomputer@gmail.com>
Convert all GitHub Actions uses: refs from mutable tags to immutable SHA pins with version comments. This prevents tag-hijack supply chain attacks while preserving version tracking for Renovate. Part of the org-wide Dependency Strategy Unification (Step 2). Co-authored-by: Cursor <cursoragent@cursor.com>
…flesecurity#4983) * Update CODEOWNERS: replace 5 slugs with scanning + integrations * Move gitparse and giturl to integrations Remove explicit scanning rules -- both packages fall to the integrations default. They are git utility libraries consumed by multiple source integrations. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
* Added scan version feature flag to sharepoint proto * added more fields to sharepoint proto * renamed fields
…esecurity#4981) * support aspx decoding in html decoder * check isNamespaced before prefix comparison
… obfuscated APKs or APKs with resource types the 3rd party library isn't parsing (trufflesecurity#4991) * fix(apk): adds some debugging info (APK package name) which is helpful when processing large repos of APKs, and fixes processing of obfuscated APKs that might cause lots of errors with our APK processing library * fix(apk): put the ci.Next() iterator over classes into an IIFE * fix(apk): some cursor issues in the PR * fix(apk): continue if it fails to get a string value rather tahn error out completely skipping the rest of the resources * docs(apk): add comment noting why it is a continue now * fix(apk): had to add a thin interface wrapping apkparker logic and GetResourceEntry to mock out and test that it continues through bad string entries * refactor(apk): renamed resourceEntryProvider to GetEntryer to be more go-ish
…gs (trufflesecurity#5011) * enhance the verification in the enigma detector * feat(enigma): gate detector behind feature flag
trufflesecurity#5022) * fix: scan files with lines exceeding bufio's default 64 KB token limit * added related test
* detectors/azureapimanagementsubscriptionkey: report skipped candidates * detectors/azuredirectmanagementkey: report skipped candidates * detectors/artifactory: report candidates skipped by the host cache * detectors/algoliaadminkey: report candidates skipped by the host cache * detectors/azure_cosmosdb: report candidates skipped by the host cache * detectors/azurecontainerregistry: report candidates skipped by host cache * detectors/repositorykey: report candidates skipped by the host cache
* return error body for fetchBearerToken * took out limitReader and return full response body
…ecurity#5281) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…flesecurity#5280) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rity#5367) The Square detector treated every 403 as a live key. A 403 can also come from a TLS-intercepting proxy or security appliance, or from Square's own edge (rate limiting, bot protection). Those cases produced false "verified" results. The response body was also discarded, so there was nothing to diagnose them with. - Move verification into verifyMatch with an injectable HTTP client - Mark a 403 as verified only when Square's error body has code INSUFFICIENT_SCOPES - Report other 403s and unexpected statuses as verification errors that include the response body (read up to 4 KiB, cut to 512 bytes in the error, whitespace collapsed) - Add unit tests for 200, 401, 403 (scopes, HTML, other code, empty), 429, and body truncation
trufflesecurity#5293) Adds a CLI flag that disables the default suppression of results whose line contains a `trufflehog:ignore` comment. Detection is unchanged; only the post-detection drop in processResult is bypassed.
* Report progress inside a bucket while scanning an S3 unit * Leave objects the scan never downloads out of the S3 progress ratio and publish when the count finishes * Keep filtered objects in the S3 progress total, since the scan counts them as done * Treat a cancelled S3 count pass as incomplete so its partial totals are not divided by * Add a per-unit progress API so a source can report how far it has got inside a unit * Track S3 progress per unit behind a feature toggle and leave skipped objects out of both sides
…ufflesecurity#5353) * fix(output): escape special characters in --github-actions output Git allows characters in file paths that GitHub's workflow-command syntax does not — most notably newlines. When such a path is read back from a diff and printed as part of a ::warning:: command in --github-actions output mode, the result can be malformed and split across multiple lines that GitHub Actions doesn't expect. Escape file paths and messages before printing, per GitHub's documented escaping rules for workflow commands (percent-encode '%', '\r', '\n', and additionally ':'/',' for the file= property value). Extract the command-building into formatWarningCommand() so this is covered by unit tests, following the same data/IO separation SarifPrinter already uses. Reported by kenanyararbas <kenanyararbas@gmail.com>. * fix(output): drop doc comments on new helpers to match file convention Print() and the other helpers in this file (structToMap, extractFileAndLine) carry no doc comments, so match that rather than introducing one-off commented functions.
…ecurity#5334) Co-authored-by: Gracy769 <169199616+Gracy769@users.noreply.github.com> Co-authored-by: Shahzad Haider <76992801+shahzadhaider1@users.noreply.github.com>
…flesecurity#5358) * added entropy filter and improved verification for gitlab v2/v3 * added more cases for gitlab verification
…both sides (trufflesecurity#5357) * Corpora bench reports clean when a detector produces zero matches on both sides * Corpora bench reports clean when a detector produces zero matches on both sides * test okta corpus * remove changes that were used to test github actions
…ains (trufflesecurity#5354) domainPat required the literal substring auth0.com, so it never matched Auth0 Private Cloud tenants' default managed domain (*.auth0app.com). Since FromData only emits a result when a client_id, client_secret, and a domain match are all present together, a leaked credential pair next to its real Private Cloud domain was silently dropped -- no result, no verification attempt. *.auth0app.com is Auth0's own documented default for Private Cloud tenants, not a one-off customer naming choice: https://auth0.com/docs/migrate-private-cloud-custom-domains Add an optional, non-capturing app group so domainPat matches both *.auth0.com and *.auth0app.com. Fully custom domains (e.g. login.example.com) have no learnable Auth0-related substring and remain out of scope. A pre-existing, unrelated regex quirk was discovered while adding regression tests: a bare domain with no leading character (e.g. "auth0.com" or "auth0app.com" with no subdomain/prefix at all) never matches, since the leading character class must be consumed before the literal "auth0" substring. Left as a TODO on domainPat rather than fixed here, since it's out of scope for this change. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* List only the keys under the S3 include prefixes rather than the whole bucket * Count only the keys under the S3 include prefixes for unit progress * Validate S3 access with the listing a scan starts with, so prefix scoped policies pass
…rufflesecurity#5380) and maintain our own cache and expiry. Using ghinstallation.Transport turned out to be a mistake because rate limit errors weren't returned as the github client error types the caller looks for, but also the github client can circuit-break rate-limited requests before they're made. That's all in the client rather than a transport/roundtripper, so we're going back to request through that. INT-1151
…curity#5383) * Fix NetSuite detector memory blowup by keeping one result per key pair * Stop NetSuite verification after the deadline, look up missing hosts once, and drain responses * Update NetSuite integration test to expect one result per key pair
…5375) Every link here resolves to a 404 on GitHub today. The three file links were verified by reading the href GitHub renders and requesting it; the two anchors were checked against the headings they name. README.md said the AWS credential detector lives in pkg/detectors/aws/aws.go. That file is gone: the detector was split into access_keys and session_keys, and the GetCallerIdentity call the sentence describes is in access_keys/accesskey.go. Adding_Detectors_Internal.md linked pkg/common/http.go and a heroku test without a leading slash, so both resolved under hack/docs/ rather than the repository root. Adding_Detectors_external.md already writes the same SaneHttpClient link with the slash, so this just brings the two in line. The heroku link also named heroku_test.go, which only exercises the pattern; the step is about reading a value out of GSM, and heroku_integration_test.go is the file that does that with common.GetSecret and MustGetField. The last two are a copy-paste seam between the sibling documents. Internal titles its section "Creating a new Secret Scanner" but its table of contents asked for #creating-a-new-secret-detector; external titles the same section "Detector" but its step 5 asked for #creating-a-new-secret-scanner. Each document now points at its own heading.
…er clients (trufflesecurity#5288) * Add pkg/ssrf shared egress guard and use it for WithNoLocalIP Centralizes SSRF egress protection so detectors and downstream consumers (the thog analyzer guard) share one classification and one enforcement mechanism. pkg/ssrf provides ErrEgressBlocked, IsNonPublicIP, CheckDialAddress, and GuardDialer. The blocked set is broader than the old isLocalIP: it adds CGNAT (incl. Alibaba metadata), class E, test-nets and other special-use ranges, all multicast, and IPv6 embeddings of internal v4 targets (NAT64, 6to4, IPv4-compatible) that To4() does not normalize. WithNoLocalIP now installs a dialer whose Control hook checks the resolved address immediately before connect. The previous implementation vetted a LookupIP result and then dialed the hostname again, which a DNS-rebinding resolver could race; Control runs on the address actually being dialed, and re-runs on every redirect hop. ErrNoLocalIP remains as a deprecated alias of ssrf.ErrEgressBlocked so errors.Is call sites keep working. * Close IPv6 classification gaps and guard the analyzer HTTP clients Classification fixes in pkg/ssrf, each pinned by a test: - Block fec0::/10 (deprecated site-local, still internal scope on legacy gear), SIIT IPv4-translated ::ffff:0:0:0/96, and Teredo 2001::/32, which embed v4 targets that To4() does not normalize. - Stop blanket-blocking the NAT64 well-known prefix 64:ff9b::/96. DNS64 resolvers synthesize it for every public IPv4-only endpoint, so blocking the whole prefix broke all guarded dials in IPv6-only networks. IsNonPublicIP now extracts the embedded v4 from the low 32 bits and classifies that instead, so 64:ff9b::a00:1 (10.0.0.1) stays blocked while 64:ff9b::808:808 (8.8.8.8) is reachable. The RFC8215 local-use prefix stays blanket-blocked since its embedding layout is operator-defined. Analyzer client coverage: analyzers take their endpoints from scanned content (a secret's domain, a connection string), but the clients built in pkg/analyzer/analyzers used bare http.DefaultTransport. They now build on a guarded transport behind an opt-in SetEgressRestriction toggle, default off so the CLI and self-hosted behavior is unchanged. This covers NewAnalyzeClient, NewAnalyzeClientUnrestricted, the rate-limiter fallback transport, and HttpStatusTest.RunTest's bare client.
Brings the fork up from v3.95.2 to v3.98.0 (234 upstream commits). Two conflicts, both resolved by keeping both sides: - main.go: our --header setup and upstream's new --drop-unverified-jwt-results flag sit next to each other. - pkg/feature/feature.go: upstream's new feature flags plus our CustomHeaders field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream now enforces errcheck and the go-re2 import rule. Both hits were in our own header test files. Also gofmt the header map alignment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the fork up to upstream v3.98.0 (235 commits, last sync was May).
main.goandpkg/feature/feature.go, kept both sides. The custom--headeroption is unchanged.--headerreaches a local listener on a custom detector verification call, and the appsec-workflows tests pass against this build (64, including the real scanner).Only the branch is pushed, no upstream tags, so upstream's
release.ymldoesn't run.Next: run "scanner release" on the merge commit and pin it in appsec-workflows as v2.0.2.