Merge branch 'main' into s3-source-unit-impl
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# GitHub Workflows
|
||||
|
||||
This directory contains GitHub Actions workflows for the TruffleHog repository.
|
||||
|
||||
## PR Approval Check (`pr-approval-check.yml`)
|
||||
|
||||
This workflow enforces that at least one PR approver must be an **active** member of the `@trufflesecurity/product-eng` team or any of its child teams.
|
||||
|
||||
### How it works:
|
||||
|
||||
1. **Triggers**: The workflow runs on:
|
||||
- `pull_request_review` events when a review is submitted (`submitted` type)
|
||||
- `pull_request` events when a PR is opened, reopened, or synchronized (`opened`, `reopened`, `synchronize` types)
|
||||
|
||||
2. **Approval Check Process**: The workflow:
|
||||
- Fetches all reviews for the PR using the GitHub API
|
||||
- Filters for reviews with state `APPROVED`
|
||||
- Gets all child teams of `@trufflesecurity/product-eng` using `listChildInOrg` API
|
||||
- Checks if any approver is an **active** member (not pending) of either:
|
||||
- The parent `@trufflesecurity/product-eng` team, OR
|
||||
- Any of its child teams
|
||||
- Sets a commit status accordingly
|
||||
|
||||
3. **Status Check**: Creates a commit status named `product-eng-approval` with:
|
||||
- ✅ **Success**: When at least one approver is an active member of `@trufflesecurity/product-eng` or any child team
|
||||
- ❌ **Failure**: When there are no approvals or there are approvals but none from active `@trufflesecurity/product-eng` members
|
||||
|
||||
### Error Handling
|
||||
|
||||
If there are errors listing reviews or checking team membership, the workflow reports a failure status and also fails itself.
|
||||
|
||||
### Branch Protection
|
||||
|
||||
To make this check required:
|
||||
|
||||
1. Go to Settings → Branches
|
||||
2. Add or edit a branch protection rule for your main branch
|
||||
3. Enable "Require status checks to pass before merging"
|
||||
4. Add `pr-approval-check` to the required status checks
|
||||
|
||||
### Permissions
|
||||
|
||||
The workflow uses the default `GITHUB_TOKEN` which has sufficient permissions to:
|
||||
- Read PR reviews
|
||||
- List child teams and check team membership (for public teams)
|
||||
- Create commit statuses
|
||||
|
||||
**Note**: If the `product-eng` team or its child teams are private, you may need to use a personal access token with appropriate permissions. The Github API returns 404 for non-members and for lack of permissions.
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: Cosign install
|
||||
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
|
||||
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||
- name: Install UPX
|
||||
run: |
|
||||
sudo apt-get update
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o trufflehog .
|
||||
|
||||
FROM alpine:3.21
|
||||
FROM alpine:3.22
|
||||
RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio binutils cpio \
|
||||
&& rm -rf /var/cache/apk/* && update-ca-certificates
|
||||
COPY --from=builder /build/trufflehog /usr/bin/trufflehog
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.21
|
||||
FROM alpine:3.22
|
||||
|
||||
RUN apk add --no-cache bash git openssh-client ca-certificates \
|
||||
&& rm -rf /var/cache/apk/* && update-ca-certificates
|
||||
|
||||
@@ -160,6 +160,24 @@ In rare cases, you may need to bypass pre-commit hooks:
|
||||
git commit --no-verify -m "Your commit message"
|
||||
```
|
||||
|
||||
### Running in Audit Mode
|
||||
|
||||
You can run the TruffleHog pre-commit hook in an "audit" or "non-enforcement" mode to test the git hook with the following commands:
|
||||
|
||||
Local Binary Version:
|
||||
```bash
|
||||
trufflehog git file://. --since-commit HEAD --results=verified,unknown 2>/dev/null
|
||||
```
|
||||
|
||||
Docker Container Version:
|
||||
```bash
|
||||
docker run --rm -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --results=verified,unknown 2>/dev/null
|
||||
```
|
||||
|
||||
This change does two things: (1) removes the `--fail` flag, which means the pre-commit hook will *always* pass, (2) suppresses `stderr` output, so only verified secrets are printed to the terminal output.
|
||||
|
||||
**For users of the Pre-Commit Framework: add the `verbose: true` flag during audit mode; otherwise, the hook will pass, and you won't see any secrets.**
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook Not Running
|
||||
|
||||
@@ -38,7 +38,7 @@ We take the revenue from the enterprise product to fund more awesome open source
|
||||
|
||||
# What is TruffleHog 🐽
|
||||
|
||||
TruffleHog is the most powerful secrets **Discovery, Classification, Validation,** and **Analysis** tool. In this context secret refers to a credential a machine uses to authenticate itself to another machine. This includes API keys, database passwords, private encryption keys, and more...
|
||||
TruffleHog is the most powerful secrets **Discovery, Classification, Validation,** and **Analysis** tool. In this context, secret refers to a credential a machine uses to authenticate itself to another machine. This includes API keys, database passwords, private encryption keys, and more...
|
||||
|
||||
## Discovery 🔍
|
||||
|
||||
@@ -46,7 +46,7 @@ TruffleHog can look for secrets in many places including Git, chats, wikis, logs
|
||||
|
||||
## Classification 📁
|
||||
|
||||
TruffleHog classifies over 800 secret types, mapping them back to the specific identity they belong to. Is it an AWS secret? Stripe secret? Cloudflare secret? Postgres password? SSL Private key? Sometimes its hard to tell looking at it, so TruffleHog classifies everything it finds.
|
||||
TruffleHog classifies over 800 secret types, mapping them back to the specific identity they belong to. Is it an AWS secret? Stripe secret? Cloudflare secret? Postgres password? SSL Private key? Sometimes it's hard to tell looking at it, so TruffleHog classifies everything it finds.
|
||||
|
||||
## Validation ✅
|
||||
|
||||
@@ -58,7 +58,7 @@ For the 20 some of the most commonly leaked out credential types, instead of sen
|
||||
|
||||
# :loudspeaker: Join Our Community
|
||||
|
||||
Have questions? Feedback? Jump in slack or discord and hang out with us
|
||||
Have questions? Feedback? Jump into Slack or Discord and hang out with us.
|
||||
|
||||
Join our [Slack Community](https://join.slack.com/t/trufflehog-community/shared_invite/zt-pw2qbi43-Aa86hkiimstfdKH9UCpPzQ)
|
||||
|
||||
@@ -74,7 +74,7 @@ docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --or
|
||||
|
||||
# :floppy_disk: Installation
|
||||
|
||||
Several options available for you:
|
||||
Several options are available for you:
|
||||
|
||||
### MacOS users
|
||||
|
||||
@@ -149,7 +149,7 @@ You need the following tool to verify signature:
|
||||
|
||||
- [Cosign](https://docs.sigstore.dev/cosign/system_config/installation/)
|
||||
|
||||
Verification steps are as follow:
|
||||
Verification steps are as follows:
|
||||
|
||||
1. Download the artifact files you want, and the following files from the [releases](https://github.com/trufflesecurity/trufflehog/releases) page.
|
||||
|
||||
@@ -175,8 +175,8 @@ Verification steps are as follow:
|
||||
|
||||
Replace `{version}` with the downloaded files version
|
||||
|
||||
Alternatively, if you are using installation script, pass `-v` option to perform signature verification.
|
||||
This required Cosign binary to be installed prior to running installation script.
|
||||
Alternatively, if you are using the installation script, pass `-v` option to perform signature verification.
|
||||
This requires Cosign binary to be installed prior to running the installation script.
|
||||
|
||||
# :rocket: Quick Start
|
||||
|
||||
@@ -185,7 +185,7 @@ This required Cosign binary to be installed prior to running installation script
|
||||
Command:
|
||||
|
||||
```bash
|
||||
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified,unknown
|
||||
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified
|
||||
```
|
||||
|
||||
Expected output:
|
||||
@@ -209,15 +209,15 @@ Timestamp: 2022-06-16 10:17:40 -0700 PDT
|
||||
## 2: Scan a GitHub Org for only verified secrets
|
||||
|
||||
```bash
|
||||
trufflehog github --org=trufflesecurity --results=verified,unknown
|
||||
trufflehog github --org=trufflesecurity --results=verified
|
||||
```
|
||||
|
||||
## 3: Scan a GitHub Repo for only verified keys and get JSON output
|
||||
## 3: Scan a GitHub Repo for only verified secrets and get JSON output
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified,unknown --json
|
||||
trufflehog git https://github.com/trufflesecurity/test_keys --results=verified --json
|
||||
```
|
||||
|
||||
Expected output:
|
||||
@@ -233,7 +233,7 @@ Expected output:
|
||||
trufflehog github --repo=https://github.com/trufflesecurity/test_keys --issue-comments --pr-comments
|
||||
```
|
||||
|
||||
## 5: Scan an S3 bucket for verified keys
|
||||
## 5: Scan an S3 bucket for high-confidence results (verified + unknown)
|
||||
|
||||
```bash
|
||||
trufflehog s3 --bucket=<bucket name> --results=verified,unknown
|
||||
@@ -245,7 +245,7 @@ trufflehog s3 --bucket=<bucket name> --results=verified,unknown
|
||||
trufflehog s3 --role-arn=<iam role arn>
|
||||
```
|
||||
|
||||
## 7: Scan a Github Repo using SSH authentication in docker
|
||||
## 7: Scan a Github Repo using SSH authentication in Docker
|
||||
|
||||
```bash
|
||||
docker run --rm -v "$HOME/.ssh:/root/.ssh:ro" trufflesecurity/trufflehog:latest git ssh://github.com/trufflesecurity/test_keys
|
||||
@@ -269,18 +269,27 @@ Run trufflehog from the parent directory (outside the git repo).
|
||||
$ trufflehog git file://test_keys --results=verified,unknown
|
||||
```
|
||||
|
||||
## 10: Scan GCS buckets for verified secrets
|
||||
To guard against malicious git configs in local scanning (see CVE-2025-41390), TruffleHog clones local git repositories to a temporary directory prior to scanning. This follows [Git's security best practices](https://git-scm.com/docs/git#_security). If you want to specify a custom path to clone the repository to (instead of tmp), you can use the `--clone-path` flag. If you'd like to skip the local cloning process and scan the repository directly (only do this for trusted repos), you can use the `--trust-local-git-config` flag.
|
||||
|
||||
## 10: Scan GCS buckets for only verified secrets
|
||||
|
||||
```bash
|
||||
trufflehog gcs --project-id=<project-ID> --cloud-environment --results=verified,unknown
|
||||
trufflehog gcs --project-id=<project-ID> --cloud-environment --results=verified
|
||||
```
|
||||
|
||||
## 11: Scan a Docker image for verified secrets
|
||||
## 11: Scan a Docker image for only verified secrets
|
||||
|
||||
Use the `--image` flag multiple times to scan multiple images.
|
||||
|
||||
```bash
|
||||
trufflehog docker --image trufflesecurity/secrets --results=verified,unknown
|
||||
# to scan from a remote registry
|
||||
trufflehog docker --image trufflesecurity/secrets --results=verified
|
||||
|
||||
# to scan from the local docker daemon
|
||||
trufflehog docker --image docker://new_image:tag --results=verified
|
||||
|
||||
# to scan from an image saved as a tarball
|
||||
trufflehog docker --image file://path_to_image.tar --results=verified
|
||||
```
|
||||
|
||||
## 12: Scan in CI
|
||||
@@ -382,7 +391,7 @@ aws s3 cp s3://example/gzipped/data.gz - | gunzip -c | trufflehog stdin
|
||||
- Why is the scan taking a long time when I scan a GitHub org
|
||||
- Unauthenticated GitHub scans have rate limits. To improve your rate limits, include the `--token` flag with a personal access token
|
||||
- It says a private key was verified, what does that mean?
|
||||
- Check out our Driftwood blog post to learn how to do this, in short we've confirmed the key can be used live for SSH or SSL [Blog post](https://trufflesecurity.com/blog/driftwood-know-if-private-keys-are-sensitive/)
|
||||
- A verified result means TruffleHog confirmed the credential is valid by testing it against the service's API. For private keys, we've confirmed the key can be used live for SSH or SSL authentication. Check out our Driftwood blog post to learn more [Blog post](https://trufflesecurity.com/blog/driftwood-know-if-private-keys-are-sensitive/)
|
||||
- Is there an easy way to ignore specific secrets?
|
||||
- If the scanned source [supports line numbers](https://github.com/trufflesecurity/trufflehog/blob/d6375ba92172fd830abb4247cca15e3176448c5d/pkg/engine/engine.go#L358-L365), then you can add a `trufflehog:ignore` comment on the line containing the secret to ignore that secrets.
|
||||
|
||||
@@ -398,7 +407,13 @@ TruffleHog v3 is a complete rewrite in Go with many new powerful features.
|
||||
|
||||
## What is credential verification?
|
||||
|
||||
For every potential credential that is detected, we've painstakingly implemented programmatic verification against the API that we think it belongs to. Verification eliminates false positives. For example, the [AWS credential detector](pkg/detectors/aws/aws.go) performs a `GetCallerIdentity` API call against the AWS API to verify if an AWS credential is active.
|
||||
For every potential credential that is detected, we've painstakingly implemented programmatic verification against the API that we think it belongs to. Verification eliminates false positives and provides three result statuses:
|
||||
|
||||
- **verified**: Credential confirmed as valid and active by API testing
|
||||
- **unverified**: Credential detected but not confirmed valid (may be invalid, expired, or verification disabled)
|
||||
- **unknown**: Verification attempted but failed due to errors, such as a network or API failure
|
||||
|
||||
For example, the [AWS credential detector](pkg/detectors/aws/aws.go) performs a `GetCallerIdentity` API call against the AWS API to verify if an AWS credential is active.
|
||||
|
||||
# :memo: Usage
|
||||
|
||||
@@ -437,7 +452,7 @@ Flags:
|
||||
--github-actions Output in GitHub Actions format.
|
||||
--concurrency=20 Number of concurrent workers.
|
||||
--no-verification Don't verify the results.
|
||||
--results=RESULTS Specifies which type(s) of results to output: verified, unknown, unverified, filtered_unverified. Defaults to all types.
|
||||
--results=RESULTS Specifies which type(s) of results to output: verified (confirmed valid by API), unknown (verification failed due to error), unverified (detected but not verified), filtered_unverified (unverified but would have been filtered out). Defaults to all types.
|
||||
--allow-verification-overlap
|
||||
Allow verification of similar credentials across detectors
|
||||
--filter-unverified Only output first unverified result per chunk per detector if there are more than one results.
|
||||
@@ -568,7 +583,7 @@ In the example config above, we're scanning for live secrets in all PRs and Push
|
||||
|
||||
### Shallow Cloning
|
||||
|
||||
If you're incorporating TruffleHog into a standalone workflow and aren't running any other CI/CD tooling alongside TruffleHog, then we recommend using [Shallow Cloning](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt) to speed up your workflow. Here's an example for how to do it:
|
||||
If you're incorporating TruffleHog into a standalone workflow and aren't running any other CI/CD tooling alongside TruffleHog, then we recommend using [Shallow Cloning](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt) to speed up your workflow. Here's an example of how to do it:
|
||||
|
||||
```
|
||||
...
|
||||
@@ -670,9 +685,9 @@ webhook is used containing the regular expression matches.
|
||||
|
||||
TruffleHog will send a JSON POST request containing the regex matches to a
|
||||
configured webhook endpoint. If the endpoint responds with a `200 OK` response
|
||||
status code, the secret is considered verified.
|
||||
status code, the secret is considered verified. If verification fails due to network/API errors, the result is marked as unknown.
|
||||
|
||||
Custom Detectors support a few different filtering mechanisms: entropy, regex targeting the entire match, regex targeting the captured secret,
|
||||
Custom Detectors support a few different filtering mechanisms: entropy, regex targeting the entire match, regex targeting the captured secret,
|
||||
and excluded word lists checked against the secret (captured group if present, entire match if capture group is not present). Note that if
|
||||
your custom detector has multiple `regex` set (in this example `hogID`, and `hogToken`), then the filters get applied to each regex. [Here](examples/generic_with_filters.yml) is an example of a custom detector using these filters.
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ detectors:
|
||||
regex:
|
||||
secret: |-
|
||||
(?i)[\w.-]{0,50}?(?:access|auth|(?-i:[Aa]pi|API)|credential|creds|key|passw(?:or)?d|secret|token)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([\w.=-]{10,150}|[a-z0-9][a-z0-9+/]{11,}={0,3})(?:[\x60'"\s;]|\\[nr]|$)
|
||||
validations:
|
||||
secret: # name of the regex to apply these validations to
|
||||
contains_digit: true
|
||||
contains_special_char: true
|
||||
entropy: 3.5
|
||||
# exclude_regexes_capture:
|
||||
# - |-
|
||||
|
||||
@@ -1,49 +1,51 @@
|
||||
module github.com/trufflesecurity/trufflehog/v3
|
||||
|
||||
go 1.23.1
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.4
|
||||
toolchain go1.24.5
|
||||
|
||||
replace github.com/jpillora/overseer => github.com/trufflesecurity/overseer v1.2.8
|
||||
|
||||
// Coinbase archived this library and it has some vulnerable dependencies so we've forked.
|
||||
replace github.com/coinbase/waas-client-library-go => github.com/trufflesecurity/waas-client-library-go v1.0.9
|
||||
|
||||
// TODO: v1.134.0 is available but deprecates existing Auth methods, should be updated separately
|
||||
replace gitlab.com/gitlab-org/api/client-go => gitlab.com/gitlab-org/api/client-go v0.129.0
|
||||
|
||||
require (
|
||||
cloud.google.com/go/secretmanager v1.14.7
|
||||
cloud.google.com/go/storage v1.50.0
|
||||
cloud.google.com/go/secretmanager v1.15.0
|
||||
cloud.google.com/go/storage v1.56.1
|
||||
github.com/BobuSumisu/aho-corasick v1.0.3
|
||||
github.com/TheZeroSlave/zapsentry v1.23.0
|
||||
github.com/adrg/strutil v0.3.1
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/avast/apkparser v0.0.0-20250530053210-166ba1767cb9
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.16
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.69
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.79
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.2
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.6
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.21
|
||||
github.com/aws/smithy-go v1.22.3
|
||||
github.com/avast/apkparser v0.0.0-20250626104540-d53391f4d69d
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.7
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.11
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.5
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.88.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.38.2
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.3
|
||||
github.com/aws/smithy-go v1.23.0
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1
|
||||
github.com/bill-rich/go-syslog v0.0.0-20220413021637-49edb52a574c
|
||||
github.com/bitfinexcom/bitfinex-api-go v0.0.0-20210608095005-9e0b26f200fb
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0
|
||||
github.com/brianvoe/gofakeit/v7 v7.2.1
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0
|
||||
github.com/brianvoe/gofakeit/v7 v7.6.0
|
||||
github.com/charmbracelet/bubbles v0.18.0
|
||||
github.com/charmbracelet/bubbletea v1.3.4
|
||||
github.com/charmbracelet/glamour v0.7.0
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/coinbase/waas-client-library-go v1.0.8
|
||||
github.com/couchbase/gocb/v2 v2.10.0
|
||||
github.com/charmbracelet/bubbletea v1.3.6
|
||||
github.com/charmbracelet/glamour v0.10.0
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
|
||||
github.com/couchbase/gocb/v2 v2.11.0
|
||||
github.com/crewjam/rfc5424 v0.1.0
|
||||
github.com/csnewman/dextk v0.3.0
|
||||
github.com/docker/docker v28.3.3+incompatible
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/elastic/go-elasticsearch/v8 v8.17.1
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/gabriel-vasile/mimetype v1.4.9
|
||||
github.com/gabriel-vasile/mimetype v1.4.10
|
||||
github.com/getsentry/sentry-go v0.32.0
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
@@ -53,15 +55,15 @@ require (
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-containerregistry v0.20.3
|
||||
github.com/google/go-containerregistry v0.20.6
|
||||
github.com/google/go-github/v67 v67.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/googleapis/gax-go/v2 v2.14.2
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||
github.com/googleapis/gax-go/v2 v2.15.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.7
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8
|
||||
github.com/jlaffaye/ftp v0.2.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/jpillora/overseer v1.1.6
|
||||
@@ -84,11 +86,11 @@ require (
|
||||
github.com/repeale/fp-go v0.11.1
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/schollz/progressbar/v3 v3.17.1
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible
|
||||
github.com/sendgrid/sendgrid-go v3.16.1+incompatible
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/shuheiktgw/go-travis v0.3.1
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tailscale/depaware v0.0.0-20250112153213-b748de04d81b
|
||||
github.com/testcontainers/testcontainers-go v0.34.0
|
||||
github.com/testcontainers/testcontainers-go/modules/elasticsearch v0.34.0
|
||||
github.com/testcontainers/testcontainers-go/modules/mongodb v0.34.0
|
||||
@@ -97,20 +99,20 @@ require (
|
||||
github.com/testcontainers/testcontainers-go/modules/postgres v0.34.0
|
||||
github.com/trufflesecurity/disk-buffer-reader v0.2.1
|
||||
github.com/wasilibs/go-re2 v1.9.0
|
||||
github.com/xo/dburl v0.23.7
|
||||
gitlab.com/gitlab-org/api/client-go v0.127.0
|
||||
go.mongodb.org/mongo-driver v1.17.3
|
||||
github.com/xo/dburl v0.23.8
|
||||
gitlab.com/gitlab-org/api/client-go v0.129.0
|
||||
go.mongodb.org/mongo-driver v1.17.4
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/mock v0.5.1
|
||||
go.uber.org/mock v0.5.2
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.14.0
|
||||
golang.org/x/text v0.24.0
|
||||
golang.org/x/time v0.11.0
|
||||
google.golang.org/api v0.232.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/text v0.28.0
|
||||
golang.org/x/time v0.12.0
|
||||
google.golang.org/api v0.247.0
|
||||
google.golang.org/protobuf v1.36.9
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -120,43 +122,41 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.20.0 // indirect
|
||||
cloud.google.com/go v0.120.0 // indirect
|
||||
cloud.google.com/go/auth v0.16.1 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
cloud.google.com/go v0.121.6 // indirect
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.7 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.5 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.8.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.3 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
@@ -166,15 +166,18 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/ansi v0.9.3 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
github.com/couchbase/gocbcore/v10 v10.7.0 // indirect
|
||||
github.com/couchbase/gocbcore/v10 v10.8.0 // indirect
|
||||
github.com/couchbase/gocbcoreps v0.1.3 // indirect
|
||||
github.com/couchbase/goprotostellar v1.0.2 // indirect
|
||||
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 // indirect
|
||||
@@ -182,11 +185,10 @@ require (
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/docker/cli v27.5.0+incompatible // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/docker/cli v28.2.2+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v27.5.0+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
@@ -199,18 +201,17 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-github/v66 v66.0.0 // indirect
|
||||
github.com/google/go-github/v69 v69.0.0 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/go-github/v72 v72.0.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
@@ -238,9 +239,10 @@ require (
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.1.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
@@ -249,14 +251,12 @@ require (
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.1 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
@@ -268,6 +268,7 @@ require (
|
||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
@@ -279,7 +280,7 @@ require (
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/trufflesecurity/touchfile v0.1.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/vbatts/tar-split v0.11.6 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
@@ -289,32 +290,30 @@ require (
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yuin/goldmark v1.5.4 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.2 // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.5 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.einride.tech/aip v0.60.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/term v0.31.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/grpc v1.72.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/grpc v1.74.2 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
pault.ag/go/topsort v0.1.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
|
||||
cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI=
|
||||
cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
@@ -11,50 +9,42 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
|
||||
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
|
||||
cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU=
|
||||
cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
|
||||
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
|
||||
cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
|
||||
cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
|
||||
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
|
||||
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs=
|
||||
cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo=
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
|
||||
cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
|
||||
cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
|
||||
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/secretmanager v1.14.7 h1:VkscIRzj7GcmZyO4z9y1EH7Xf81PcoiAo7MtlD+0O80=
|
||||
cloud.google.com/go/secretmanager v1.14.7/go.mod h1:uRuB4F6NTFbg0vLQ6HsT7PSsfbY7FqHbtJP1J94qxGc=
|
||||
cloud.google.com/go/secretmanager v1.15.0 h1:RtkCMgTpaBMbzozcRUGfZe46jb9a3qh5EdEtVRUATF8=
|
||||
cloud.google.com/go/secretmanager v1.15.0/go.mod h1:1hQSAhKK7FldiYw//wbR/XPfPc08eQ81oBsnRUHEvUc=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
|
||||
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
|
||||
cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
|
||||
cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
|
||||
cloud.google.com/go/storage v1.56.1 h1:n6gy+yLnHn0hTwBFzNn8zJ1kqWfR91wzdM8hjRF4wP0=
|
||||
cloud.google.com/go/storage v1.56.1/go.mod h1:C9xuCZgFl3buo2HZU/1FncgvvOgTAs/rnh4gF4lMg0s=
|
||||
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
|
||||
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||
@@ -77,16 +67,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0 h1:nNMpRpnkWDAaqcpxMJvxa/Ud98gjbYwayJY4/9bdjiU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
@@ -98,14 +86,14 @@ github.com/TheZeroSlave/zapsentry v1.23.0 h1:TKyzfEL7LRlRr+7AvkukVLZ+jZPC++ebCUv
|
||||
github.com/TheZeroSlave/zapsentry v1.23.0/go.mod h1:3DRFLu4gIpnCTD4V9HMCBSaqYP8gYU7mZickrs2/rIY=
|
||||
github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4=
|
||||
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA=
|
||||
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
|
||||
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||
github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264=
|
||||
github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
|
||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
@@ -118,86 +106,54 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/avast/apkparser v0.0.0-20250307094510-e2100ee9c0f5 h1:zRaiVswCgpvZ6lyeErxC94EJaZdSoj0mzUPYRBOnqCI=
|
||||
github.com/avast/apkparser v0.0.0-20250307094510-e2100ee9c0f5/go.mod h1:GNvprXNmXaDjpHmN3RFxz5QdK5VXTUvmQludCbjoBy4=
|
||||
github.com/avast/apkparser v0.0.0-20250530053210-166ba1767cb9 h1:/c8YtwIZvwI43QdQh/nh5s4GYWnMPqnzZNJ5JFxMAkU=
|
||||
github.com/avast/apkparser v0.0.0-20250530053210-166ba1767cb9/go.mod h1:3F9A8btIerUcuy7Fmno+g/nIk4ELKJ6NCs2/KK1bvLs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.16 h1:XkruGnXX1nEZ+Nyo9v84TzsX+nj86icbFAeust6uo8A=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.16/go.mod h1:uCW7PNjGwZ5cOGZ5jr8vCWrYkGIhPoTNV23Q/tpHKzg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.69 h1:8B8ZQboRc3uaIKjshve/XlvJ570R7BKNy3gftSbS178=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.69/go.mod h1:gPME6I8grR1jCqBFEGthULiolzf/Sexq/Wy42ibKK9c=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31 h1:oQWSGexYasNpYp4epLGZxxjsDo8BMBh6iNWkTXQvkwk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.31/go.mod h1:nc332eGUU+djP3vrMI6blS0woaCfHTe3KiSQUVTMRq0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.75 h1:S61/E3N01oral6B3y9hZ2E1iFDqCZPPOBoBQretCnBI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.75/go.mod h1:bDMQbkI1vJbNjnvJYpPTSNYBkI/VIv18ngWb/K84tkk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.79 h1:mGo6WGWry+s5GEf2GLfw3zkHad109FQmtvBV3VYQ8mA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.79/go.mod h1:siwnpWxHYFSSge7Euw9lGMgQBgvRyym352mCuGNHsMQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs=
|
||||
github.com/avast/apkparser v0.0.0-20250626104540-d53391f4d69d h1:PGSn2pnK/u5ZBompy83R6Wo4BqLYp3dX43QWDoPv7TA=
|
||||
github.com/avast/apkparser v0.0.0-20250626104540-d53391f4d69d/go.mod h1:3F9A8btIerUcuy7Fmno+g/nIk4ELKJ6NCs2/KK1bvLs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.7 h1:zS1O6hr6t0nZdBCMFc/c9OyZFyLhXhf/B2IZ9Y0lRQE=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.7/go.mod h1:GpHmi1PQDdL5pP4JaB00pU0ek4EXVcYH7IkjkUadQmM=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.11 h1:1Fnb+7Dk96/VYx/uYfzk5sU2V0b0y2RWZROiMZCN/Io=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.11/go.mod h1:iuvn9v10dkxU4sDgtTXGWY0MrtkEcmkUmjv4clxhuTc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.5 h1:fSuJX/VBJKufwJG/szWgUdRJVyRiEQDDXNh/6NPrTBg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.5/go.mod h1:LvN0noQuST+3Su55Wl++BkITpptnfN9g6Ohkv4zs9To=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.35 h1:th/m+Q18CkajTw1iqx2cKkLCij/uz8NMwJFPK91p2ug=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.35/go.mod h1:dkJuf0a1Bc8HAA0Zm2MoTGm/WDC18Td9vSbrQ1+VqE8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.1 h1:4nm2G6A4pV9rdlWzGMPv4BNtQp22v1hg3yrtkYpeLl8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.1/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.3 h1:VHPZakq2L7w+RLzV54LmQavbvheFaR2u1NomJRSEfcU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.3/go.mod h1:DX1e/lkbsAt0MkY3NgLYuH4jQvRfw8MYxTe9feR7aXM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16 h1:/ldKrPPXTC421bTNWrUIpq3CxwHwRI/kpc+jPUTJocM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.16/go.mod h1:5vkf/Ws0/wgIMJDQbjI4p2op86hNW6Hie5QtebrDgT8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.16 h1:2HuI7vWKhFWsBhIr2Zq8KfFZT6xqaId2XXnXZjkbEuc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.16/go.mod h1:BrwWnsfbFtFeRjdx0iM1ymvlqDX1Oz68JsQaibX/wG8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.3 h1:BRXS0U76Z8wfF+bnkilA2QwpIch6URlm++yPUt9QPmQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.3/go.mod h1:bNXKFFyaiVvWuR6O16h/I1724+aXe/tAkA9/QS01t5k=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.2 h1:T6Wu+8E2LeTUqzqQ/Bh1EoFNj1u4jUyveMgmTlu9fDU=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.2/go.mod h1:chSY8zfqmS0OnhZoO/hpPx/BHfAIL80m77HwhRLYScY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.4 h1:ihddI5wufQQCJiujUgAvWRqZcfDmSKIfXlAuX7T95cg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.4/go.mod h1:PJtxxMdj747j8DeZENRTTYAz/lx/pADn/U0k7YNNiUY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.6 h1:+UdAoQcO1KupOdam6vJC06kzmbeES64L0W9FM+LEvow=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.34.6/go.mod h1:0PvYt3tRBPMJ/vky7631/4C6OCvWecnWwR6oq1jF4Uk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.4 h1:EU58LP8ozQDVroOEyAfcq0cGc5R/FTZjVoYJ6tvby3w=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.4/go.mod h1:CrtOgCcysxMvrCoHnvNAD7PHWclmoFG78Q2xLK0KKcs=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2 h1:XB4z0hbQtpmBnb1FQYvKaCM7UsS6Y/u8jVBwIUGeCTk=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.2/go.mod h1:hwRpqkRxnQ58J9blRDrB4IanlXCpcKmsC83EhG77upg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.21 h1:nyLjs8sYJShFYj6aiyjCBI3EcLn1udWrQTjEF+SOXB0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.21/go.mod h1:EhdxtZ+g84MSGrSrHzZiUm9PYiZkrADNja15wtRJSJo=
|
||||
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7 h1:BszAktdUo2xlzmYHjWMq70DqJ7cROM8iBd3f6hrpuMQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7/go.mod h1:XJ1yHki/P7ZPuG4fd3f0Pg/dSGA2cTQBCLw82MH2H48=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.7 h1:zmZ8qvtE9chfhBPuKB2aQFxW5F/rpwXUgmcVCgQzqRw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.7/go.mod h1:vVYfbpd2l+pKqlSIDIOgouxNsGu5il9uDp0ooWb0jys=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.7 h1:u3VbDKUCWarWiU+aIUK4gjTr/wQFXV17y3hgNno9fcA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.7/go.mod h1:/OuMQwhSyRapYxq6ZNpPer8juGNrB4P5Oz8bZ2cgjQE=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.88.0 h1:k5JXPr+2SrPDwM3PdygZUenn0lVPLa3KOs7cCYqinFs=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.88.0/go.mod h1:xajPTguLoeQMAOE44AAP2RQoUhF8ey1g5IFHARv71po=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.38.2 h1:Djc2m7mTPuizL1iMxJfMc209PDy2AqiN1AXrtq/rBdY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.38.2/go.mod h1:kHMCS+JDWKuKSDP9J/v3dlV2S9zNBKbXzaLy/kHSdEE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.2 h1:rcoTaYOhGE/zfxE1uR6X5fvj+uKkqeCNRE0rBbiQM34=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.2/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.3 h1:BSIfeFtU9tlSt8vEYS7KzurMoAuYzYPWhcZiMtxVf2M=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.3/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.3 h1:yEiZ0ztgji2GsCb/6uQSITXcGdtmWMfLRys0jJFiUkc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.3/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8=
|
||||
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
|
||||
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E=
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
@@ -205,20 +161,16 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bill-rich/go-syslog v0.0.0-20220413021637-49edb52a574c h1:tSME5FDS02qQll3JYodI6RZR/g4EKOHApGv1wMZT+Z0=
|
||||
github.com/bill-rich/go-syslog v0.0.0-20220413021637-49edb52a574c/go.mod h1:+sCc6hztur+oZCLOsNk6wCCy+GLrnSNHSRmTnnL+8iQ=
|
||||
github.com/bitfinexcom/bitfinex-api-go v0.0.0-20210608095005-9e0b26f200fb h1:9v7Bzlg+1EBYi2IYcUmOwHReBEfqBbYIj3ZCi9cIe1Q=
|
||||
github.com/bitfinexcom/bitfinex-api-go v0.0.0-20210608095005-9e0b26f200fb/go.mod h1:EkOqCuelvo7DY8vCOoZ09p7pHvAK9B1PHI9GeM4Rdxc=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
|
||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.12.0 h1:k8oVjGhZel2qmCUsYwSE34jPNT9DL2wCBOtugsHv26g=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.12.0/go.mod h1:V4gJcNyAftH0rXpRp1SUVUuh+ACxOH1xOk/ZzkRHltg=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0 h1:0D4vKCHOvYrDU8u61TnE2JfNT4VRrBLphmxtqazTO+M=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0/go.mod h1:LOVmdZYVZ8jqdr4n9wWm1ocDiMz9IfMGfRkaYC1a52A=
|
||||
github.com/brianvoe/gofakeit/v7 v7.2.1 h1:AGojgaaCdgq4Adzrd2uWdbGNDyX6MWNhHdQBraNfOHI=
|
||||
github.com/brianvoe/gofakeit/v7 v7.2.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 h1:B91r9bHtXp/+XRgS5aZm6ZzTdz3ahgJYmkt4xZkgDz8=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0/go.mod h1:OeVe5ggFzoBnmgitZe/A+BqGOnv1DvU/0uiLQi1wutM=
|
||||
github.com/brianvoe/gofakeit/v7 v7.6.0 h1:M3RUb5CuS2IZmF/cP+O+NdLxJEuDAZxNQBwPbbqR6h4=
|
||||
github.com/brianvoe/gofakeit/v7 v7.6.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -226,18 +178,22 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
|
||||
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
|
||||
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
|
||||
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng=
|
||||
github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
|
||||
github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
|
||||
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
|
||||
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
|
||||
@@ -252,21 +208,25 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
||||
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/couchbase/gocb/v2 v2.10.0 h1:NNxZ4okToU1Ylqp6F8tE41CEJQPhb2WjufryAkeubOk=
|
||||
github.com/couchbase/gocb/v2 v2.10.0/go.mod h1:OSbMfQkP7ltbKiDZhsT2mGDhkQNmvGXxptKcxAUJQ2Y=
|
||||
github.com/couchbase/gocbcore/v10 v10.7.0 h1:lAEi0PNeEGKOu8pWrPUdtLOT2oGr1J/UTdGHVPC3r/0=
|
||||
github.com/couchbase/gocbcore/v10 v10.7.0/go.mod h1:Q8JWVenMCEOuRgrDQKApHbzzPif38HzefGgRVe9apAI=
|
||||
github.com/couchbase/gocb/v2 v2.11.0 h1:OVB+KlVeXlKVtziKx/LWZT7DClLsoQHQFrI4wan5Ijc=
|
||||
github.com/couchbase/gocb/v2 v2.11.0/go.mod h1:Y+lODSgyVzDSaf0Sy8sIzIa0RTAw3vlZUsjY6+FUq9Y=
|
||||
github.com/couchbase/gocbcore/v10 v10.8.0 h1:zDcJyYqOirFyC8T/aVvNL4N9oj6GI4qtaBuTGGWCDb4=
|
||||
github.com/couchbase/gocbcore/v10 v10.8.0/go.mod h1:OWKfU9R5Nm5V3QZBtfdZl5qCfgxtxTqOgXiNr4pn9/c=
|
||||
github.com/couchbase/gocbcoreps v0.1.3 h1:fILaKGCjxFIeCgAUG8FGmRDSpdrRggohOMKEgO9CUpg=
|
||||
github.com/couchbase/gocbcoreps v0.1.3/go.mod h1:hBFpDNPnRno6HH5cRXExhqXYRmTsFJlFHQx7vztcXPk=
|
||||
github.com/couchbase/goprotostellar v1.0.2 h1:yoPbAL9sCtcyZ5e/DcU5PRMOEFaJrF9awXYu3VPfGls=
|
||||
@@ -291,16 +251,16 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM=
|
||||
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
|
||||
github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
|
||||
github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
||||
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -342,8 +302,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/getsentry/sentry-go v0.32.0 h1:YKs+//QmwE3DcYtfKRH8/KyOOF/I6Qnx7qYGNHCGmCY=
|
||||
github.com/getsentry/sentry-go v0.32.0/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
@@ -362,17 +322,13 @@ github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Z
|
||||
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
@@ -389,26 +345,22 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
|
||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -430,8 +382,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -445,14 +397,12 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
|
||||
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
|
||||
github.com/google/go-github/v66 v66.0.0 h1:ADJsaXj9UotwdgK8/iFZtv7MLc8E8WBl62WLd/D/9+M=
|
||||
github.com/google/go-github/v66 v66.0.0/go.mod h1:+4SO9Zkuyf8ytMj0csN1NR/5OTR+MfqPp8P8dVlcvY4=
|
||||
github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
|
||||
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
|
||||
github.com/google/go-github/v67 v67.0.0 h1:g11NDAmfaBaCO8qYdI9fsmbaRipHNWRIU/2YGvlh4rg=
|
||||
github.com/google/go-github/v67 v67.0.0/go.mod h1:zH3K7BxjFndr9QSeFibx4lTKkYS3K9nDanoI1NjaOtY=
|
||||
github.com/google/go-github/v69 v69.0.0 h1:YnFvZ3pEIZF8KHmI8xyQQe3mYACdkhnaTV2hr7CP2/w=
|
||||
github.com/google/go-github/v69 v69.0.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM=
|
||||
github.com/google/go-github/v72 v72.0.0 h1:FcIO37BLoVPBO9igQQ6tStsv2asG4IPcYFi655PPvBM=
|
||||
github.com/google/go-github/v72 v72.0.0/go.mod h1:WWtw8GMRiL62mvIquf1kO3onRHeWWKmK01qdCY8c5fg=
|
||||
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
@@ -474,13 +424,10 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
@@ -498,8 +445,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@@ -533,8 +480,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||
github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg=
|
||||
github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
@@ -553,8 +500,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d h1:RnWZeH8N8KXfbwMTex/KKMYMj0FJRCF6tQubUuQ02GM=
|
||||
github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d/go.mod h1:phT/jsRPBAEqjAibu1BurrabCBNTYiVI+zbmyCZJY6Q=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
@@ -591,7 +536,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
@@ -599,8 +543,6 @@ github.com/mholt/archives v0.0.0-20241216060121-23e0af8fe73d h1:Vw3f39TqFSQLA+Oy
|
||||
github.com/mholt/archives v0.0.0-20241216060121-23e0af8fe73d/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
|
||||
github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
|
||||
github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I=
|
||||
github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
@@ -611,12 +553,16 @@ github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
@@ -638,13 +584,11 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||
github.com/nwaples/rardecode/v2 v2.2.1 h1:DgHK/O/fkTQEKBJxBMC5d9IU8IgauifbpG78+rZJMnI=
|
||||
github.com/nwaples/rardecode/v2 v2.2.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
@@ -653,11 +597,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
@@ -672,8 +615,6 @@ github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 h1:+/+DxvQaYifJ+grD4klzrS5y+KJXldn/2YTl5JG+vZ8=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -715,9 +656,8 @@ github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK
|
||||
github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw=
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1GjJohAA0p6hVEaDtHWWs=
|
||||
github.com/sendgrid/sendgrid-go v3.16.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
@@ -728,6 +668,10 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shuheiktgw/go-travis v0.3.1 h1:SAT16mi77ccqogOslnXxBXzXbpeyChaIYUwi2aJpVZY=
|
||||
github.com/shuheiktgw/go-travis v0.3.1/go.mod h1:avnFFDqJDdRHwlF9tgqvYi3asQCm/HGL8aLxYiKa4Yg=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
@@ -752,7 +696,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
@@ -760,8 +703,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tailscale/depaware v0.0.0-20250112153213-b748de04d81b h1:ewWb4cA+YO9/3X+v5UhdV+eKFsNBOPcGRh39Glshx/4=
|
||||
github.com/tailscale/depaware v0.0.0-20250112153213-b748de04d81b/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
||||
github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo=
|
||||
github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ=
|
||||
github.com/testcontainers/testcontainers-go/modules/elasticsearch v0.34.0 h1:BBwJUs9xBpt1uOfO+yAr2pYW75MsyzuO/o70HTPnhe4=
|
||||
@@ -788,13 +729,11 @@ github.com/trufflesecurity/overseer v1.2.8 h1:VXlWPiwYaQmwNxY2W1rVulEAG9O6iF1S0L
|
||||
github.com/trufflesecurity/overseer v1.2.8/go.mod h1:Dt6Y9LFpM+C/3rRWpy4//4iS5qrbb0pL3XvZqMd4zhg=
|
||||
github.com/trufflesecurity/touchfile v0.1.1 h1:Snhd5VEa8Cxd+D60nvLEj2kVeb1omY2tWwnhDhjTqdo=
|
||||
github.com/trufflesecurity/touchfile v0.1.1/go.mod h1:Yg/AUMrxAk+dWDUjIig0OyGgFOHFuWNw+t2S/GvO6Mk=
|
||||
github.com/trufflesecurity/waas-client-library-go v1.0.9 h1:dUS5s8/guamH5gHA0MvTmf5Qw2UILOHEayYIINU8FOw=
|
||||
github.com/trufflesecurity/waas-client-library-go v1.0.9/go.mod h1:0O/2T0CPq0wm8Wl9KEJklKbiRNQNZ1MOh4nCYY5V2zo=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/wasilibs/go-re2 v1.9.0 h1:kjAd8qbNvV4Ve2Uf+zrpTCrDHtqH4dlsRXktywo73JQ=
|
||||
github.com/wasilibs/go-re2 v1.9.0/go.mod h1:0sRtscWgpUdNA137bmr1IUgrRX0Su4dcn9AEe61y+yI=
|
||||
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4=
|
||||
@@ -811,8 +750,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xo/dburl v0.23.7 h1:UCiK8Dyll38NdDHVi7UOxhz5/ugWuyQGgQHdxfdEQDY=
|
||||
github.com/xo/dburl v0.23.7/go.mod h1:uazlaAQxj4gkshhfuuYyvwCBouOmNnG2aDxTCFZpmL4=
|
||||
github.com/xo/dburl v0.23.8 h1:NwFghJfjaUW7tp+WE5mTLQQCfgseRsvgXjlSvk7x4t4=
|
||||
github.com/xo/dburl v0.23.8/go.mod h1:uazlaAQxj4gkshhfuuYyvwCBouOmNnG2aDxTCFZpmL4=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
@@ -821,50 +760,48 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zU
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
|
||||
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
|
||||
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
gitlab.com/gitlab-org/api/client-go v0.127.0 h1:8xnxcNKGF2gDazEoMs+hOZfOspSSw8D0vAoWhQk9U+U=
|
||||
gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE=
|
||||
go.einride.tech/aip v0.60.0 h1:h6bgabZ5BCfAptbGex8jbh3VvPBRLa6xq+pQ1CAjHYw=
|
||||
go.einride.tech/aip v0.60.0/go.mod h1:SdLbSbgSU60Xkb4TMkmsZEQPHeEWx0ikBoq5QnqZvdg=
|
||||
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
gitlab.com/gitlab-org/api/client-go v0.129.0 h1:o9KLn6fezmxBQWYnQrnilwyuOjlx4206KP0bUn3HuBE=
|
||||
gitlab.com/gitlab-org/api/client-go v0.129.0/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM=
|
||||
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
|
||||
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -873,8 +810,8 @@ go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwE
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
|
||||
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
@@ -890,8 +827,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -920,10 +857,9 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -945,15 +881,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -964,10 +898,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -990,7 +922,6 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1010,13 +941,13 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1026,12 +957,12 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -1058,12 +989,9 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1076,10 +1004,8 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.229.0 h1:p98ymMtqeJ5i3lIBMj5MpR9kzIIgzpHHh8vQ+vgAzx8=
|
||||
google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0=
|
||||
google.golang.org/api v0.232.0 h1:qGnmaIMf7KcuwHOlF3mERVzChloDYwRfOJOrHt8YC3I=
|
||||
google.golang.org/api v0.232.0/go.mod h1:p9QCfBWZk1IJETUdbTKloR5ToFdKbYh2fkjsUL6vNoY=
|
||||
google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc=
|
||||
google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -1099,18 +1025,12 @@ google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvx
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 h1:IqsN8hx+lWLqlN+Sc3DoMy/watjofWiU8sRFgQ8fhKM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1120,18 +1040,16 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
|
||||
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -1156,8 +1074,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -57,7 +57,7 @@ Note: Be sure to update the tests to reference the new secret values in GSM, or
|
||||
|
||||
3. Add a 'version' field in ExtraData for both existing and new detector versions.
|
||||
|
||||
4. Update the existing detector in DefaultDetectors in `/pkg/engine/defaults.go`
|
||||
4. Update the existing detector in DefaultDetectors in `/pkg/engine/defaults/defaults.go`
|
||||
|
||||
5. Proceed from step 3 of [Creating a new Secret Scanner](#creating-a-new-secret-scanner)
|
||||
|
||||
@@ -87,7 +87,7 @@ Note: Be sure to update the tests to reference the new secret values in GSM, or
|
||||
4. Found and unverified (indeterminately due to an unexpected API response)
|
||||
5. Not found
|
||||
6. Any false positive cases that you come across
|
||||
5. Add your new detector to DefaultDetectors in `/pkg/engine/defaults.go`
|
||||
5. Add your new detector to DefaultDetectors in `/pkg/engine/defaults/defaults.go`
|
||||
6. Create a merge request for review. CI tests must be passing.
|
||||
|
||||
## Addendum
|
||||
|
||||
@@ -53,7 +53,7 @@ Note: Be sure to update the tests to reference the new secret values in GSM, or
|
||||
|
||||
3. Add a 'version' field in ExtraData for both existing and new detector versions.
|
||||
|
||||
4. Update the existing detector in DefaultDetectors in `/pkg/engine/defaults.go`
|
||||
4. Update the existing detector in DefaultDetectors in `/pkg/engine/defaults/defaults.go`
|
||||
|
||||
5. Proceed from step 3 of [Creating a new Secret Scanner](#creating-a-new-secret-scanner)
|
||||
|
||||
@@ -71,8 +71,8 @@ Note: Be sure to update the tests to reference the new secret values in GSM, or
|
||||
```
|
||||
4. Add the Secret Detector to TruffleHog's Default Detectors
|
||||
|
||||
Add the secret scanner to the [`pkg/engine/defaults.go`](https://github.com/trufflesecurity/trufflehog/blob/main/pkg/engine/defaults.go) file like [`github.com/trufflesecurity/trufflehog/v3/pkg/detectors/<detector_name>`](https://github.com/trufflesecurity/trufflehog/blob/b71ea27a696bdf1c3141f637fda4ee4936c2f2d6/pkg/engine/defaults.go#L9) and
|
||||
[`<detector_name>.Scanner{}`](https://github.com/trufflesecurity/trufflehog/blob/b71ea27a696bdf1c3141f637fda4ee4936c2f2d6/pkg/engine/defaults.go#L1546)
|
||||
Add the secret scanner to the [`pkg/engine/defaults/defaults.go`](https://github.com/trufflesecurity/trufflehog/blob/main/pkg/engine/defaults/defaults.go) file like [`github.com/trufflesecurity/trufflehog/v3/pkg/detectors/<detector_name>`](https://github.com/trufflesecurity/trufflehog/blob/b71ea27a696bdf1c3141f637fda4ee4936c2f2d6/pkg/engine/defaults/defaults.go#L9) and
|
||||
[`<detector_name>.Scanner{}`](https://github.com/trufflesecurity/trufflehog/blob/b71ea27a696bdf1c3141f637fda4ee4936c2f2d6/pkg/engine/defaults/defaults.go#L1546)
|
||||
|
||||
5. Complete the Secret Detector.
|
||||
|
||||
@@ -83,7 +83,7 @@ Note: Be sure to update the tests to reference the new secret values in GSM, or
|
||||
2. Update the verifier code to use a non-destructive API call that can determine whether the secret is valid or not.
|
||||
* Make sure you understand [verification indeterminacy](#verification-indeterminacy).
|
||||
3. Create a [test for the detector](#testing-the-detector).
|
||||
4. Add your new detector to DefaultDetectors in `/pkg/engine/defaults.go`.
|
||||
4. Add your new detector to DefaultDetectors in `/pkg/engine/defaults/defaults.go`.
|
||||
5. Create a pull request for review.
|
||||
|
||||
### Testing the Detector
|
||||
|
||||
@@ -181,7 +181,7 @@ func main() {
|
||||
defer sem.Release(1)
|
||||
defer wgChunkers.Done()
|
||||
logger.Info("cloning repo", "repo", r)
|
||||
path, repo, err := git.CloneRepoUsingUnauthenticated(ctx, r)
|
||||
path, repo, err := git.CloneRepoUsingUnauthenticated(ctx, r, "")
|
||||
if err != nil {
|
||||
logFatal(err, "error cloning repo", "repo", r)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func main() {
|
||||
SkipBinaries: true,
|
||||
SkipArchives: false,
|
||||
Concurrency: runtime.NumCPU(),
|
||||
SourceMetadataFunc: func(file, email, commit, timestamp, repository string, line int64) *source_metadatapb.MetaData {
|
||||
SourceMetadataFunc: func(file, email, commit, timestamp, repository, repositoryLocalPath string, line int64) *source_metadatapb.MetaData {
|
||||
return &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Git{
|
||||
Git: &source_metadatapb.Git{
|
||||
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
concurrency = cli.Flag("concurrency", "Number of concurrent workers.").Default(strconv.Itoa(runtime.NumCPU())).Int()
|
||||
noVerification = cli.Flag("no-verification", "Don't verify the results.").Bool()
|
||||
onlyVerified = cli.Flag("only-verified", "Only output verified results.").Hidden().Bool()
|
||||
results = cli.Flag("results", "Specifies which type(s) of results to output: verified, unknown, unverified, filtered_unverified. Defaults to verified,unverified,unknown.").String()
|
||||
results = cli.Flag("results", "Specifies which type(s) of results to output: verified (confirmed valid by API), unknown (verification failed due to error), unverified (detected but not verified), filtered_unverified (unverified but would have been filtered out). Defaults to verified,unverified,unknown.").String()
|
||||
noColor = cli.Flag("no-color", "Disable colorized output").Bool()
|
||||
noColour = cli.Flag("no-colour", "Alias for --no-color").Hidden().Bool()
|
||||
|
||||
@@ -72,6 +72,7 @@ var (
|
||||
printAvgDetectorTime = cli.Flag("print-avg-detector-time", "Print the average time spent on each detector.").Bool()
|
||||
noUpdate = cli.Flag("no-update", "Don't check for updates.").Bool()
|
||||
fail = cli.Flag("fail", "Exit with code 183 if results are found.").Bool()
|
||||
failOnScanErrors = cli.Flag("fail-on-scan-errors", "Exit with non-zero error code if an error occurs during the scan.").Bool()
|
||||
verifiers = cli.Flag("verifier", "Set custom verification endpoints.").StringMap()
|
||||
customVerifiersOnly = cli.Flag("custom-verifiers-only", "Only use custom verification endpoints.").Bool()
|
||||
detectorTimeout = cli.Flag("detector-timeout", "Maximum time to spend scanning chunks per detector (e.g., 30s).").Duration()
|
||||
@@ -90,18 +91,21 @@ var (
|
||||
skipAdditionalRefs = cli.Flag("skip-additional-refs", "Skip additional references.").Bool()
|
||||
userAgentSuffix = cli.Flag("user-agent-suffix", "Suffix to add to User-Agent.").String()
|
||||
|
||||
gitScan = cli.Command("git", "Find credentials in git repositories.")
|
||||
gitScanURI = gitScan.Arg("uri", "Git repository URL. https://, file://, or ssh:// schema expected.").Required().String()
|
||||
gitScanIncludePaths = gitScan.Flag("include-paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
||||
gitScanExcludePaths = gitScan.Flag("exclude-paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
||||
gitScanExcludeGlobs = gitScan.Flag("exclude-globs", "Comma separated list of globs to exclude in scan. This option filters at the `git log` level, resulting in faster scans.").String()
|
||||
gitScanSinceCommit = gitScan.Flag("since-commit", "Commit to start scan from.").String()
|
||||
gitScanBranch = gitScan.Flag("branch", "Branch to scan.").String()
|
||||
gitScanMaxDepth = gitScan.Flag("max-depth", "Maximum depth of commits to scan.").Int()
|
||||
gitScanBare = gitScan.Flag("bare", "Scan bare repository (e.g. useful while using in pre-receive hooks)").Bool()
|
||||
_ = gitScan.Flag("allow", "No-op flag for backwards compat.").Bool()
|
||||
_ = gitScan.Flag("entropy", "No-op flag for backwards compat.").Bool()
|
||||
_ = gitScan.Flag("regex", "No-op flag for backwards compat.").Bool()
|
||||
gitScan = cli.Command("git", "Find credentials in git repositories.")
|
||||
gitScanURI = gitScan.Arg("uri", "Git repository URL. https://, file://, or ssh:// schema expected.").Required().String()
|
||||
gitScanIncludePaths = gitScan.Flag("include-paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
||||
gitScanExcludePaths = gitScan.Flag("exclude-paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
||||
gitScanExcludeGlobs = gitScan.Flag("exclude-globs", "Comma separated list of globs to exclude in scan. This option filters at the `git log` level, resulting in faster scans.").String()
|
||||
gitScanSinceCommit = gitScan.Flag("since-commit", "Commit to start scan from.").String()
|
||||
gitScanBranch = gitScan.Flag("branch", "Branch to scan.").String()
|
||||
gitScanMaxDepth = gitScan.Flag("max-depth", "Maximum depth of commits to scan.").Int()
|
||||
gitScanBare = gitScan.Flag("bare", "Scan bare repository (e.g. useful while using in pre-receive hooks)").Bool()
|
||||
gitClonePath = gitScan.Flag("clone-path", "Custom path where the repository should be cloned (default: temp dir).").String()
|
||||
gitNoCleanup = gitScan.Flag("no-cleanup", "Do not delete cloned repositories after scanning (can only be used with --clone-path).").Bool()
|
||||
gitTrustLocalGitConfig = gitScan.Flag("trust-local-git-config", "Trust local git config.").Bool()
|
||||
_ = gitScan.Flag("allow", "No-op flag for backwards compat.").Bool()
|
||||
_ = gitScan.Flag("entropy", "No-op flag for backwards compat.").Bool()
|
||||
_ = gitScan.Flag("regex", "No-op flag for backwards compat.").Bool()
|
||||
|
||||
githubScan = cli.Command("github", "Find credentials in GitHub repositories.")
|
||||
githubScanEndpoint = githubScan.Flag("endpoint", "GitHub endpoint.").Default("https://api.github.com").String()
|
||||
@@ -120,6 +124,9 @@ var (
|
||||
githubScanGistComments = githubScan.Flag("gist-comments", "Include gist comments in scan.").Bool()
|
||||
githubCommentsTimeframeDays = githubScan.Flag("comments-timeframe", "Number of days in the past to review when scanning issue, PR, and gist comments.").Uint32()
|
||||
githubAuthInUrl = githubScan.Flag("auth-in-url", "Embed authentication credentials in repository URLs instead of using secure HTTP headers").Bool()
|
||||
githubClonePath = githubScan.Flag("clone-path", "Custom path where the repository should be cloned (default: temp dir).").String()
|
||||
githubNoCleanup = githubScan.Flag("no-cleanup", "Do not delete cloned repositories after scanning (can only be used with --clone-path).").Bool()
|
||||
githubIgnoreGists = githubScan.Flag("ignore-gists", "Ignore all gists in scan.").Bool()
|
||||
|
||||
// GitHub Cross Fork Object Reference Experimental Feature
|
||||
githubExperimentalScan = cli.Command("github-experimental", "Run an experimental GitHub scan. Must specify at least one experimental sub-module to run: object-discovery.")
|
||||
@@ -136,11 +143,14 @@ var (
|
||||
gitlabScanEndpoint = gitlabScan.Flag("endpoint", "GitLab endpoint.").Default("https://gitlab.com").String()
|
||||
gitlabScanRepos = gitlabScan.Flag("repo", "GitLab repo url. You can repeat this flag. Leave empty to scan all repos accessible with provided credential. Example: https://gitlab.com/org/repo.git").Strings()
|
||||
gitlabScanToken = gitlabScan.Flag("token", "GitLab token. Can be provided with environment variable GITLAB_TOKEN.").Envar("GITLAB_TOKEN").Required().String()
|
||||
gitlabScanGroupIds = gitlabScan.Flag("group-id", "GitLab group ID. If provided, it will scan the group and its subgroups. You can repeat this flag.").Strings()
|
||||
gitlabScanIncludePaths = gitlabScan.Flag("include-paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String()
|
||||
gitlabScanExcludePaths = gitlabScan.Flag("exclude-paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String()
|
||||
gitlabScanIncludeRepos = gitlabScan.Flag("include-repos", `Repositories to include in an org scan. This can also be a glob pattern. You can repeat this flag. Must use Gitlab repo full name. Example: "trufflesecurity/trufflehog", "trufflesecurity/t*"`).Strings()
|
||||
gitlabScanExcludeRepos = gitlabScan.Flag("exclude-repos", `Repositories to exclude in an org scan. This can also be a glob pattern. You can repeat this flag. Must use Gitlab repo full name. Example: "trufflesecurity/driftwood", "trufflesecurity/d*"`).Strings()
|
||||
gitlabAuthInUrl = gitlabScan.Flag("auth-in-url", "Embed authentication credentials in repository URLs instead of using secure HTTP headers").Bool()
|
||||
gitlabClonePath = gitlabScan.Flag("clone-path", "Custom path where the repository should be cloned (default: temp dir)").String()
|
||||
gitlabNoCleanup = gitlabScan.Flag("no-cleanup", "Do not delete cloned repositories after scanning (can only be used with --clone-path).").Bool()
|
||||
|
||||
filesystemScan = cli.Command("filesystem", "Find credentials in a filesystem.")
|
||||
filesystemPaths = filesystemScan.Arg("path", "Path to file or directory to scan.").Strings()
|
||||
@@ -178,15 +188,17 @@ var (
|
||||
syslogProtocol = syslogScan.Flag("protocol", "Protocol to listen on. udp or tcp").String()
|
||||
syslogTLSCert = syslogScan.Flag("cert", "Path to TLS cert.").String()
|
||||
syslogTLSKey = syslogScan.Flag("key", "Path to TLS key.").String()
|
||||
syslogFormat = syslogScan.Flag("format", "Log format. Can be rfc3164 or rfc5424").String()
|
||||
syslogFormat = syslogScan.Flag("format", "Log format. Can be rfc3164 or rfc5424").Required().String()
|
||||
|
||||
circleCiScan = cli.Command("circleci", "Scan CircleCI")
|
||||
circleCiScanToken = circleCiScan.Flag("token", "CircleCI token. Can also be provided with environment variable").Envar("CIRCLECI_TOKEN").Required().String()
|
||||
|
||||
dockerScan = cli.Command("docker", "Scan Docker Image")
|
||||
dockerScanImages = dockerScan.Flag("image", "Docker image to scan. Use the file:// prefix to point to a local tarball, otherwise a image registry is assumed.").Required().Strings()
|
||||
dockerScanToken = dockerScan.Flag("token", "Docker bearer token. Can also be provided with environment variable").Envar("DOCKER_TOKEN").String()
|
||||
dockerExcludePaths = dockerScan.Flag("exclude-paths", "Comma separated list of paths to exclude from scan").String()
|
||||
dockerScan = cli.Command("docker", "Scan Docker Image")
|
||||
dockerScanImages = dockerScan.Flag("image", "Docker image to scan. Use the file:// prefix to point to a local tarball, the docker:// prefix to point to the docker daemon, otherwise an image registry is assumed.").Strings()
|
||||
dockerScanToken = dockerScan.Flag("token", "Docker bearer token. Can also be provided with environment variable").Envar("DOCKER_TOKEN").String()
|
||||
dockerExcludePaths = dockerScan.Flag("exclude-paths", "Comma separated list of paths to exclude from scan").String()
|
||||
dockerScanNamespace = dockerScan.Flag("namespace", "Docker namespace (organization or user). For non-Docker Hub registries, include the registry address as well (e.g., ghcr.io/namespace or quay.io/namespace).").String()
|
||||
dockerScanRegistryToken = dockerScan.Flag("registry-token", "Optional Docker registry access token. Provide this if you want to include private images within the specified namespace.").String()
|
||||
|
||||
travisCiScan = cli.Command("travisci", "Scan TravisCI")
|
||||
travisCiScanToken = travisCiScan.Flag("token", "TravisCI token. Can also be provided with environment variable").Envar("TRAVISCI_TOKEN").Required().String()
|
||||
@@ -275,8 +287,9 @@ func init() {
|
||||
|
||||
cli.Version("trufflehog " + version.BuildVersion)
|
||||
|
||||
// Support -h for help
|
||||
// Support -h for help and write it to stdout.
|
||||
cli.HelpFlag.Short('h')
|
||||
cli.UsageWriter(os.Stdout)
|
||||
|
||||
// Check if the TUI environment variable is set.
|
||||
if ok, err := strconv.ParseBool(os.Getenv("TUI_PARENT")); err == nil {
|
||||
@@ -449,6 +462,16 @@ func run(state overseer.State) {
|
||||
// OSS Default APK handling on
|
||||
feature.EnableAPKHandler.Store(true)
|
||||
|
||||
// OSS Default Use Git Mirror on
|
||||
feature.UseGitMirror.Store(true)
|
||||
|
||||
// OSS Default simplified gitlab enumeration
|
||||
feature.UseSimplifiedGitlabEnumeration.Store(true)
|
||||
feature.GitlabProjectsPerPage.Store(100)
|
||||
|
||||
// OSS Default using github graphql api for issues, pr's and comments
|
||||
feature.UseGithubGraphQLAPI.Store(false)
|
||||
|
||||
conf := &config.Config{}
|
||||
if *configFilename != "" {
|
||||
var err error
|
||||
@@ -694,32 +717,48 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
}
|
||||
eng.Start(ctx)
|
||||
|
||||
persistGitRepo := *gitNoCleanup || *githubNoCleanup || *gitlabNoCleanup
|
||||
gitCloneTempPath := ""
|
||||
|
||||
defer func() {
|
||||
// Clean up temporary artifacts.
|
||||
if err := cleantemp.CleanTempArtifacts(ctx); err != nil {
|
||||
ctx.Logger().Error(err, "error cleaning temp artifacts")
|
||||
}
|
||||
|
||||
if *jsonLegacy {
|
||||
// If JSON legacy is enabled, that means the cloned repos are not deleted yet
|
||||
// because they were needed for outputting legacy JSON.
|
||||
// We only clean them up here if the user did not request to persist them.
|
||||
if !persistGitRepo {
|
||||
if err := cleantemp.CleanTempDirsForLegacyJSON(gitCloneTempPath); err != nil {
|
||||
ctx.Logger().Error(err, "error cleaning temp artifacts for legacy JSON")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var refs []sources.JobProgressRef
|
||||
switch cmd {
|
||||
case gitScan.FullCommand():
|
||||
// validate the commit for local repository only
|
||||
if *gitScanSinceCommit != "" && strings.HasPrefix(*gitScanURI, "file") {
|
||||
if !isValidCommit(*gitScanURI, *gitScanSinceCommit) {
|
||||
ctx.Logger().Info("Warning: The provided commit hash appears to be invalid.")
|
||||
}
|
||||
|
||||
if err := validateClonePath(*gitClonePath, *gitNoCleanup); err != nil {
|
||||
return scanMetrics, err
|
||||
}
|
||||
|
||||
gitCfg := sources.GitConfig{
|
||||
URI: *gitScanURI,
|
||||
IncludePathsFile: *gitScanIncludePaths,
|
||||
ExcludePathsFile: *gitScanExcludePaths,
|
||||
HeadRef: *gitScanBranch,
|
||||
BaseRef: *gitScanSinceCommit,
|
||||
MaxDepth: *gitScanMaxDepth,
|
||||
Bare: *gitScanBare,
|
||||
ExcludeGlobs: *gitScanExcludeGlobs,
|
||||
URI: *gitScanURI,
|
||||
IncludePathsFile: *gitScanIncludePaths,
|
||||
ExcludePathsFile: *gitScanExcludePaths,
|
||||
HeadRef: *gitScanBranch,
|
||||
BaseRef: *gitScanSinceCommit,
|
||||
MaxDepth: *gitScanMaxDepth,
|
||||
Bare: *gitScanBare,
|
||||
ExcludeGlobs: *gitScanExcludeGlobs,
|
||||
ClonePath: *gitClonePath,
|
||||
NoCleanup: *gitNoCleanup,
|
||||
PrintLegacyJSON: *jsonLegacy,
|
||||
TrustLocalGitConfig: *gitTrustLocalGitConfig,
|
||||
}
|
||||
if ref, err := eng.ScanGit(ctx, gitCfg); err != nil {
|
||||
return scanMetrics, fmt.Errorf("failed to scan Git: %v", err)
|
||||
@@ -727,6 +766,7 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
refs = []sources.JobProgressRef{ref}
|
||||
}
|
||||
case githubScan.FullCommand():
|
||||
gitCloneTempPath = *githubClonePath
|
||||
filter, err := common.FilterFromFiles(*githubScanIncludePaths, *githubScanExcludePaths)
|
||||
if err != nil {
|
||||
return scanMetrics, fmt.Errorf("could not create filter: %v", err)
|
||||
@@ -738,6 +778,10 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
return scanMetrics, fmt.Errorf("invalid config: you cannot specify both organizations and repositories at the same time")
|
||||
}
|
||||
|
||||
if err := validateClonePath(*githubClonePath, *githubNoCleanup); err != nil {
|
||||
return scanMetrics, err
|
||||
}
|
||||
|
||||
cfg := sources.GithubConfig{
|
||||
Endpoint: *githubScanEndpoint,
|
||||
Token: *githubScanToken,
|
||||
@@ -755,7 +799,12 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
CommentsTimeframeDays: *githubCommentsTimeframeDays,
|
||||
Filter: filter,
|
||||
AuthInUrl: *githubAuthInUrl,
|
||||
ClonePath: *githubClonePath,
|
||||
NoCleanup: *githubNoCleanup,
|
||||
IgnoreGists: *githubIgnoreGists,
|
||||
PrintLegacyJSON: *jsonLegacy,
|
||||
}
|
||||
|
||||
if ref, err := eng.ScanGitHub(ctx, cfg); err != nil {
|
||||
return scanMetrics, fmt.Errorf("failed to scan Github: %v", err)
|
||||
} else {
|
||||
@@ -775,20 +824,34 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
refs = []sources.JobProgressRef{ref}
|
||||
}
|
||||
case gitlabScan.FullCommand():
|
||||
gitCloneTempPath = *gitlabClonePath
|
||||
filter, err := common.FilterFromFiles(*gitlabScanIncludePaths, *gitlabScanExcludePaths)
|
||||
if err != nil {
|
||||
return scanMetrics, fmt.Errorf("could not create filter: %v", err)
|
||||
}
|
||||
|
||||
cfg := sources.GitlabConfig{
|
||||
Endpoint: *gitlabScanEndpoint,
|
||||
Token: *gitlabScanToken,
|
||||
Repos: *gitlabScanRepos,
|
||||
IncludeRepos: *gitlabScanIncludeRepos,
|
||||
ExcludeRepos: *gitlabScanExcludeRepos,
|
||||
Filter: filter,
|
||||
AuthInUrl: *gitlabAuthInUrl,
|
||||
if len(*gitlabScanRepos) > 0 && len(*gitlabScanGroupIds) > 0 {
|
||||
return scanMetrics, fmt.Errorf("invalid config: you cannot specify both repositories and groups at the same time")
|
||||
}
|
||||
|
||||
if err := validateClonePath(*gitlabClonePath, *gitlabNoCleanup); err != nil {
|
||||
return scanMetrics, err
|
||||
}
|
||||
|
||||
cfg := sources.GitlabConfig{
|
||||
Endpoint: *gitlabScanEndpoint,
|
||||
Token: *gitlabScanToken,
|
||||
Repos: *gitlabScanRepos,
|
||||
GroupIds: *gitlabScanGroupIds,
|
||||
IncludeRepos: *gitlabScanIncludeRepos,
|
||||
ExcludeRepos: *gitlabScanExcludeRepos,
|
||||
Filter: filter,
|
||||
AuthInUrl: *gitlabAuthInUrl,
|
||||
ClonePath: *gitlabClonePath,
|
||||
NoCleanup: *gitlabNoCleanup,
|
||||
PrintLegacyJSON: *jsonLegacy,
|
||||
}
|
||||
|
||||
if ref, err := eng.ScanGitLab(ctx, cfg); err != nil {
|
||||
return scanMetrics, fmt.Errorf("failed to scan GitLab: %v", err)
|
||||
} else {
|
||||
@@ -873,11 +936,25 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
refs = []sources.JobProgressRef{ref}
|
||||
}
|
||||
case dockerScan.FullCommand():
|
||||
if *dockerScanImages != nil && *dockerScanNamespace != "" {
|
||||
return scanMetrics, fmt.Errorf("invalid config: you cannot specify both images and namespace at the same time")
|
||||
}
|
||||
|
||||
if *dockerScanImages == nil && *dockerScanNamespace == "" {
|
||||
return scanMetrics, fmt.Errorf("invalid config: both images and namespace cannot be empty; one is required")
|
||||
}
|
||||
|
||||
if *dockerScanRegistryToken != "" && *dockerScanNamespace == "" {
|
||||
return scanMetrics, fmt.Errorf("invalid config: registry token can only be used with registry namespace")
|
||||
}
|
||||
|
||||
cfg := sources.DockerConfig{
|
||||
BearerToken: *dockerScanToken,
|
||||
Images: *dockerScanImages,
|
||||
UseDockerKeychain: *dockerScanToken == "",
|
||||
ExcludePaths: strings.Split(*dockerExcludePaths, ","),
|
||||
Namespace: *dockerScanNamespace,
|
||||
RegistryToken: *dockerScanRegistryToken,
|
||||
}
|
||||
if ref, err := eng.ScanDocker(ctx, cfg); err != nil {
|
||||
return scanMetrics, fmt.Errorf("failed to scan Docker: %v", err)
|
||||
@@ -1014,8 +1091,12 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
}
|
||||
|
||||
// Print any non-fatal errors reported during the scan.
|
||||
var retErr error
|
||||
for _, ref := range refs {
|
||||
if errs := ref.Snapshot().Errors; len(errs) > 0 {
|
||||
if *failOnScanErrors {
|
||||
retErr = fmt.Errorf("encountered errors during scan")
|
||||
}
|
||||
errMsgs := make([]string, len(errs))
|
||||
for i := 0; i < len(errs); i++ {
|
||||
errMsgs[i] = errs[i].Error()
|
||||
@@ -1032,7 +1113,7 @@ func runSingleScan(ctx context.Context, cmd string, cfg engine.Config) (metrics,
|
||||
printAverageDetectorTime(eng)
|
||||
}
|
||||
|
||||
return metrics{Metrics: eng.GetMetrics(), hasFoundResults: eng.HasFoundResults()}, nil
|
||||
return metrics{Metrics: eng.GetMetrics(), hasFoundResults: eng.HasFoundResults()}, retErr
|
||||
}
|
||||
|
||||
// parseResults ensures that users provide valid CSV input to `--results`.
|
||||
@@ -1096,14 +1177,30 @@ func printAverageDetectorTime(e *engine.Engine) {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if the commit is valid
|
||||
func isValidCommit(uri, commit string) bool {
|
||||
// handle file:// urls
|
||||
repoPath, _ := strings.CutPrefix(uri, "file://") // remove the prefix to validate against the repo path
|
||||
output, err := exec.Command("git", "-C", repoPath, "cat-file", "-t", commit).Output()
|
||||
if err != nil {
|
||||
return false
|
||||
// validateClonePath ensures that --clone-path, if provided, exists and is a directory.
|
||||
// It also verifies that --no-cleanup is only allowed when --clone-path is set.
|
||||
// Note: without a custom clone path, repositories are cloned into temporary directories, which should never be retained.
|
||||
func validateClonePath(clonePath string, noCleanup bool) error {
|
||||
if noCleanup && clonePath == "" {
|
||||
return fmt.Errorf("invalid configuration: --no-cleanup can only be used together with --clone-path")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(output)) == "commit"
|
||||
if clonePath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
info, err := os.Stat(clonePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("path provided to --clone-path: %q does not exist", clonePath)
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to access --clone-path %q: %w", clonePath, err)
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return fmt.Errorf("path provided to --clone-path: %q is not a directory", clonePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ type (
|
||||
Binding struct {
|
||||
Resource Resource
|
||||
Permission Permission
|
||||
Condition string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func secretInfoToAnalyzerResult(info *SecretInfo) *analyzers.AnalyzerResult {
|
||||
|
||||
result := analyzers.AnalyzerResult{}
|
||||
|
||||
// resoures/permission setup
|
||||
// resources/permission setup
|
||||
permissions := allPermissions()
|
||||
userResource := analyzers.Resource{
|
||||
Name: info.Data.Name,
|
||||
|
||||
@@ -416,7 +416,7 @@ func AnalyzePermissions(cfg *config.Config, key string) (*SecretInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AnalyzePermissions prints the permissions of a HuggingFace API key
|
||||
// AnalyzeAndPrintPermissions prints the permissions of a HuggingFace API key
|
||||
func AnalyzeAndPrintPermissions(cfg *config.Config, key string) {
|
||||
info, err := AnalyzePermissions(cfg, key)
|
||||
if err != nil {
|
||||
|
||||
@@ -78,7 +78,7 @@ type Resource struct {
|
||||
MetaData map[string]string
|
||||
}
|
||||
|
||||
// appendResources append resource to secret info resources list
|
||||
// appendResource append resource to secret info resources list
|
||||
func (s *SecretInfo) appendResource(resource Resource) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
@@ -38,7 +38,7 @@ var (
|
||||
webhooksKey: "/v2/webhooks",
|
||||
/*
|
||||
TODO:
|
||||
release piplelines: https://launchdarkly.com/docs/api/release-pipelines-beta/get-all-release-pipelines (Beta)
|
||||
release pipelines: https://launchdarkly.com/docs/api/release-pipelines-beta/get-all-release-pipelines (Beta)
|
||||
insight deployments: https://launchdarkly.com/docs/api/insights-deployments-beta/get-deployments (Beta)
|
||||
delivery configuration: https://launchdarkly.com/docs/api/integration-delivery-configurations-beta/get-integration-delivery-configuration-by-environment (Beta)
|
||||
metrics: https://launchdarkly.com/docs/api/metrics-beta/get-metric-groups (Beta)
|
||||
|
||||
@@ -269,7 +269,7 @@ func AnalyzeAndPrintPermissions(cfg *config.Config, key string) {
|
||||
}
|
||||
|
||||
if info.organization == nil {
|
||||
color.Yellow("\n[i] No permissions were verified for this key because the key does not have one of the necessary permissions (user:read or organization:read) required to verifiy other permissions.")
|
||||
color.Yellow("\n[i] No permissions were verified for this key because the key does not have one of the necessary permissions (user:read or organization:read) required to verify other permissions.")
|
||||
}
|
||||
|
||||
if info.orgPermissions != nil {
|
||||
|
||||
@@ -117,3 +117,29 @@ func CleanTempArtifacts(ctx logContext.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanTempDirsForLegacyJSON removes all directories that start with "trufflehog-"
|
||||
// from either the provided clonePath (if not empty) or the OS temp directory.
|
||||
func CleanTempDirsForLegacyJSON(baseDir string) error {
|
||||
// If no custom clone path was provided, clean repos from the OS temp directory
|
||||
// since that's where they were cloned during the scan.
|
||||
if baseDir == "" {
|
||||
baseDir = os.TempDir()
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(baseDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && strings.HasPrefix(entry.Name(), "trufflehog-") {
|
||||
fullPath := filepath.Join(baseDir, entry.Name())
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,3 +28,35 @@ func TestExecName(t *testing.T) {
|
||||
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestCleanTempDirsForLegacyJSON(t *testing.T) {
|
||||
baseDir := t.TempDir()
|
||||
|
||||
// Create dirs that should be deleted
|
||||
dir1 := filepath.Join(baseDir, "trufflehog-123")
|
||||
dir2 := filepath.Join(baseDir, "trufflehog-456")
|
||||
assert.NoError(t, os.Mkdir(dir1, 0o755))
|
||||
assert.NoError(t, os.Mkdir(dir2, 0o755))
|
||||
|
||||
// Create dirs that should NOT be deleted
|
||||
keepDir := filepath.Join(baseDir, "keepme-123")
|
||||
assert.NoError(t, os.Mkdir(keepDir, 0o755))
|
||||
|
||||
// Create a file with trufflehog- prefix (should not be deleted because only dirs are deleted)
|
||||
keepFile := filepath.Join(baseDir, "trufflehog-file")
|
||||
assert.NoError(t, os.WriteFile(keepFile, []byte("data"), 0o644))
|
||||
|
||||
err := CleanTempDirsForLegacyJSON(baseDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = os.Stat(dir1)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
_, err = os.Stat(dir2)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
_, err = os.Stat(keepDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = os.Stat(keepFile)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
_ "github.com/tailscale/depaware/depaware"
|
||||
)
|
||||
+41
-2
@@ -108,6 +108,45 @@ func NewCustomTransport(T http.RoundTripper) *CustomTransport {
|
||||
return &CustomTransport{T}
|
||||
}
|
||||
|
||||
type InstrumentedTransport struct {
|
||||
T http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *InstrumentedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
|
||||
sanitizedURL := sanitizeURL(req.URL.String())
|
||||
|
||||
// increment counter for the URL
|
||||
recordHTTPRequest(sanitizedURL)
|
||||
|
||||
// Record start time for latency measurement
|
||||
start := time.Now()
|
||||
|
||||
resp, err := t.T.RoundTrip(req)
|
||||
|
||||
// Time the latency
|
||||
duration := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
recordNetworkError(sanitizedURL)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp != nil {
|
||||
// record latency and increment counter for non-200 status code
|
||||
recordHTTPResponse(sanitizedURL, resp.StatusCode, duration.Seconds())
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func NewInstrumentedTransport(T http.RoundTripper) *InstrumentedTransport {
|
||||
if T == nil {
|
||||
T = http.DefaultTransport
|
||||
}
|
||||
return &InstrumentedTransport{T}
|
||||
}
|
||||
|
||||
func ConstantResponseHttpClient(statusCode int, body string) *http.Client {
|
||||
return &http.Client{
|
||||
Timeout: DefaultResponseTimeout,
|
||||
@@ -223,7 +262,7 @@ var saneTransport = &http.Transport{
|
||||
func SaneHttpClient() *http.Client {
|
||||
httpClient := &http.Client{}
|
||||
httpClient.Timeout = DefaultResponseTimeout
|
||||
httpClient.Transport = NewCustomTransport(saneTransport)
|
||||
httpClient.Transport = NewInstrumentedTransport(NewCustomTransport(saneTransport))
|
||||
return httpClient
|
||||
}
|
||||
|
||||
@@ -231,6 +270,6 @@ func SaneHttpClient() *http.Client {
|
||||
func SaneHttpClientTimeOut(timeout time.Duration) *http.Client {
|
||||
httpClient := &http.Client{}
|
||||
httpClient.Timeout = timeout
|
||||
httpClient.Transport = NewCustomTransport(nil)
|
||||
httpClient.Transport = NewInstrumentedTransport(NewCustomTransport(nil))
|
||||
return httpClient
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
httpRequestsTotal = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: MetricsNamespace,
|
||||
Subsystem: "http_client",
|
||||
Name: "requests_total",
|
||||
Help: "Total number of HTTP requests made, labeled by URL.",
|
||||
},
|
||||
[]string{"url"},
|
||||
)
|
||||
|
||||
httpRequestDuration = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: MetricsNamespace,
|
||||
Subsystem: "http_client",
|
||||
Name: "request_duration_seconds",
|
||||
Help: "HTTP request latency in seconds, labeled by URL.",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
[]string{"url"},
|
||||
)
|
||||
|
||||
httpNon200ResponsesTotal = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: MetricsNamespace,
|
||||
Subsystem: "http_client",
|
||||
Name: "non_200_responses_total",
|
||||
Help: "Total number of non-200 HTTP responses, labeled by URL and status code.",
|
||||
},
|
||||
[]string{"url", "status_code"},
|
||||
)
|
||||
)
|
||||
|
||||
// sanitizeURL sanitizes a URL to avoid high cardinality metrics.
|
||||
// It keeps only the host and path, removing query parameters, fragments, and user info.
|
||||
func sanitizeURL(rawURL string) string {
|
||||
if rawURL == "" {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return "invalid_url"
|
||||
}
|
||||
|
||||
// Build sanitized URL with just scheme, host, and path
|
||||
sanitized := &url.URL{
|
||||
Scheme: parsedURL.Scheme,
|
||||
Host: parsedURL.Host,
|
||||
Path: parsedURL.Path,
|
||||
}
|
||||
|
||||
// If host is empty, try to extract from the raw URL
|
||||
if sanitized.Host == "" {
|
||||
// For relative URLs or malformed URLs, just use a placeholder
|
||||
return "relative_or_invalid"
|
||||
}
|
||||
|
||||
// Normalize path
|
||||
if sanitized.Path == "" {
|
||||
sanitized.Path = "/"
|
||||
}
|
||||
|
||||
// Limit path length to avoid extremely long paths creating high cardinality
|
||||
if len(sanitized.Path) > 100 {
|
||||
sanitized.Path = sanitized.Path[:100] + "..."
|
||||
}
|
||||
|
||||
result := sanitized.String()
|
||||
|
||||
// Final fallback to avoid empty strings
|
||||
if result == "" {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// recordHTTPRequest records metrics for an HTTP request.
|
||||
func recordHTTPRequest(sanitizedURL string) {
|
||||
httpRequestsTotal.WithLabelValues(sanitizedURL).Inc()
|
||||
}
|
||||
|
||||
// recordHTTPResponse records metrics for an HTTP response.
|
||||
func recordHTTPResponse(sanitizedURL string, statusCode int, durationSeconds float64) {
|
||||
// Record latency
|
||||
httpRequestDuration.WithLabelValues(sanitizedURL).Observe(durationSeconds)
|
||||
|
||||
// Record non-200 responses
|
||||
if statusCode != 200 {
|
||||
httpNon200ResponsesTotal.WithLabelValues(sanitizedURL, strconv.Itoa(statusCode)).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// recordNetworkError records metrics for failed HTTP response
|
||||
func recordNetworkError(sanitizedURL string) {
|
||||
httpNon200ResponsesTotal.WithLabelValues(sanitizedURL, "network_error").Inc()
|
||||
}
|
||||
@@ -6,11 +6,14 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRetryableHTTPClientCheckRetry(t *testing.T) {
|
||||
@@ -269,3 +272,170 @@ func TestRetryableHTTPClientTimeout(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeURL(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "valid https URL",
|
||||
input: "https://api.example.com/v1/users",
|
||||
expected: "https://api.example.com/v1/users",
|
||||
},
|
||||
{
|
||||
name: "URL with query parameters",
|
||||
input: "https://api.example.com/search?q=secret&limit=10",
|
||||
expected: "https://api.example.com/search",
|
||||
},
|
||||
{
|
||||
name: "URL with fragment",
|
||||
input: "https://example.com/page#section",
|
||||
expected: "https://example.com/page",
|
||||
},
|
||||
{
|
||||
name: "URL with user info",
|
||||
input: "https://user:[email protected]/path",
|
||||
expected: "https://api.example.com/path",
|
||||
},
|
||||
{
|
||||
name: "empty URL",
|
||||
input: "",
|
||||
expected: "unknown",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
input: "not-a-url",
|
||||
expected: "relative_or_invalid",
|
||||
},
|
||||
{
|
||||
name: "very long path",
|
||||
input: "https://example.com/" + strings.Repeat("a", 150),
|
||||
expected: "https://example.com/" + strings.Repeat("a", 99) + "...", // 99 + 1 ("/") = 100 chars
|
||||
},
|
||||
{
|
||||
name: "root path",
|
||||
input: "https://example.com",
|
||||
expected: "https://example.com/",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := sanitizeURL(tc.input)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaneHttpClientMetrics(t *testing.T) {
|
||||
// Create a test server that returns different status codes
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/success":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
case "/error":
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte("error"))
|
||||
case "/notfound":
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte("not found"))
|
||||
default:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("default"))
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Create a SaneHttpClient
|
||||
client := SaneHttpClient()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
path string
|
||||
expectedStatusCode int
|
||||
expectsNon200 bool
|
||||
}{
|
||||
{
|
||||
name: "successful request",
|
||||
path: "/success",
|
||||
expectedStatusCode: 200,
|
||||
expectsNon200: false,
|
||||
},
|
||||
{
|
||||
name: "server error request",
|
||||
path: "/error",
|
||||
expectedStatusCode: 500,
|
||||
expectsNon200: true,
|
||||
},
|
||||
{
|
||||
name: "not found request",
|
||||
path: "/notfound",
|
||||
expectedStatusCode: 404,
|
||||
expectsNon200: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var requestURL string
|
||||
if strings.HasPrefix(tc.path, "http") {
|
||||
requestURL = tc.path
|
||||
} else {
|
||||
requestURL = server.URL + tc.path
|
||||
}
|
||||
|
||||
// Get initial metric values
|
||||
sanitizedURL := sanitizeURL(requestURL)
|
||||
initialRequestsTotal := testutil.ToFloat64(httpRequestsTotal.WithLabelValues(sanitizedURL))
|
||||
|
||||
// Make the request
|
||||
resp, err := client.Get(requestURL)
|
||||
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||
|
||||
// Check that request counter was incremented
|
||||
requestsTotal := testutil.ToFloat64(httpRequestsTotal.WithLabelValues(sanitizedURL))
|
||||
assert.Equal(t, initialRequestsTotal+1, requestsTotal)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstrumentedTransport(t *testing.T) {
|
||||
// Create a mock transport that we can control
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("test response"))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Create instrumented transport
|
||||
transport := NewInstrumentedTransport(nil)
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
// Get initial metric value
|
||||
sanitizedURL := sanitizeURL(server.URL)
|
||||
initialCount := testutil.ToFloat64(httpRequestsTotal.WithLabelValues(sanitizedURL))
|
||||
|
||||
// Make a request
|
||||
resp, err := client.Get(server.URL)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Verify the request was successful
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
// Verify metrics were recorded
|
||||
finalCount := testutil.ToFloat64(httpRequestsTotal.WithLabelValues(sanitizedURL))
|
||||
assert.Equal(t, initialCount+1, finalCount)
|
||||
|
||||
// Note: Testing histogram metrics is complex due to the way Prometheus handles them
|
||||
// The main thing is that the request completed successfully and counters were incremented
|
||||
}
|
||||
|
||||
@@ -27,6 +27,29 @@ func Recover(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// RecoverWithHandler handles panics and reports to Sentry, then turns control
|
||||
// over to a provided function. This permits extra reporting in the same scope
|
||||
// without re-panicking, as recover() clears the state after it's called. Does
|
||||
// NOT block to flush sentry report.
|
||||
func RecoverWithHandler(ctx context.Context, callback func(error)) {
|
||||
if err := recover(); err != nil {
|
||||
panicStack := string(debug.Stack())
|
||||
if eventID := sentry.CurrentHub().Recover(err); eventID != nil {
|
||||
ctx.Logger().Info("panic captured", "event_id", *eventID)
|
||||
}
|
||||
ctx.Logger().Error(fmt.Errorf("panic"), panicStack,
|
||||
"recover", err,
|
||||
)
|
||||
|
||||
switch v := err.(type) {
|
||||
case error:
|
||||
callback(fmt.Errorf("panic: %w", v))
|
||||
default:
|
||||
callback(fmt.Errorf("panic: %v", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RecoverWithExit handles panics and reports to Sentry before exiting.
|
||||
func RecoverWithExit(ctx context.Context) {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
+20
-2
@@ -16,6 +16,13 @@ var (
|
||||
defaultLogger logr.Logger
|
||||
)
|
||||
|
||||
// logEntryKey is used to store a reference to the logger in the
|
||||
// context.Context key/value bag. This is used for regaining the logger in case
|
||||
// the context is converted into a different type.
|
||||
const logEntryKey logEntryKeyT = 0
|
||||
|
||||
type logEntryKeyT int
|
||||
|
||||
func init() {
|
||||
defaultLogger, _ = log.New("context", log.WithConsoleSink(os.Stderr))
|
||||
}
|
||||
@@ -132,12 +139,14 @@ func Cause(ctx context.Context) error {
|
||||
// the value added to the structured log values (if the key is a string).
|
||||
func WithValue(parent Context, key, val any) Context {
|
||||
logger := parent.Logger()
|
||||
parentCtx := context.WithValue(parent, key, val)
|
||||
if k, ok := key.(string); ok {
|
||||
logger = logger.WithValues(k, val)
|
||||
parentCtx = context.WithValue(parentCtx, logEntryKey, logger)
|
||||
}
|
||||
return logCtx{
|
||||
log: logger,
|
||||
Context: context.WithValue(parent, key, val),
|
||||
Context: parentCtx,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +164,7 @@ func WithValues(parent Context, keyAndVals ...any) Context {
|
||||
func WithLogger(parent context.Context, logger logr.Logger) Context {
|
||||
return logCtx{
|
||||
log: logger,
|
||||
Context: parent,
|
||||
Context: context.WithValue(parent, logEntryKey, logger),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,9 +172,18 @@ func WithLogger(parent context.Context, logger logr.Logger) Context {
|
||||
// is already a Context, that will be returned, otherwise a default logger will
|
||||
// be added.
|
||||
func AddLogger(parent context.Context) Context {
|
||||
// If the context.Context is already a Context, return that.
|
||||
if loggerCtx, ok := parent.(Context); ok {
|
||||
return loggerCtx
|
||||
}
|
||||
// If the logger exists in the grab bag (and is the correct type),
|
||||
// return that.
|
||||
if logEntryVal := parent.Value(logEntryKey); logEntryVal != nil {
|
||||
if logger, ok := logEntryVal.(logr.Logger); ok {
|
||||
return WithLogger(parent, logger)
|
||||
}
|
||||
}
|
||||
// Otherwise, add the default logger.
|
||||
return WithLogger(parent, defaultLogger)
|
||||
}
|
||||
|
||||
|
||||
@@ -185,3 +185,34 @@ func TestCause(t *testing.T) {
|
||||
cancel(err)
|
||||
assert.Equal(t, err, Cause(ctx))
|
||||
}
|
||||
|
||||
// TestBuriedLogger tests when a logging context is wrapped by a non-logging
|
||||
// implementation that we can still regain the original logger.
|
||||
func TestBuriedLogger(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger, sync := log.New("test",
|
||||
log.WithConsoleSink(&buffer),
|
||||
)
|
||||
defer func(prevLogger logr.Logger) {
|
||||
defaultLogger = prevLogger
|
||||
}(defaultLogger)
|
||||
SetDefaultLogger(logger)
|
||||
|
||||
// Create a context with a key/value log entry.
|
||||
ctx := WithValue(Background(), "log", "entry")
|
||||
// Convert it to a stdlib context.
|
||||
stdCtx := context.WithValue(ctx, "std", "entry") //nolint:staticcheck
|
||||
// Try to get the logger back again.
|
||||
ctx = AddLogger(stdCtx)
|
||||
ctx.Logger().Info("test")
|
||||
|
||||
assert.Nil(t, sync())
|
||||
logs := strings.Split(strings.TrimSpace(buffer.String()), "\n")
|
||||
|
||||
// Logger has the key/value log entry.
|
||||
assert.Equal(t, 1, len(logs))
|
||||
assert.Contains(t, logs[0], `{"log": "entry"}`)
|
||||
// Grab bag still has both values.
|
||||
assert.Equal(t, "entry", ctx.Value("log"))
|
||||
assert.Equal(t, "entry", ctx.Value("std"))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# TruffleHog Custom Detector Setup Guide
|
||||
|
||||
This guide will walk you through setting up a custom detector in TruffleHog to identify specific patterns unique to your project.
|
||||
This guide will walk you through setting up a custom detector in TruffleHog to identify specific patterns unique to your project. For users of Trufflehog Enterprise, this guide applies to that environment as well.
|
||||
|
||||
## Steps to Set Up a Custom Detector
|
||||
|
||||
@@ -40,9 +40,16 @@ This guide will walk you through setting up a custom detector in TruffleHog to i
|
||||
**Other allowed parameters:**
|
||||
- **`primary_regex_name`**: This parameter allows you designate the primary regex pattern when multiple regex patterns are defined in the regex section. If a match is found, the match for the designated primary regex will be used to determine the line number. The value must be one of the names specified in the regex section.
|
||||
- **`exclude_regexes_capture`**: This parameter allows you to define regex patterns to exclude specific parts of a detected secret. If a match is found within the detected secret, the portion matching this regex is excluded from the result.
|
||||
- **`exclude_regexes_match`**: This parameter enables you to define regex patterns to exclude entire matches from being reported as secrets.
|
||||
- **`exclude_regexes_match`**: This parameter enables you to define regex patterns to exclude entire matches from being reported as secrets. This applies to the entire matched string, not just the token.
|
||||
- **`entropy`**: This parameter is used to assess the randomness of detected strings. High entropy often indicates that a string is a potential secret, such as an API key or password, due to its complexity and unpredictability. It helps in filtering false-positives. While an entropy threshold of `3` can be a starting point, it's essential to adjust this value based on your project's specific requirements and the nature of the data you have.
|
||||
- **`exclude_words`**: This parameter allows you to specify a list of words that, if present in a detected string, will cause TruffleHog to ignore that string.
|
||||
- **`exclude_words`**: This parameter allows you to specify a list of words that, if present in a detected string, will cause TruffleHog to ignore that string. This is a substring match and does not enforce word boundaries. It applies only to the token.
|
||||
- **`validations`**: This parameter lets you define extra validation rules for each regex specified in the regex option. These rules address limitations of Go's RE2 engine, such as the lack of lookahead support, and are applied after a regex match to help reduce false positives.
|
||||
Available validation options:
|
||||
- **`contains_digit`**: Ensures the match contains at least one numeric digit (0-9). Useful for API keys or tokens that must include numbers.
|
||||
- **`contains_lowercase`**: Ensures the match contains at least one lowercase letter (a-z). Common requirement for passwords and mixed-case tokens.
|
||||
- **`contains_uppercase`**: Ensures the match contains at least one uppercase letter (A-Z). Helps validate tokens that follow mixed-case conventions.
|
||||
- **`contains_special_char`**: Ensures the match contains at least one special character from the set `!@#$%^&*()_+-=[]{}|;:,.<>?`. Useful for complex passwords or encoded tokens.
|
||||
|
||||
|
||||
[Here](/examples/generic_with_filters.yml) is an example of a custom detector using these parameters.
|
||||
|
||||
@@ -207,4 +214,4 @@ func main() {
|
||||
log.Fatalf("Server failed: %s", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -116,7 +118,7 @@ func (c *CustomRegexWebhook) FromData(ctx context.Context, verify bool, data []b
|
||||
|
||||
MatchLoop:
|
||||
for _, match := range matches {
|
||||
for _, values := range match {
|
||||
for key, values := range match {
|
||||
// attempt to use capture group
|
||||
secret := values[0]
|
||||
if len(values) > 1 {
|
||||
@@ -150,6 +152,26 @@ MatchLoop:
|
||||
continue MatchLoop
|
||||
}
|
||||
}
|
||||
|
||||
if validations := c.GetValidations(); validations != nil {
|
||||
validationRules := []struct {
|
||||
enabled bool
|
||||
validator func(string) bool
|
||||
}{
|
||||
{validations[key].GetContainsDigit(), ContainsDigit},
|
||||
{validations[key].GetContainsLowercase(), ContainsLowercase},
|
||||
{validations[key].GetContainsUppercase(), ContainsUppercase},
|
||||
{validations[key].GetContainsSpecialChar(), ContainsSpecialChar},
|
||||
}
|
||||
|
||||
for _, rule := range validationRules {
|
||||
if rule.enabled && !rule.validator(secret) {
|
||||
// skip this match if a validation rule is enabled but missing from the secret
|
||||
continue MatchLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
g.Go(func() error {
|
||||
@@ -194,7 +216,8 @@ func (c *CustomRegexWebhook) createResults(ctx context.Context, match map[string
|
||||
}
|
||||
|
||||
var raw string
|
||||
for key, values := range match {
|
||||
for _, key := range slices.Sorted(maps.Keys(match)) {
|
||||
values := match[key]
|
||||
// values[0] contains the entire regex match.
|
||||
secret := values[0]
|
||||
if len(values) > 1 {
|
||||
|
||||
@@ -4,9 +4,13 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/protoyaml"
|
||||
)
|
||||
|
||||
@@ -227,6 +231,334 @@ func TestDetectorPrimarySecret(t *testing.T) {
|
||||
assert.Equal(t, "secret_YI7C90ACY1_yy", results[0].GetPrimarySecretValue())
|
||||
}
|
||||
|
||||
func TestDetectorValidations(t *testing.T) {
|
||||
type args struct {
|
||||
CustomRegex *custom_detectorspb.CustomRegex
|
||||
Data string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input args
|
||||
want []detectors.Result
|
||||
}{
|
||||
{
|
||||
name: "custom validation - contains digit",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsDigit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStr0ngP@ssword!
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("MyStr0ngP@ssword!"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom validation - does not contains digit",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsDigit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongPassword!
|
||||
End of file`,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "custom validation - contains lowercase",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsLowercase: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongPassword!
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("MyStrongPassword!"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom validation - does not contains lowercase",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsLowercase: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MYSTRONGPASSWORD!
|
||||
End of file`,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "custom validation - contains uppercase",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsUppercase: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongPassword!
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("MyStrongPassword!"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom validation - does not contains uppercase",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsUppercase: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: mystrongpassword!
|
||||
End of file`,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "custom validation - contains special character",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsSpecialChar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStr@ngP@ssword!
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("MyStr@ngP@ssword!"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom validation - does not contains special character",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsSpecialChar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongPassword
|
||||
End of file`,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "custom validation - contains uppercase and special characters",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsUppercase: true,
|
||||
ContainsSpecialChar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongP@ssword
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("MyStrongP@ssword"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom validation - contains uppercase but does not contain special characters",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsUppercase: true,
|
||||
ContainsSpecialChar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongPassword
|
||||
End of file`,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "custom validation - wrong regex name in validations",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password"},
|
||||
Regex: map[string]string{"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"wrong": {
|
||||
ContainsUppercase: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: mystrongp@ssword
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("mystrongp@ssword"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom validation - multiple regex validations",
|
||||
input: args{
|
||||
CustomRegex: &custom_detectorspb.CustomRegex{
|
||||
Name: "test",
|
||||
Keywords: []string{"password", "api_key"},
|
||||
Regex: map[string]string{
|
||||
"password": `([A-Za-z0-9!@#$%^&*()_+=\-]{12,})`,
|
||||
"api_key": `([a-f0-9_-]{32})`,
|
||||
},
|
||||
Validations: map[string]*custom_detectorspb.ValidationConfig{
|
||||
"password": {
|
||||
ContainsUppercase: true,
|
||||
ContainsSpecialChar: true,
|
||||
},
|
||||
"api_key": {
|
||||
ContainsSpecialChar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Data: `This is custom example
|
||||
This file has a random text and maybe a secret
|
||||
Password: MyStrongP@ssword
|
||||
API_Key: c392c9837d69b44c764cbf260b-e6184 // should be detected
|
||||
API_Key: c392c9837d69b44c764cbf260be6184 // should be filtered by validation
|
||||
End of file`,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: "test",
|
||||
Verified: false,
|
||||
Raw: []byte("c392c9837d69b44c764cbf260b-e6184MyStrongP@ssword"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
detector, err := NewWebhookCustomRegex(tt.input.CustomRegex)
|
||||
assert.NoError(t, err)
|
||||
results, err := detector.FromData(context.Background(), false, []byte(tt.input.Data))
|
||||
assert.NoError(t, err)
|
||||
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "ExtraData", "verificationError", "primarySecret")
|
||||
if diff := cmp.Diff(results, tt.want, ignoreOpts); diff != "" {
|
||||
t.Errorf("CustomDetector.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProductIndices(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = productIndices(3, 2, 6)
|
||||
|
||||
@@ -107,3 +107,47 @@ func ValidateRegexVars(regex map[string]string, body ...string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// === Custom Validations ===
|
||||
|
||||
// ContainsDigit checks if string contains at least one digit
|
||||
func ContainsDigit(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
char := s[i]
|
||||
if char >= '0' && char <= '9' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsLowercase checks if string contains at least one lowercase letter
|
||||
func ContainsLowercase(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
char := s[i]
|
||||
if char >= 'a' && char <= 'z' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsUppercase checks if string contains at least one uppercase letter
|
||||
func ContainsUppercase(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
char := s[i]
|
||||
if char >= 'A' && char <= 'Z' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsSpecialChar checks if string contains at least one special character
|
||||
func ContainsSpecialChar(s string) bool {
|
||||
specialChars := "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
return strings.ContainsAny(s, specialChars)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package custom_detectors
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCustomDetectorsKeywordValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -232,3 +234,119 @@ func TestCustomDetectorsVerifyRegexVarsValidation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsDigit(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "contains digit",
|
||||
args: args{s: "lzscqf&60M"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "does not contains digit",
|
||||
args: args{s: "ZlDQOdaM*vsT"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ContainsDigit(tt.args.s); got != tt.want {
|
||||
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsLowercase(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "contains lower case",
|
||||
args: args{s: "g0AJBHdnhRG2"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "does not contains lower case",
|
||||
args: args{s: "V7T#MEA6@+TN"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ContainsLowercase(tt.args.s); got != tt.want {
|
||||
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsUppercase(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "contains upper case",
|
||||
args: args{s: "G1sKkJeKlSQf"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "does not contains upper case",
|
||||
args: args{s: "pq6-14ydz1@d"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ContainsUppercase(tt.args.s); got != tt.want {
|
||||
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsSpecialChar(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "contains upper case",
|
||||
args: args{s: "HP$gE7s=do0B"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "does not contains upper case",
|
||||
args: args{s: "w9gvBYctrSjB"},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ContainsSpecialChar(tt.args.s); got != tt.want {
|
||||
t.Errorf("ContainsDigit() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ func BenchmarkFromChunkSmall(b *testing.B) {
|
||||
d := Base64{}
|
||||
data := detectors.MustGetBenchmarkData()["small"]
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for b.Loop() {
|
||||
d.FromChunk(&sources.Chunk{Data: data})
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func BenchmarkFromChunkMedium(b *testing.B) {
|
||||
d := Base64{}
|
||||
data := detectors.MustGetBenchmarkData()["medium"]
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for b.Loop() {
|
||||
d.FromChunk(&sources.Chunk{Data: data})
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,7 @@ func BenchmarkFromChunkLarge(b *testing.B) {
|
||||
d := Base64{}
|
||||
data := detectors.MustGetBenchmarkData()["big"]
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for b.Loop() {
|
||||
d.FromChunk(&sources.Chunk{Data: data})
|
||||
}
|
||||
}
|
||||
|
||||
+167
-33
@@ -23,6 +23,33 @@ var (
|
||||
|
||||
// Common escape sequence used in programming languages.
|
||||
escapePat = regexp.MustCompile(`(?i:\\{1,2}u)([a-fA-F0-9]{4})`)
|
||||
|
||||
// Additional Unicode escape formats from dencode.com
|
||||
|
||||
// \u{X} format - Rust, Swift, some JS, etc. (variable length hex in braces)
|
||||
braceEscapePat = regexp.MustCompile(`\\u\{([a-fA-F0-9]{1,6})\}`)
|
||||
|
||||
// \U00XXXXXX format - Python, etc. (8-digit format for non-BMP characters)
|
||||
longEscapePat = regexp.MustCompile(`\\U([a-fA-F0-9]{8})`)
|
||||
|
||||
// \x{X} format - Perl (variable length hex in braces)
|
||||
perlEscapePat = regexp.MustCompile(`\\x\{([a-fA-F0-9]{1,6})\}`)
|
||||
|
||||
// \X format - CSS (hex without padding). Go's regexp (RE2) has no look-ahead, so we
|
||||
// include the delimiter (whitespace, another backslash, or end-of-string) in the
|
||||
// match using a non-capturing group. The delimiter is later re-inserted by the
|
||||
// decoder when necessary.
|
||||
cssEscapePat = regexp.MustCompile(`\\([a-fA-F0-9]{1,6})(?:\s|\\|$)`)
|
||||
|
||||
// &#xX; format - HTML/XML (hex with semicolon)
|
||||
htmlEscapePat = regexp.MustCompile(`&#x([a-fA-F0-9]{1,6});`)
|
||||
|
||||
// %uXXXX format - Percent-encoding (non-standard)
|
||||
percentEscapePat = regexp.MustCompile(`%u([a-fA-F0-9]{4})`)
|
||||
|
||||
// // 0xX format - Hexadecimal notation with space separation
|
||||
// Note: Commenting out for now due to high memory overhead. Review ways to handle this.
|
||||
// hexEscapePat = regexp.MustCompile(`0x([a-fA-F0-9]{1,6})(?:\s|$)`)
|
||||
)
|
||||
|
||||
func (d *EscapedUnicode) Type() detectorspb.DecoderType {
|
||||
@@ -39,13 +66,38 @@ func (d *EscapedUnicode) FromChunk(chunk *sources.Chunk) *DecodableChunk {
|
||||
chunkData = bytes.Clone(chunk.Data)
|
||||
matched = false
|
||||
)
|
||||
if codePointPat.Match(chunkData) {
|
||||
|
||||
// Process patterns in priority order - more specific patterns first
|
||||
// This prevents conflicts where multiple patterns match the same input
|
||||
|
||||
// Long escape format (8 hex digits) - highest priority
|
||||
if longEscapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodeCodePoint(chunkData)
|
||||
}
|
||||
if escapePat.Match(chunkData) {
|
||||
chunkData = decodeLongEscape(chunkData)
|
||||
} else if braceEscapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodeBraceEscape(chunkData)
|
||||
} else if perlEscapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodePerlEscape(chunkData)
|
||||
} else if htmlEscapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodeHtmlEscape(chunkData)
|
||||
} else if percentEscapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodePercentEscape(chunkData)
|
||||
} else if escapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodeEscaped(chunkData)
|
||||
} else if codePointPat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodeCodePoint(chunkData)
|
||||
} else if cssEscapePat.Match(chunkData) {
|
||||
matched = true
|
||||
chunkData = decodeCssEscape(chunkData)
|
||||
// } else if hexEscapePat.Match(chunkData) {
|
||||
// matched = true
|
||||
// chunkData = decodeHexEscape(chunkData)
|
||||
}
|
||||
|
||||
if matched {
|
||||
@@ -71,6 +123,34 @@ func (d *EscapedUnicode) FromChunk(chunk *sources.Chunk) *DecodableChunk {
|
||||
const maxBytesPerRune = 4
|
||||
const spaceChar = byte(' ')
|
||||
|
||||
// decodeWithPattern replaces escape sequences matched by re with their UTF-8
|
||||
// equivalents. The regex *must* have the first capturing group contain the
|
||||
// hexadecimal code-point digits. Any invalid value (> 0x10FFFF or parse error)
|
||||
// is skipped. The replacement walks matches in reverse order to avoid index
|
||||
// shifts.
|
||||
func decodeWithPattern(input []byte, re *regexp.Regexp) []byte {
|
||||
indices := re.FindAllSubmatchIndex(input, -1)
|
||||
if len(indices) == 0 {
|
||||
return input
|
||||
}
|
||||
|
||||
utf8Bytes := make([]byte, maxBytesPerRune)
|
||||
for i := len(indices) - 1; i >= 0; i-- {
|
||||
m := indices[i]
|
||||
start, end := m[0], m[1]
|
||||
hexStart, hexEnd := m[2], m[3]
|
||||
|
||||
cp, err := strconv.ParseUint(string(input[hexStart:hexEnd]), 16, 32)
|
||||
if err != nil || cp > 0x10FFFF {
|
||||
continue
|
||||
}
|
||||
|
||||
utf8Len := utf8.EncodeRune(utf8Bytes, rune(cp))
|
||||
input = append(input[:start], append(utf8Bytes[:utf8Len], input[end:]...)...)
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
func decodeCodePoint(input []byte) []byte {
|
||||
// Find all Unicode escape sequences in the input byte slice
|
||||
indices := codePointPat.FindAllSubmatchIndex(input, -1)
|
||||
@@ -112,33 +192,87 @@ func decodeCodePoint(input []byte) []byte {
|
||||
}
|
||||
|
||||
func decodeEscaped(input []byte) []byte {
|
||||
// Find all Unicode escape sequences in the input byte slice
|
||||
indices := escapePat.FindAllSubmatchIndex(input, -1)
|
||||
|
||||
// Iterate over found indices in reverse order to avoid modifying the slice length
|
||||
utf8Bytes := make([]byte, maxBytesPerRune)
|
||||
for i := len(indices) - 1; i >= 0; i-- {
|
||||
matches := indices[i]
|
||||
startIndex := matches[0]
|
||||
hexStartIndex := matches[2]
|
||||
endIndex := matches[3]
|
||||
|
||||
// Extract the hexadecimal value from the escape sequence
|
||||
hexValue := string(input[hexStartIndex:endIndex])
|
||||
|
||||
// Parse the hexadecimal value to an integer
|
||||
unicodeInt, err := strconv.ParseInt(hexValue, 16, 32)
|
||||
if err != nil {
|
||||
// If there's an error, continue to the next escape sequence
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert the Unicode code point to a UTF-8 representation
|
||||
utf8Len := utf8.EncodeRune(utf8Bytes, rune(unicodeInt))
|
||||
|
||||
// Replace the escape sequence with the UTF-8 representation
|
||||
input = append(input[:startIndex], append(utf8Bytes[:utf8Len], input[endIndex:]...)...)
|
||||
}
|
||||
|
||||
return input
|
||||
return decodeWithPattern(input, escapePat)
|
||||
}
|
||||
|
||||
// decodeBraceEscape handles \u{X} format - Rust, Swift, some JS, etc.
|
||||
func decodeBraceEscape(input []byte) []byte {
|
||||
return decodeWithPattern(input, braceEscapePat)
|
||||
}
|
||||
|
||||
// decodeLongEscape handles \U00XXXXXX format - Python, etc.
|
||||
func decodeLongEscape(input []byte) []byte {
|
||||
return decodeWithPattern(input, longEscapePat)
|
||||
}
|
||||
|
||||
// decodePerlEscape handles \x{X} format - Perl
|
||||
func decodePerlEscape(input []byte) []byte {
|
||||
return decodeWithPattern(input, perlEscapePat)
|
||||
}
|
||||
|
||||
// decodeCssEscape handles \X format - CSS (hex without padding, with space delimiter or end of string or next hex sequence)
|
||||
func decodeCssEscape(input []byte) []byte {
|
||||
return decodeWithPattern(input, cssEscapePat)
|
||||
}
|
||||
|
||||
// decodeHtmlEscape handles &#xX; format - HTML/XML
|
||||
func decodeHtmlEscape(input []byte) []byte {
|
||||
return decodeWithPattern(input, htmlEscapePat)
|
||||
}
|
||||
|
||||
// decodePercentEscape handles %uXXXX format - Percent-encoding (non-standard)
|
||||
func decodePercentEscape(input []byte) []byte {
|
||||
return decodeWithPattern(input, percentEscapePat)
|
||||
}
|
||||
|
||||
// decodeHexEscape handles 0xX format - Hexadecimal notation with space separation
|
||||
// func decodeHexEscape(input []byte) []byte {
|
||||
// // This format requires consecutive 0xNN sequences to be considered for decoding
|
||||
// // We'll look for patterns of multiple consecutive hex values
|
||||
// hexPattern := regexp.MustCompile(`(?:0x[a-fA-F0-9]{1,2}(?:\s+|$))+`)
|
||||
|
||||
// matches := hexPattern.FindAll(input, -1)
|
||||
// if len(matches) == 0 {
|
||||
// return input
|
||||
// }
|
||||
|
||||
// result := input
|
||||
// for _, match := range matches {
|
||||
// // Extract individual hex values
|
||||
// individualHex := regexp.MustCompile(`0x([a-fA-F0-9]{1,2})`)
|
||||
// hexMatches := individualHex.FindAllSubmatch(match, -1)
|
||||
|
||||
// // Only decode if we have multiple consecutive hex values (likely to be a Unicode string)
|
||||
// if len(hexMatches) < 3 {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// var decoded []byte
|
||||
// for _, hexMatch := range hexMatches {
|
||||
// hexValue := string(hexMatch[1])
|
||||
// if len(hexValue) == 1 {
|
||||
// hexValue = "0" + hexValue // Pad single digit hex values
|
||||
// }
|
||||
|
||||
// unicodeInt, err := strconv.ParseUint(hexValue, 16, 32)
|
||||
// if err != nil || unicodeInt > 0x10FFFF {
|
||||
// break
|
||||
// }
|
||||
|
||||
// if unicodeInt <= 0x7F {
|
||||
// // ASCII character
|
||||
// decoded = append(decoded, byte(unicodeInt))
|
||||
// } else {
|
||||
// // Unicode character
|
||||
// utf8Bytes := make([]byte, maxBytesPerRune)
|
||||
// utf8Len := utf8.EncodeRune(utf8Bytes, rune(unicodeInt))
|
||||
// decoded = append(decoded, utf8Bytes[:utf8Len]...)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Replace the original sequence with decoded bytes
|
||||
// result = bytes.Replace(result, match, decoded, 1)
|
||||
// }
|
||||
|
||||
// return result
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
package decoders
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
// Benchmark data for testing
|
||||
var (
|
||||
// Original formats
|
||||
originalUnicodeData = []byte("\\u0041\\u004b\\u0049\\u0041\\u0055\\u004d\\u0034\\u0047\\u0036\\u004f\\u0036\\u004e\\u0041\\u004b\\u0045\\u0037\\u004c\\u0043\\u0044\\u004a")
|
||||
codePointData = []byte("U+0041 U+004B U+0049 U+0041 U+0055 U+004D U+0034 U+0047 U+0036 U+004F U+0036 U+004E U+0041 U+004B U+0045 U+0037 U+004C U+0043 U+0044 U+004A")
|
||||
|
||||
// New formats
|
||||
braceEscapeData = []byte("\\u{41}\\u{4b}\\u{49}\\u{41}\\u{55}\\u{4d}\\u{34}\\u{47}\\u{36}\\u{4f}\\u{36}\\u{4e}\\u{41}\\u{4b}\\u{45}\\u{37}\\u{4c}\\u{43}\\u{44}\\u{4a}")
|
||||
longEscapeData = []byte("\\U00000041\\U0000004b\\U00000049\\U00000041\\U00000055\\U0000004d\\U00000034\\U00000047\\U00000036\\U0000004f\\U00000036\\U0000004e\\U00000041\\U0000004b\\U00000045\\U00000037\\U0000004c\\U00000043\\U00000044\\U0000004a")
|
||||
perlEscapeData = []byte("\\x{41}\\x{4b}\\x{49}\\x{41}\\x{55}\\x{4d}\\x{34}\\x{47}\\x{36}\\x{4f}\\x{36}\\x{4e}\\x{41}\\x{4b}\\x{45}\\x{37}\\x{4c}\\x{43}\\x{44}\\x{4a}")
|
||||
cssEscapeData = []byte("\\41 \\4b \\49 \\41 \\55 \\4d \\34 \\47 \\36 \\4f \\36 \\4e \\41 \\4b \\45 \\37 \\4c \\43 \\44 \\4a ")
|
||||
htmlEscapeData = []byte("AKIAUM4G6O6NAKE7LCDJ")
|
||||
percentEscapeData = []byte("%u0041%u004b%u0049%u0041%u0055%u004d%u0034%u0047%u0036%u004f%u0036%u004e%u0041%u004b%u0045%u0037%u004c%u0043%u0044%u004a")
|
||||
//hexEscapeData = []byte("0x41 0x4b 0x49 0x41 0x55 0x4d 0x34 0x47 0x36 0x4f 0x36 0x4e 0x41 0x4b 0x45 0x37 0x4c 0x43 0x44 0x4a ")
|
||||
|
||||
// Mixed content (more realistic scenario)
|
||||
mixedContentData = []byte(`
|
||||
const config = {
|
||||
apiKey: "\\u0041\\u004b\\u0049\\u0041\\u0055\\u004d\\u0034\\u0047\\u0036\\u004f\\u0036\\u004e\\u0041\\u004b\\u0045\\u0037\\u004c\\u0043\\u0044\\u004a",
|
||||
secretKey: "\\u{6e}\\u{62}\\u{75}\\u{68}\\u{7a}\\u{4b}\\u{79}\\u{39}\\u{50}\\u{50}\\u{7a}\\u{32}\\u{7a}\\u{47}\\u{33}\\u{47}\\u{54}\\u{4a}\\u{71}\\u{4b}\\u{45}\\u{43}\\u{6e}\\u{71}\\u{4c}\\u{41}\\u{78}\\u{43}\\u{76}\\u{2f}\\u{36}\\u{68}\\u{43}\\u{6a}\\u{6b}\\u{50}\\u{68}\\u{66}\\u{58}\\u{6f}",
|
||||
htmlToken: "AKIAUM4G6O6NAKE7LCDJ",
|
||||
normalText: "This is normal text that should not be processed"
|
||||
}
|
||||
`)
|
||||
|
||||
// Large data for stress testing
|
||||
largeData = func() []byte {
|
||||
data := make([]byte, 0, 10000)
|
||||
for i := 0; i < 100; i++ {
|
||||
data = append(data, originalUnicodeData...)
|
||||
data = append(data, braceEscapeData...)
|
||||
data = append(data, longEscapeData...)
|
||||
data = append(data, htmlEscapeData...)
|
||||
data = append(data, []byte(" normal text ")...)
|
||||
}
|
||||
return data
|
||||
}()
|
||||
|
||||
// No Unicode data (worst case for performance)
|
||||
noUnicodeData = []byte(`
|
||||
This is a large block of text with no Unicode escape sequences.
|
||||
It contains various programming constructs like:
|
||||
- Variable declarations: var x = 123;
|
||||
- Function calls: doSomething(param1, param2);
|
||||
- Comments: /* this is a comment */
|
||||
- Strings: "hello world"
|
||||
- Numbers: 42, 3.14159, 0xFF
|
||||
- But no Unicode escapes that would trigger our decoders.
|
||||
This simulates the common case where files don't contain Unicode escapes.
|
||||
`)
|
||||
)
|
||||
|
||||
// Benchmark individual decoder functions
|
||||
func BenchmarkDecodeOriginalEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodeEscaped(originalUnicodeData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeCodePoint(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodeCodePoint(codePointData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeBraceEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodeBraceEscape(braceEscapeData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeLongEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodeLongEscape(longEscapeData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodePerlEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodePerlEscape(perlEscapeData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeCssEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodeCssEscape(cssEscapeData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeHtmlEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodeHtmlEscape(htmlEscapeData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodePercentEscape(b *testing.B) {
|
||||
for b.Loop() {
|
||||
_ = decodePercentEscape(percentEscapeData)
|
||||
}
|
||||
}
|
||||
|
||||
// func BenchmarkDecodeHexEscape(b *testing.B) {
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// _ = decodeHexEscape(hexEscapeData)
|
||||
// }
|
||||
// }
|
||||
|
||||
// Benchmark the full FromChunk method with different data types
|
||||
func BenchmarkFromChunk_OriginalFormat(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: originalUnicodeData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromChunk_BraceFormat(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: braceEscapeData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromChunk_LongFormat(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: longEscapeData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromChunk_HtmlFormat(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: htmlEscapeData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromChunk_MixedContent(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: mixedContentData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromChunk_NoUnicode(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: noUnicodeData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromChunk_LargeData(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: largeData}
|
||||
|
||||
for b.Loop() {
|
||||
_ = decoder.FromChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark regex matching performance (most expensive operation)
|
||||
func BenchmarkRegexMatching_AllPatterns(b *testing.B) {
|
||||
testData := mixedContentData
|
||||
|
||||
for b.Loop() {
|
||||
// Simulate the pattern matching in FromChunk
|
||||
_ = longEscapePat.Match(testData)
|
||||
_ = braceEscapePat.Match(testData)
|
||||
_ = perlEscapePat.Match(testData)
|
||||
_ = htmlEscapePat.Match(testData)
|
||||
_ = percentEscapePat.Match(testData)
|
||||
_ = escapePat.Match(testData)
|
||||
_ = codePointPat.Match(testData)
|
||||
_ = cssEscapePat.Match(testData)
|
||||
//_ = hexEscapePat.Match(testData)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexMatching_NoMatch(b *testing.B) {
|
||||
testData := noUnicodeData
|
||||
|
||||
for b.Loop() {
|
||||
// Simulate the pattern matching in FromChunk on data with no matches
|
||||
_ = longEscapePat.Match(testData)
|
||||
_ = braceEscapePat.Match(testData)
|
||||
_ = perlEscapePat.Match(testData)
|
||||
_ = htmlEscapePat.Match(testData)
|
||||
_ = percentEscapePat.Match(testData)
|
||||
_ = escapePat.Match(testData)
|
||||
_ = codePointPat.Match(testData)
|
||||
_ = cssEscapePat.Match(testData)
|
||||
//_ = hexEscapePat.Match(testData)
|
||||
}
|
||||
}
|
||||
|
||||
// Memory allocation benchmarks
|
||||
func BenchmarkFromChunk_MemoryAllocation(b *testing.B) {
|
||||
decoder := &EscapedUnicode{}
|
||||
chunk := &sources.Chunk{Data: mixedContentData}
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
result := decoder.FromChunk(chunk)
|
||||
if result != nil {
|
||||
// Prevent compiler optimization
|
||||
_ = result.Data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,139 @@ func TestUnicodeEscape_FromChunk(t *testing.T) {
|
||||
},
|
||||
},
|
||||
|
||||
// New test cases for additional Unicode escape formats
|
||||
|
||||
// \u{X} format - Rust, Swift, some JS, etc.
|
||||
{
|
||||
name: "[brace] \\u{X} format - Rust/Swift style",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("\\u{74}\\u{6f}\\u{6b}\\u{65}\\u{6e}\\u{3a}\\u{20}\\u{22}\\u{67}\\u{68}\\u{70}\\u{5f}\\u{49}\\u{77}\\u{64}\\u{4d}\\u{78}\\u{39}\\u{57}\\u{46}\\u{57}\\u{52}\\u{52}\\u{66}\\u{4d}\\u{68}\\u{54}\\u{59}\\u{69}\\u{61}\\u{56}\\u{6a}\\u{5a}\\u{37}\\u{38}\\u{4a}\\u{66}\\u{75}\\u{61}\\u{6d}\\u{76}\\u{6e}\\u{30}\\u{59}\\u{57}\\u{52}\\u{4d}\\u{30}\\u{22}"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("token: \"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\""),
|
||||
},
|
||||
},
|
||||
|
||||
// \U00XXXXXX format - Python, etc.
|
||||
{
|
||||
name: "[long] \\U00XXXXXX format - Python style",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("\\U00000074\\U0000006f\\U0000006b\\U00000065\\U0000006e\\U0000003a\\U00000020\\U00000022\\U00000067\\U00000068\\U00000070\\U0000005f\\U00000049\\U00000077\\U00000064\\U0000004d\\U00000078\\U00000039\\U00000057\\U00000046\\U00000057\\U00000052\\U00000052\\U00000066\\U0000004d\\U00000068\\U00000054\\U00000059\\U00000069\\U00000061\\U00000056\\U0000006a\\U0000005a\\U00000037\\U00000038\\U0000004a\\U00000066\\U00000075\\U00000061\\U0000006d\\U00000076\\U0000006e\\U00000030\\U00000059\\U00000057\\U00000052\\U0000004d\\U00000030\\U00000022"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("token: \"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\""),
|
||||
},
|
||||
},
|
||||
|
||||
// \x{X} format - Perl
|
||||
{
|
||||
name: "[perl] \\x{X} format - Perl style",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("\\x{74}\\x{6f}\\x{6b}\\x{65}\\x{6e}\\x{3a}\\x{20}\\x{22}\\x{67}\\x{68}\\x{70}\\x{5f}\\x{49}\\x{77}\\x{64}\\x{4d}\\x{78}\\x{39}\\x{57}\\x{46}\\x{57}\\x{52}\\x{52}\\x{66}\\x{4d}\\x{68}\\x{54}\\x{59}\\x{69}\\x{61}\\x{56}\\x{6a}\\x{5a}\\x{37}\\x{38}\\x{4a}\\x{66}\\x{75}\\x{61}\\x{6d}\\x{76}\\x{6e}\\x{30}\\x{59}\\x{57}\\x{52}\\x{4d}\\x{30}\\x{22}"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("token: \"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\""),
|
||||
},
|
||||
},
|
||||
|
||||
// \X format - CSS (space delimited)
|
||||
// ToDo: Look into supporting CSS where there is no whitespace ex: \013322\013171\013001. Currently not supported by this implementation.
|
||||
{
|
||||
name: "[css] \\X format - CSS style",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("\\74 \\6f \\6b \\65 \\6e \\3a \\20 \\22 \\67 \\68 \\70 \\5f \\49 \\77 \\64 \\4d \\78 \\39 \\57 \\46 \\57 \\52 \\52 \\66 \\4d \\68 \\54 \\59 \\69 \\61 \\56 \\6a \\5a \\37 \\38 \\4a \\66 \\75 \\61 \\6d \\76 \\6e \\30 \\59 \\57 \\52 \\4d \\30 \\22 "),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("token: \"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\""),
|
||||
},
|
||||
},
|
||||
|
||||
// &#xX; format - HTML/XML
|
||||
{
|
||||
name: "[html] &#xX; format - HTML/XML style",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("token: "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""),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("token: \"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\""),
|
||||
},
|
||||
},
|
||||
|
||||
// %uXXXX format - Percent-encoding (non-standard)
|
||||
{
|
||||
name: "[percent] %uXXXX format - Percent encoding",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("%u0074%u006f%u006b%u0065%u006e%u003a%u0020%u0022%u0067%u0068%u0070%u005f%u0049%u0077%u0064%u004d%u0078%u0039%u0057%u0046%u0057%u0052%u0052%u0066%u004d%u0068%u0054%u0059%u0069%u0061%u0056%u006a%u005a%u0037%u0038%u004a%u0066%u0075%u0061%u006d%u0076%u006e%u0030%u0059%u0057%u0052%u004d%u0030%u0022"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("token: \"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\""),
|
||||
},
|
||||
},
|
||||
|
||||
// // 0xX format - Hexadecimal notation with space separation
|
||||
// {
|
||||
// name: "[hex] 0xX format - Hex with spaces",
|
||||
// chunk: &sources.Chunk{
|
||||
// Data: []byte("0x74 0x6f 0x6b 0x65 0x6e 0x3a 0x20 0x22 0x67 0x68 0x70 0x5f 0x49 0x77 0x64 0x4d 0x78 0x39 0x57 0x46 0x57 0x52 0x52 0x66 0x4d 0x68 0x54 0x59 0x69 0x61 0x56 0x6a 0x5a 0x37 0x38 0x4a 0x66 0x75 0x61 0x6d 0x76 0x6e 0x30 0x59 0x57 0x52 0x4d 0x30 0x22 "),
|
||||
// },
|
||||
// want: &sources.Chunk{
|
||||
// Data: []byte("token: \"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\""),
|
||||
// },
|
||||
// },
|
||||
|
||||
// // 0xX format - Hexadecimal notation with comma separation
|
||||
// {
|
||||
// name: "[hex] 0xX format - Hex with commas",
|
||||
// chunk: &sources.Chunk{
|
||||
// Data: []byte("0x74,0x6f,0x6b,0x65,0x6e,0x3a,0x20,0x22,0x67,0x68,0x70,0x5f,0x49,0x77,0x64,0x4d,0x78,0x39,0x57,0x46,0x57,0x52,0x52,0x66,0x4d,0x68,0x54,0x59,0x69,0x61,0x56,0x6a,0x5a,0x37,0x38,0x4a,0x66,0x75,0x61,0x6d,0x76,0x6e,0x30,0x59,0x57,0x52,0x4d,0x30,0x22"),
|
||||
// },
|
||||
// want: &sources.Chunk{
|
||||
// Data: []byte("token: \"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\""),
|
||||
// },
|
||||
// },
|
||||
|
||||
// Test cases for mixed content with new formats
|
||||
{
|
||||
name: "[mixed] \\u{X} in code context",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("const secret = \"\\u{41}\\u{4b}\\u{49}\\u{41}\\u{55}\\u{4d}\\u{34}\\u{47}\\u{36}\\u{4f}\\u{36}\\u{4e}\\u{41}\\u{4b}\\u{45}\\u{37}\\u{4c}\\u{43}\\u{44}\\u{4a}\";"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("const secret = \"AKIAUM4G6O6NAKE7LCDJ\";"),
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "[mixed] HTML entity in web context",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("<span>AWS Key: AKIAUM4G6O6NAKE7LCDJ</span>"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("<span>AWS Key: AKIAUM4G6O6NAKE7LCDJ</span>"),
|
||||
},
|
||||
},
|
||||
|
||||
// Test cases for higher Unicode values (non-BMP)
|
||||
{
|
||||
name: "[emoji] \\u{X} with emoji",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("\\u{1f600} Happy face emoji"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("😀 Happy face emoji"),
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "[emoji] \\U00XXXXXX with emoji",
|
||||
chunk: &sources.Chunk{
|
||||
Data: []byte("\\U0001f600 Happy face emoji"),
|
||||
},
|
||||
want: &sources.Chunk{
|
||||
Data: []byte("😀 Happy face emoji"),
|
||||
},
|
||||
},
|
||||
|
||||
// nothing
|
||||
{
|
||||
name: "no escaped",
|
||||
|
||||
@@ -27,6 +27,18 @@ func TestUTF16Decoder(t *testing.T) {
|
||||
expected: []byte("Hello World"),
|
||||
expectNil: false,
|
||||
},
|
||||
{
|
||||
name: "Valid UTF-16LE input with BOM (FF FE)",
|
||||
input: []byte{255, 254, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0},
|
||||
expected: []byte("Hello World"),
|
||||
expectNil: false,
|
||||
},
|
||||
{
|
||||
name: "Valid UTF-16BE input with BOM (FE FF)",
|
||||
input: []byte{254, 255, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100},
|
||||
expected: []byte("Hello World"),
|
||||
expectNil: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid UTF-16 input (it's UTF-8)",
|
||||
input: []byte("Hello World!"),
|
||||
@@ -88,7 +100,7 @@ func BenchmarkUtf16ToUtf8(b *testing.B) {
|
||||
// Example UTF-16LE encoded data
|
||||
data := []byte{72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for b.Loop() {
|
||||
_, _ = utf16ToUTF8(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ go
|
||||
away.`)
|
||||
|
||||
func Benchmark_extractSubstrings(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
extractSubstrings(testBytes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@ package abstract
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "qwerty12345iugt67s7a7sa0akhsxz82"
|
||||
invalidPattern = "zxcvbr12345iugt67s7a7sa0akhsXz820"
|
||||
)
|
||||
|
||||
func TestAbstract_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,58 @@ func TestAbstract_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("abstract token = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to abstract API
|
||||
[DEBUG] Using API_KEY=oxpf4a93fjovt0v1z6lltcbcizlrml98
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"oxpf4a93fjovt0v1z6lltcbcizlrml98"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("abstract token keyword is not close to the real token = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{abstract}</id>
|
||||
<secret>{abstract AQAAABAAA 5422358j60yxo9nc0dbpxby602tsxd6j}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"5422358j60yxo9nc0dbpxby602tsxd6j"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("abstract = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - two keys",
|
||||
input: `
|
||||
[INFO] Sending request to abstract API
|
||||
[DEBUG] Using API_KEY=oxpf4a93fjovt0v1z6lltcbcizlrml98
|
||||
[Error] Response received: 401 UnAuthorized
|
||||
[INFO] Sending request to abstract API
|
||||
[DEBUG] Using API_KEY=muytrs09876iugt67s7a7sa0akhsxz82
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"oxpf4a93fjovt0v1z6lltcbcizlrml98", "muytrs09876iugt67s7a7sa0akhsxz82"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to abstract API
|
||||
[INFO] Processing request
|
||||
[Info] Response received: 200 OK
|
||||
[DEBUG] Used API_KEY=oxpf4a93fjovt0v1z6lltcbcizlrml98
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to abstract API
|
||||
[DEBUG] Using API_KEY=zxcvbr12345iugt67s7a7sa0akhsXz820
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +80,15 @@ func TestAbstract_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +100,7 @@ func TestAbstract_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package abuseipdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "123abcdef456ghijkl789mnopqr012stuvwx3455123abcdef456ghijkl789mnopqr012stuvwx3455"
|
||||
invalidPattern = "123abcdef456Ghijkl789mnopqr012stuvwx3455123abcdef456ghijkl789mnopqr012stuvwX"
|
||||
)
|
||||
|
||||
func TestAbuseipdb_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAbuseipdb_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("abuseipdb token = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to abuseipdb API
|
||||
[DEBUG] Using API_KEY=o8oqti3tghu2xic76ii4t7jb9bxuzd4200j1yrkdjl6s8834hx4dgz1wwo90diqraakjd13sljcjkfnf
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"o8oqti3tghu2xic76ii4t7jb9bxuzd4200j1yrkdjl6s8834hx4dgz1wwo90diqraakjd13sljcjkfnf"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("abuseipdb token keyword is not close to the real token = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{abuseipdb}</id>
|
||||
<secret>{abuseipdb AQAAABAAA zgtj0q3v38u4pthc6nmy02n60bj244u5o9j47ln1jlue5mxzaasfi29x4dzcbxroawvkm26thtr61066}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"zgtj0q3v38u4pthc6nmy02n60bj244u5o9j47ln1jlue5mxzaasfi29x4dzcbxroawvkm26thtr61066"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("abuseipdb = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to abuseipdb API
|
||||
[INFO] Processing request
|
||||
[Info] Response received: 200 OK
|
||||
[DEBUG] Used API_KEY=o8oqti3tghu2xic76ii4t7jb9bxuzd4200j1yrkdjl6s8834hx4dgz1wwo90diqraakjd13sljcjkfnf
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to abuseipdb API
|
||||
[DEBUG] Using API_KEY=7e4abcdef456Ghijkl789mnopqr012stuvwx3455123abcdef456ghijkl789mnopqr012stuvwX
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAbuseipdb_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAbuseipdb_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package abyssale
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "123abcDEF456ghiJKL789mnoPQR012stuVWX3456"
|
||||
invalidPattern = "123abcDEF456ghiJKL789mnoPQR012stuVWX"
|
||||
)
|
||||
|
||||
func TestAbyssale_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAbyssale_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("abyssale token = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to abyssale API
|
||||
[DEBUG] Using API_KEY=rWE8I0axy6Fvw40RE8tsNS3L7zBU5vAhEnW4hq9G
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"rWE8I0axy6Fvw40RE8tsNS3L7zBU5vAhEnW4hq9G"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("abyssale token keyword is not close to the real token = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{abyssale}</id>
|
||||
<secret>{abyssale AQAAABAAA xTiPNSDg6JjzG8fWoLb8JlE8SBcMKkCx2fZLZD91}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"xTiPNSDg6JjzG8fWoLb8JlE8SBcMKkCx2fZLZD91"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("abyssale = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to abyssale API
|
||||
[INFO] Processing request
|
||||
[Info] Response received: 200 OK
|
||||
[DEBUG] Used API_KEY=rWE8I0axy6Fvw40RE8tsNS3L7zBU5vAhEnW4hq9G
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to abyssale API
|
||||
[DEBUG] Using API_KEY=rWE8_0axy6Fvw40RE8tsNS3L7zBU5vAhEnW4hq9G
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAbyssale_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAbyssale_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package detectors
|
||||
|
||||
// AccountFilter implements account-based filtering functionality that detectors can embed
|
||||
// to gain allow and deny list capabilities for account IDs.
|
||||
type AccountFilter struct {
|
||||
allowedAccounts map[string]struct{}
|
||||
deniedAccounts map[string]struct{}
|
||||
}
|
||||
|
||||
// SetAllowedAccounts configures the allowed account IDs.
|
||||
// If set, only accounts in this list will be verified.
|
||||
func (a *AccountFilter) SetAllowedAccounts(accountIDs []string) {
|
||||
if len(accountIDs) == 0 {
|
||||
a.allowedAccounts = nil
|
||||
return
|
||||
}
|
||||
|
||||
accounts := make(map[string]struct{}, len(accountIDs))
|
||||
for _, accountID := range accountIDs {
|
||||
accounts[accountID] = struct{}{}
|
||||
}
|
||||
a.allowedAccounts = accounts
|
||||
}
|
||||
|
||||
// SetDeniedAccounts configures the denied account IDs.
|
||||
// Accounts in this list will never be verified.
|
||||
func (a *AccountFilter) SetDeniedAccounts(accountIDs []string) {
|
||||
if len(accountIDs) == 0 {
|
||||
a.deniedAccounts = nil
|
||||
return
|
||||
}
|
||||
|
||||
accounts := make(map[string]struct{}, len(accountIDs))
|
||||
for _, accountID := range accountIDs {
|
||||
accounts[accountID] = struct{}{}
|
||||
}
|
||||
a.deniedAccounts = accounts
|
||||
}
|
||||
|
||||
// ShouldSkipAccount checks if an account ID should be skipped for verification
|
||||
// based on allow and deny lists.
|
||||
//
|
||||
// Precedence: deny list > allow list (if account is in both, it's denied)
|
||||
func (a *AccountFilter) ShouldSkipAccount(accountID string) bool {
|
||||
// Check deny list first - takes precedence
|
||||
if len(a.deniedAccounts) > 0 {
|
||||
if _, isDenied := a.deniedAccounts[accountID]; isDenied {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Check allow list - if populated, account must be in it
|
||||
if len(a.allowedAccounts) > 0 {
|
||||
if _, isAllowed := a.allowedAccounts[accountID]; !isAllowed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Account is allowed for verification
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInDenyList checks if an account ID is in the deny list
|
||||
func (a *AccountFilter) IsInDenyList(accountID string) bool {
|
||||
if len(a.deniedAccounts) == 0 {
|
||||
return false
|
||||
}
|
||||
_, isDenied := a.deniedAccounts[accountID]
|
||||
return isDenied
|
||||
}
|
||||
|
||||
// IsInAllowList checks if an account ID is in the allow list
|
||||
func (a *AccountFilter) IsInAllowList(accountID string) bool {
|
||||
if len(a.allowedAccounts) == 0 {
|
||||
return false
|
||||
}
|
||||
_, isAllowed := a.allowedAccounts[accountID]
|
||||
return isAllowed
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package detectors
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEmbeddedAccountFilter(t *testing.T) {
|
||||
type Scanner struct{ AccountFilter }
|
||||
|
||||
t.Run("no filtering configured - should not skip", func(t *testing.T) {
|
||||
var s Scanner // Fresh instance for this test
|
||||
|
||||
shouldSkip := s.ShouldSkipAccount("test-account")
|
||||
assert.False(t, shouldSkip)
|
||||
assert.False(t, s.IsInDenyList("test-account"))
|
||||
assert.False(t, s.IsInAllowList("test-account"))
|
||||
})
|
||||
|
||||
t.Run("allowed accounts only", func(t *testing.T) {
|
||||
var s Scanner // Fresh instance for this test
|
||||
s.SetAllowedAccounts([]string{"allowed-account-1", "allowed-account-2"})
|
||||
|
||||
// Account in allow list - should not skip
|
||||
shouldSkip := s.ShouldSkipAccount("allowed-account-1")
|
||||
assert.False(t, shouldSkip)
|
||||
assert.True(t, s.IsInAllowList("allowed-account-1"))
|
||||
|
||||
// Account not in allow list - should skip
|
||||
shouldSkip = s.ShouldSkipAccount("other-account")
|
||||
assert.True(t, shouldSkip)
|
||||
assert.False(t, s.IsInAllowList("other-account"))
|
||||
})
|
||||
|
||||
t.Run("denied accounts only", func(t *testing.T) {
|
||||
var s Scanner // Fresh instance for this test
|
||||
s.SetDeniedAccounts([]string{"denied-account-1", "denied-account-2"})
|
||||
|
||||
// Account in deny list - should skip
|
||||
shouldSkip := s.ShouldSkipAccount("denied-account-1")
|
||||
assert.True(t, shouldSkip)
|
||||
assert.True(t, s.IsInDenyList("denied-account-1"))
|
||||
|
||||
// Account not in deny list - should not skip (no allow list restrictions)
|
||||
shouldSkip = s.ShouldSkipAccount("other-account")
|
||||
assert.False(t, shouldSkip)
|
||||
assert.False(t, s.IsInDenyList("other-account"))
|
||||
})
|
||||
|
||||
t.Run("deny list takes precedence over allow list", func(t *testing.T) {
|
||||
var s Scanner // Fresh instance for this test
|
||||
s.SetAllowedAccounts([]string{"conflicted-account", "allowed-only-account"})
|
||||
s.SetDeniedAccounts([]string{"conflicted-account"}) // Same account in both lists
|
||||
|
||||
// Account is in both allow and deny lists - deny takes precedence
|
||||
shouldSkip := s.ShouldSkipAccount("conflicted-account")
|
||||
assert.True(t, shouldSkip)
|
||||
assert.True(t, s.IsInDenyList("conflicted-account"))
|
||||
assert.True(t, s.IsInAllowList("conflicted-account"))
|
||||
|
||||
// Account only in allow list - should not skip
|
||||
shouldSkip = s.ShouldSkipAccount("allowed-only-account")
|
||||
assert.False(t, shouldSkip)
|
||||
assert.False(t, s.IsInDenyList("allowed-only-account"))
|
||||
assert.True(t, s.IsInAllowList("allowed-only-account"))
|
||||
})
|
||||
|
||||
t.Run("allow list with denied account not in allow list", func(t *testing.T) {
|
||||
var s Scanner // Fresh instance for this test
|
||||
s.SetAllowedAccounts([]string{"trusted-account"}) // Allow one account
|
||||
s.SetDeniedAccounts([]string{"blocked-account"}) // Deny different account
|
||||
|
||||
// Account in deny list (not in allow list) - should skip due to deny list
|
||||
shouldSkip := s.ShouldSkipAccount("blocked-account")
|
||||
assert.True(t, shouldSkip)
|
||||
assert.True(t, s.IsInDenyList("blocked-account"))
|
||||
assert.False(t, s.IsInAllowList("blocked-account"))
|
||||
|
||||
// Account in allow list (not in deny list) - should not skip
|
||||
shouldSkip = s.ShouldSkipAccount("trusted-account")
|
||||
assert.False(t, shouldSkip)
|
||||
assert.False(t, s.IsInDenyList("trusted-account"))
|
||||
assert.True(t, s.IsInAllowList("trusted-account"))
|
||||
|
||||
// Account in neither list - should skip due to allow list restriction
|
||||
shouldSkip = s.ShouldSkipAccount("unknown-account")
|
||||
assert.True(t, shouldSkip)
|
||||
assert.False(t, s.IsInDenyList("unknown-account"))
|
||||
assert.False(t, s.IsInAllowList("unknown-account"))
|
||||
})
|
||||
|
||||
t.Run("clearing lists", func(t *testing.T) {
|
||||
var s Scanner // Fresh instance for this test
|
||||
s.SetAllowedAccounts([]string{"initial-allowed"})
|
||||
s.SetDeniedAccounts([]string{"initial-denied"})
|
||||
|
||||
// Verify initial state
|
||||
assert.True(t, s.ShouldSkipAccount("random-account")) // Not in allow list
|
||||
assert.True(t, s.ShouldSkipAccount("initial-denied")) // In deny list
|
||||
|
||||
// Clear allowed accounts with nil
|
||||
s.SetAllowedAccounts(nil)
|
||||
assert.False(t, s.ShouldSkipAccount("random-account")) // No allow list restriction
|
||||
assert.True(t, s.ShouldSkipAccount("initial-denied")) // Still in deny list
|
||||
|
||||
// Clear denied accounts with empty slice
|
||||
s.SetDeniedAccounts([]string{})
|
||||
assert.False(t, s.ShouldSkipAccount("initial-denied")) // No longer denied
|
||||
assert.False(t, s.ShouldSkipAccount("initial-allowed")) // No restrictions
|
||||
})
|
||||
}
|
||||
@@ -2,21 +2,15 @@ package accuweather
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "DqFtwc490oPc%xaE67sBSF741M56%sd091A"
|
||||
invalidPattern = "DqFtwc490oPc%xaE67sBSF741M56=sd091A"
|
||||
validPatternLowEntropy = "DsFtwfaEsAPS%eaEsaESEsFesfMsfMsDmdA"
|
||||
)
|
||||
|
||||
func TestAccuWeather_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -27,24 +21,55 @@ func TestAccuWeather_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("accuweather token = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[DEBUG] Using API_KEY=WAgP6m4gYc1qe%HnjWAAF5HBKL%i6kwrsbD
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"WAgP6m4gYc1qe%HnjWAAF5HBKL%i6kwrsbD"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("accuweather token keyword is not close to the real token = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{accuweather}</id>
|
||||
<secret>{accuweather AQAAABAAA ErOAU9rTSuX6IfHFGsJbpK3bCC1jIEX%gtj}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"ErOAU9rTSuX6IfHFGsJbpK3bCC1jIEX%gtj"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("accuweather = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[INFO] Processing request
|
||||
[Info] Response received: 200 OK
|
||||
[DEBUG] Used API_KEY=WAgP6m4gYc1qe%HnjWAAF5HBKL%i6kwrsbD
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - Shannon entropy below threshold",
|
||||
input: fmt.Sprintf("accuweather = '%s'", validPatternLowEntropy),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[DEBUG] Using API_KEY=WAgP6m4gYc1qe$HnjWAAF5HBKL%i6kwrsbD
|
||||
[Error] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - Shannon entropy below threshold",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[DEBUG] Using API_KEY=WAAP6A4gYA1qA%HAaWAAFAHBAL%a6kwwwbD
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -52,22 +77,15 @@ func TestAccuWeather_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -79,6 +97,7 @@ func TestAccuWeather_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package accuweather
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "dqftwc490oPcxae67sBSF741M56d091a"
|
||||
invalidPattern = "dqftwc49%oPcxae67sBSF741M56f091a"
|
||||
)
|
||||
|
||||
func TestAccuWeather_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAccuWeather_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("accuweather token = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[DEBUG] Using API_KEY=Qh6DP6Zf7vHtmnDDsbS219qcz4d883Y9
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"Qh6DP6Zf7vHtmnDDsbS219qcz4d883Y9"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("accuweather token keyword is not close to the real token = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{accuweather}</id>
|
||||
<secret>{accuweather AQAAABAAA BJDD9bYh8bR586Wcw3F1lvkUYy3RZZbD}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"BJDD9bYh8bR586Wcw3F1lvkUYy3RZZbD"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("accuweather = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[INFO] Processing request
|
||||
[Info] Response received: 200 OK
|
||||
[DEBUG] Used API_KEY=Qh6DP6Zf7vHtmnDDsbS219qcz4d883Y9
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to accuweather API
|
||||
[DEBUG] Using API_KEY=Qh6DP6Zf7vHtm@DDsbS219qcz4d883Y9
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAccuWeather_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAccuWeather_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
//go:build detectors
|
||||
// +build detectors
|
||||
|
||||
package adafruitio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
func TestAdafruitIO_FromChunk(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors1")
|
||||
if err != nil {
|
||||
t.Fatalf("could not get test secrets from GCP: %s", err)
|
||||
}
|
||||
secret := testSecrets.MustGetField("ADAFRUITIO")
|
||||
inactiveSecret := testSecrets.MustGetField("ADAFRUITIO_INACTIVE")
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
data []byte
|
||||
verify bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
s Scanner
|
||||
args args
|
||||
want []detectors.Result
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "found, verified",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within", secret)),
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: true,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "found, real secrets, verification error due to timeout",
|
||||
s: Scanner{client: common.SaneHttpClientTimeOut(10 * time.Microsecond)},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within", secret)),
|
||||
verify: true,
|
||||
},
|
||||
want: func() []detectors.Result {
|
||||
r := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: false,
|
||||
}
|
||||
r.SetVerificationError(context.DeadlineExceeded)
|
||||
return []detectors.Result{r}
|
||||
}(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "found, real secrets, verification error due to unexpected api surface",
|
||||
s: Scanner{client: common.ConstantResponseHttpClient(500, "{}")},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within", secret)),
|
||||
verify: true,
|
||||
},
|
||||
want: func() []detectors.Result {
|
||||
r := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: false,
|
||||
}
|
||||
r.SetVerificationError(fmt.Errorf("unexpected HTTP response status 500"))
|
||||
return []detectors.Result{r}
|
||||
}(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "found, unverified",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: false,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte("You cannot find the secret within"),
|
||||
verify: true,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("AdafruitIO.FromData() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
for i := range got {
|
||||
if len(got[i].Raw) == 0 {
|
||||
t.Fatalf("no raw secret present: \n %+v", got[i])
|
||||
}
|
||||
gotErr := ""
|
||||
if got[i].VerificationError() != nil {
|
||||
gotErr = got[i].VerificationError().Error()
|
||||
}
|
||||
wantErr := ""
|
||||
if tt.want[i].VerificationError() != nil {
|
||||
wantErr = tt.want[i].VerificationError().Error()
|
||||
}
|
||||
if gotErr != wantErr {
|
||||
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.want[i].VerificationError(), got[i].VerificationError())
|
||||
}
|
||||
}
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError")
|
||||
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
|
||||
t.Errorf("AdafruitIO.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromData(benchmark *testing.B) {
|
||||
ctx := context.Background()
|
||||
s := Scanner{}
|
||||
for name, data := range detectors.MustGetBenchmarkData() {
|
||||
benchmark.Run(name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,167 +1,92 @@
|
||||
//go:build detectors
|
||||
// +build detectors
|
||||
|
||||
package adafruitio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
func TestAdafruitIO_FromChunk(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors1")
|
||||
if err != nil {
|
||||
t.Fatalf("could not get test secrets from GCP: %s", err)
|
||||
}
|
||||
secret := testSecrets.MustGetField("ADAFRUITIO")
|
||||
inactiveSecret := testSecrets.MustGetField("ADAFRUITIO_INACTIVE")
|
||||
func TestAdafruitio_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
data []byte
|
||||
verify bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
s Scanner
|
||||
args args
|
||||
want []detectors.Result
|
||||
wantErr bool
|
||||
name string
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "found, verified",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within", secret)),
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: true,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using API_KEY=aio_VxEqGaqgMgZej3DceezbBy03eWyW
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"aio_VxEqGaqgMgZej3DceezbBy03eWyW"},
|
||||
},
|
||||
{
|
||||
name: "found, real secrets, verification error due to timeout",
|
||||
s: Scanner{client: common.SaneHttpClientTimeOut(10 * time.Microsecond)},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within", secret)),
|
||||
verify: true,
|
||||
},
|
||||
want: func() []detectors.Result {
|
||||
r := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: false,
|
||||
}
|
||||
r.SetVerificationError(context.DeadlineExceeded)
|
||||
return []detectors.Result{r}
|
||||
}(),
|
||||
wantErr: false,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{adafruitio}</id>
|
||||
<secret>{AQAAABAAA aio_cQD77DF9SgsYbgWcxJbpLOlR5emX}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"aio_cQD77DF9SgsYbgWcxJbpLOlR5emX"},
|
||||
},
|
||||
{
|
||||
name: "found, real secrets, verification error due to unexpected api surface",
|
||||
s: Scanner{client: common.ConstantResponseHttpClient(500, "{}")},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within", secret)),
|
||||
verify: true,
|
||||
},
|
||||
want: func() []detectors.Result {
|
||||
r := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: false,
|
||||
}
|
||||
r.SetVerificationError(fmt.Errorf("unexpected HTTP response status 500"))
|
||||
return []detectors.Result{r}
|
||||
}(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "found, unverified",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find a adafruitio secret %s within but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AdafruitIO,
|
||||
Verified: false,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte("You cannot find the secret within"),
|
||||
verify: true,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using API_KEY=aio_VxEqGaqgMgZej3DceezbBy03eWyWa
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("AdafruitIO.FromData() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
for i := range got {
|
||||
if len(got[i].Raw) == 0 {
|
||||
t.Fatalf("no raw secret present: \n %+v", got[i])
|
||||
}
|
||||
gotErr := ""
|
||||
if got[i].VerificationError() != nil {
|
||||
gotErr = got[i].VerificationError().Error()
|
||||
}
|
||||
wantErr := ""
|
||||
if tt.want[i].VerificationError() != nil {
|
||||
wantErr = tt.want[i].VerificationError().Error()
|
||||
}
|
||||
if gotErr != wantErr {
|
||||
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.want[i].VerificationError(), got[i].VerificationError())
|
||||
}
|
||||
}
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError")
|
||||
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
|
||||
t.Errorf("AdafruitIO.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromData(benchmark *testing.B) {
|
||||
ctx := context.Background()
|
||||
s := Scanner{}
|
||||
for name, data := range detectors.MustGetBenchmarkData() {
|
||||
benchmark.Run(name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
actual := make(map[string]struct{}, len(results))
|
||||
for _, r := range results {
|
||||
if len(r.RawV2) > 0 {
|
||||
actual[string(r.RawV2)] = struct{}{}
|
||||
} else {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||
t.Errorf("%s diff: (-want +got)\n%s", test.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@ package adobeio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "zxcv0987mnbv1234poiu6749gtnrfv54/WDcv0981Mn.B"
|
||||
invalidPattern = "Rzxc#0987$%bv1234poiu6749gtnrfv54"
|
||||
)
|
||||
|
||||
func TestAdobeIO_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,48 @@ func TestAdobeIO_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("adobe = '%s'", validPattern),
|
||||
want: []string{"zxcv0987mnbv1234poiu6749gtnrfv54WDcv0981Mn.B"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the adobe API
|
||||
[DEBUG] Using adobe KEY=zoaw0c0m50m0hz2h1fm21y4tqfyl7ifi
|
||||
[DEBUG] Using adobe ID=qCRbiIy1NJaW
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"zoaw0c0m50m0hz2h1fm21y4tqfyl7ifiqCRbiIy1NJaW"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("adobe keyword is not close to the real id and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{adobe ftd7hkeafk0q}</id>
|
||||
<secret>{adobe AQAAABAAA siybmtkgho9nsgjhng5yhp92wnir2a9t}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"siybmtkgho9nsgjhng5yhp92wnir2a9tftd7hkeafk0q"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("adobe%s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to the adobe API
|
||||
[DEBUG] Using KEY=zoaw0c0m50m0hz2h1fm21y4tqfyl7ifi
|
||||
[DEBUG] Using ID=qCRbiIy1NJaW
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the adobe API
|
||||
[DEBUG] Using adobe KEY=Rzxc#0987$%bv1234poiu6749gtnrfv54
|
||||
[DEBUG] Using adobe ID=qCRbiIy1NJaW
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +70,15 @@ func TestAdobeIO_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +90,7 @@ func TestAdobeIO_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,21 +2,15 @@ package adzuna
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "asdf0987mnbv1234qsxojb6ygb2wsx0o/qsc0f098"
|
||||
invalidPattern = "as#f0987mnbv1234^sxojb6ygb2wsx0o/qsc0f098"
|
||||
)
|
||||
|
||||
func TestAdzuna_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -27,29 +21,66 @@ func TestAdzuna_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("adzuna = '%s'", validPattern),
|
||||
want: []string{"asdf0987mnbv1234qsxojb6ygb2wsx0oqsc0f098"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the adzuna API
|
||||
[DEBUG] Using adzuna KEY=smcud4y6elxx7u6q58ewwv8rq01hpi3f
|
||||
[DEBUG] Using adzuna ID=cxu9w2g6
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"smcud4y6elxx7u6q58ewwv8rq01hpi3fcxu9w2g6"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("adzuna keyword is not close to the real id and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{adzuna svkit0wx}</id>
|
||||
<secret>{adzuna AQAAABAAA atubvgvpd6jjo0ac1wjianofnpgr24ac}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"atubvgvpd6jjo0ac1wjianofnpgr24acsvkit0wx"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only key",
|
||||
input: fmt.Sprintf("adzuna %s", strings.Split(validPattern, "/")[0]),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to the adzuna API
|
||||
[DEBUG] Using KEY=smcud4y6elxx7u6q58ewwv8rq01hpi3f
|
||||
[DEBUG] Using ID=cxu9w2g6
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only id",
|
||||
input: fmt.Sprintf("adzuna %s", strings.Split(validPattern, "/")[1]),
|
||||
want: nil,
|
||||
name: "valid pattern - only key",
|
||||
input: `
|
||||
[INFO] Sending request to the adzuna API
|
||||
[DEBUG] Using KEY=smcud4y6elxx7u6q58ewwv8rq01hpi3f
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("adzuna %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - only id",
|
||||
input: `
|
||||
[INFO] Sending request to the adzuna API
|
||||
[DEBUG] Using ID=cxu9w2g6
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the adzuna API
|
||||
[DEBUG] Using KEY=sxojb6ygb2wsx0o
|
||||
[DEBUG] Using ID=cxu9w2g6
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -57,22 +88,15 @@ func TestAdzuna_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,6 +108,7 @@ func TestAdzuna_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,21 +2,15 @@ package aeroworkflow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "qscVgy!WdvG;^#O:*?TG/806445634"
|
||||
invalidPattern = "qscVg&!WdvG;^#O:*?TG/8064456A4"
|
||||
)
|
||||
|
||||
func TestAeroWorkflow_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -27,29 +21,66 @@ func TestAeroWorkflow_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("aeroworkflow = '%s'", validPattern),
|
||||
want: []string{"qscVgy!WdvG;^#O:*?TG806445634"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the aeroworkflow API
|
||||
[DEBUG] Using aeroworkflow KEY=VmFYK7WG3CkgVmTl:c*X
|
||||
[DEBUG] Using aeroworkflow ID=678436
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"VmFYK7WG3CkgVmTl:c*X678436"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("aeroworkflow keyword is not close to the real id and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{aeroworkflow 6}</id>
|
||||
<secret>{aeroworkflow AQAAABAAA XjPSUOhREIN:4HX2#akH}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"XjPSUOhREIN:4HX2#akH6"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only key",
|
||||
input: fmt.Sprintf("aeroworkflow %s", strings.Split(validPattern, "/")[0]),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to the aeroworkflow API
|
||||
[DEBUG] Using KEY=VmFYK7WG3CkgVmTl:c*X
|
||||
[DEBUG] Using ID=678436
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only id",
|
||||
input: fmt.Sprintf("aeroworkflow %s", strings.Split(validPattern, "/")[1]),
|
||||
want: nil,
|
||||
name: "valid pattern - only key",
|
||||
input: `
|
||||
[INFO] Sending request to the aeroworkflow API
|
||||
[DEBUG] Using KEY=VmFYK7WG3CkgVmTl:c*X
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("aeroworkflow %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - only id",
|
||||
input: `
|
||||
[INFO] Sending request to the aeroworkflow API
|
||||
[DEBUG] Using ID=678436
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the aeroworkflow API
|
||||
[DEBUG] Using KEY=VmFYK7WG3CkgVmTl:c*X
|
||||
[DEBUG] Using ID=cxu9w2g6
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -57,22 +88,15 @@ func TestAeroWorkflow_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,6 +108,7 @@ func TestAeroWorkflow_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,28 +2,15 @@ package agora
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validKeyPattern = "asdf0987mnbv1234qsxojb6ygb2wsx0o"
|
||||
validSecretPattern = "beqr7215fr4g6bfjkmnvxrtygb2wsxap"
|
||||
complexPattern = `agora credentials
|
||||
these are some example credentials for login.
|
||||
use these to login.
|
||||
key: asdf0987mnbv1234qsxojb6ygb2wsx0o
|
||||
secret: beqr7215fr4g6bfjkmnvxrtygb2wsxap
|
||||
loginUrl: https://www.agora.com/example_login
|
||||
`
|
||||
invalidPattern = "asdf0987mNbv1234qsxojb6ygb2w$x0o/beqr7215fr4g6bfjkmnVxrtygb2wsxap"
|
||||
)
|
||||
|
||||
func TestAgora_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -34,34 +21,66 @@ func TestAgora_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("agora key='%s' - secret='%s'", validKeyPattern, validSecretPattern),
|
||||
want: []string{validKeyPattern + validSecretPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the agora API
|
||||
[DEBUG] Using Token=6p77f9gjhxx9mwdj86of7y7820bh49vw
|
||||
[DEBUG] Using Secret=qi6txx6vd0qzn6j01xj9rr6clyejvjw5
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"6p77f9gjhxx9mwdj86of7y7820bh49vwqi6txx6vd0qzn6j01xj9rr6clyejvjw5"},
|
||||
},
|
||||
{
|
||||
name: "valid complex pattern",
|
||||
input: fmt.Sprintf("agora data='%s'", complexPattern),
|
||||
want: []string{validKeyPattern + validSecretPattern},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{agora 3devtbiys8b282kidr9u78kjq8xdtlo1}</id>
|
||||
<secret>{AQAAABAAA bc7c6tag5jfuhz4y7v6v05dx2wq2z1ua}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"3devtbiys8b282kidr9u78kjq8xdtlo1bc7c6tag5jfuhz4y7v6v05dx2wq2z1ua"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: fmt.Sprintf("agora keyword is not close to the real key or secret = '%s|%s'", validKeyPattern, validSecretPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to the agora API
|
||||
[DEBUG] Using 6p77f9gjhxx9mwdj86of7y7820bh49vw
|
||||
[DEBUG] Using qi6txx6vd0qzn6j01xj9rr6clyejvjw5
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only key",
|
||||
input: fmt.Sprintf("agora key%s", validKeyPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - only key",
|
||||
input: `
|
||||
[INFO] Sending request to the agora API
|
||||
[DEBUG] Using Key=6p77f9gjhxx9mwdj86of7y7820bh49vw
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only secret",
|
||||
input: fmt.Sprintf("agora secret%s", validSecretPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - only secret",
|
||||
input: `
|
||||
[INFO] Sending request to the agora API
|
||||
[DEBUG] Using Secret=qi6txx6vd0qzn6j01xj9rr6clyejvjw5
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("agora %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the agora API
|
||||
[DEBUG] Using KEY=qi6txx6vd0qzn6j01xj9rr6clyejvjw
|
||||
[DEBUG] Using ID=qi6txx6vd0qzn6j01xj9rr6clyejvjw5yt
|
||||
[ERROR] Response received: 400 BadRequest
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -69,22 +88,15 @@ func TestAgora_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -96,6 +108,7 @@ func TestAgora_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
+35
-26
@@ -34,6 +34,14 @@ func (s Scanner) Keywords() []string {
|
||||
return []string{"aha.io"}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Aha
|
||||
}
|
||||
|
||||
func (s Scanner) Description() string {
|
||||
return "Aha is a product management software suite. Aha API keys can be used to access and modify product data and workflows."
|
||||
}
|
||||
|
||||
func (s Scanner) getClient() *http.Client {
|
||||
if s.client != nil {
|
||||
return s.client
|
||||
@@ -44,30 +52,39 @@ func (s Scanner) getClient() *http.Client {
|
||||
// FromData will find and optionally verify Aha secrets in a given set of bytes.
|
||||
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
dataStr := string(data)
|
||||
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
URLmatches := URLPat.FindAllStringSubmatch(dataStr, -1)
|
||||
|
||||
resURLMatch := "aha.io"
|
||||
for _, URLmatch := range URLmatches {
|
||||
resURLMatch = strings.TrimSpace(URLmatch[1])
|
||||
var uniqueFoundUrls = make(map[string]struct{})
|
||||
|
||||
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
|
||||
for _, match := range URLPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
uniqueFoundUrls[match[1]] = struct{}{}
|
||||
}
|
||||
|
||||
// if no url was found use the default
|
||||
if len(uniqueFoundUrls) == 0 {
|
||||
uniqueFoundUrls["aha.io"] = struct{}{}
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
resMatch := strings.TrimSpace(match[1])
|
||||
for url := range uniqueFoundUrls {
|
||||
resMatch := strings.TrimSpace(match[1])
|
||||
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Aha,
|
||||
Raw: []byte(resMatch),
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Aha,
|
||||
Raw: []byte(resMatch),
|
||||
RawV2: []byte(resMatch + url),
|
||||
}
|
||||
|
||||
if verify {
|
||||
client := s.getClient()
|
||||
isVerified, verificationErr := verifyAha(ctx, client, resMatch, url)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
}
|
||||
|
||||
if verify {
|
||||
client := s.getClient()
|
||||
isVerified, verificationErr := verifyAha(ctx, client, resMatch, resURLMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
@@ -98,11 +115,3 @@ func verifyAha(ctx context.Context, client *http.Client, resMatch, resURLMatch s
|
||||
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Aha
|
||||
}
|
||||
|
||||
func (s Scanner) Description() string {
|
||||
return "Aha is a product management software suite. Aha API keys can be used to access and modify product data and workflows."
|
||||
}
|
||||
|
||||
@@ -2,62 +2,85 @@ package aha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff/example.aha.io"
|
||||
invalidPattern = "00112233445566778899aabbCC$%eeff00112233445566778899aabbccddeeff/example.fake.io"
|
||||
)
|
||||
|
||||
func TestAha_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
|
||||
key := strings.Split(validPattern, "/")[0]
|
||||
url := strings.Split(validPattern, "/")[1]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("aha.io = '%s'", validPattern),
|
||||
want: []string{key},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] sending request to the aha.io API
|
||||
[DEBUG] using key = 81a1411a7e276fd88819df3137eb406e0f281f8a8c417947ca4b025890c8541c
|
||||
[DEBUG] using host = example.aha.io
|
||||
[INFO] response received: 200 OK
|
||||
`,
|
||||
want: []string{"81a1411a7e276fd88819df3137eb406e0f281f8a8c417947ca4b025890c8541cexample.aha.io"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - detect URL far away from keyword",
|
||||
input: fmt.Sprintf("aha.io = '%s\n URL is not close to the keyword but should be detected %s'", key, url),
|
||||
want: []string{key},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{aha 3af0b286b668d9636fd68076d6c87a333fe285fd41593cfceab36b35606c915a}</id>
|
||||
<secret>{AQAAABAAA ACTp3nufSEO791nIReS5udnRVFcG9j6-CqBJogBxo1pbql.aha.io}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"3af0b286b668d9636fd68076d6c87a333fe285fd41593cfceab36b35606c915aACTp3nufSEO791nIReS5udnRVFcG9j6-CqBJogBxo1pbql.aha.io"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("aha.io keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[INFO] sending request to the aha.io API
|
||||
[WARN] Do not commit the secrets
|
||||
[DEBUG] using key = 81a1411a7e276fd88819df3137eb406e0f281f8a8c417947ca4b025890c8541c
|
||||
[DEBUG] using host = example.aha.io
|
||||
[INFO] response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only key",
|
||||
input: fmt.Sprintf("aha.io %s", key),
|
||||
want: []string{key},
|
||||
name: "valid pattern - only key",
|
||||
input: `
|
||||
[INFO] sending request to the aha.io API
|
||||
[DEBUG] using key = 81a1411a7e276fd88819df3137eb406e0f281f8a8c417947ca4b025890c8541c
|
||||
[INFO] response received: 200 OK
|
||||
`,
|
||||
want: []string{"81a1411a7e276fd88819df3137eb406e0f281f8a8c417947ca4b025890c8541caha.io"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only URL",
|
||||
input: fmt.Sprintf("aha.io %s", url),
|
||||
want: nil,
|
||||
name: "valid pattern - only URL",
|
||||
input: `
|
||||
[INFO] sending request to the example.aha.io API
|
||||
[INFO] response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("aha.io %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] sending request to the aha.io API
|
||||
[DEBUG] using key = 81a1411a7e276fd88819df3137eJ406e0f281f8a8c417947ca4b025890c8541c
|
||||
[DEBUG] using host = 1test.aha.io
|
||||
[INFO] response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -65,22 +88,15 @@ func TestAha_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,6 +108,7 @@ func TestAha_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,21 +2,15 @@ package airbrakeprojectkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "qwmnerBv56zxpocvkjqr78afvYUx90Op/451298"
|
||||
invalidPattern = "qwmnerBv56zxpocvkjqr78afvYU$90Op/4512987"
|
||||
)
|
||||
|
||||
func TestAirBrakeProjectKey_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -27,29 +21,67 @@ func TestAirBrakeProjectKey_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("airbrake = '%s'", validPattern),
|
||||
want: []string{"qwmnerBv56zxpocvkjqr78afvYUx90Op451298"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airbrake API
|
||||
[DEBUG] Using airbrake Key=7B759RwRR5Txo9pDxXtPNcrTOj0zhvmR
|
||||
[DEBUG] Using airbrake ID=856019
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"7B759RwRR5Txo9pDxXtPNcrTOj0zhvmR856019"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("airbrake keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{airbrake 691149}</id>
|
||||
<secret>{airbrake AQAAABAAA hYNK8PlcGXZ6PXXDFJI89LCjpoM8koTx}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"hYNK8PlcGXZ6PXXDFJI89LCjpoM8koTx691149"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only key",
|
||||
input: fmt.Sprintf("airbrake %s", strings.Split(validPattern, "/")[0]),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[INFO] airbrake API request handling
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=7B759RwRR5Txo9pDxXtPNcrTOj0zhvmR
|
||||
[DEBUG] Using ID=856019
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid pattern - only ID",
|
||||
input: fmt.Sprintf("airbrake %s", strings.Split(validPattern, "/")[0]),
|
||||
want: nil,
|
||||
name: "valid pattern - only key",
|
||||
input: `
|
||||
[INFO] Sending request to the airbrake API
|
||||
[DEBUG] Using airbrake Key=7B759RwRR5Txo9pDxXtPNcrTOj0zhvmR
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("airbrake = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - only ID",
|
||||
input: `
|
||||
[INFO] Sending request to the airbrake API
|
||||
[DEBUG] Using airbrake ID=856019
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airbrake API
|
||||
[DEBUG] Using airbrake Key=qwmnerBv56zx**cvkjqr78afvYU$90Op
|
||||
[DEBUG] Using airbrake ID=856019
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -57,22 +89,15 @@ func TestAirBrakeProjectKey_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,6 +109,7 @@ func TestAirBrakeProjectKey_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package airbrakeuserkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "qsCGuilpkk2ngrsz75wtYqsCGuilpkk2ngrsz75w"
|
||||
invalidPattern = "Qs%CGuil#pkk2ngrsz75wtYqsCGuilpkk2ngrsz75w"
|
||||
)
|
||||
|
||||
func TestAirBrakeUserKey_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAirBrakeUserKey_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("airbrake = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airbrake API
|
||||
[DEBUG] Using Key=qsCGuilpkk2ngrsz75wtYqsCGuilpkk2ngrsz75w
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"qsCGuilpkk2ngrsz75wtYqsCGuilpkk2ngrsz75w"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("airbrake keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{airbrake 691149}</id>
|
||||
<secret>{airbrake AQAAABAAA UTDwMhGhuk0T04V0yqTqcKIwSSp7syUyQRG8JwoF}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"UTDwMhGhuk0T04V0yqTqcKIwSSp7syUyQRG8JwoF"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("airbrake = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] airbrake api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=qsCGuilpkk2ngrsz75wtYqsCGuilpkk2ngrsz75w
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airbrake API
|
||||
[DEBUG] Using airbrake Key=Qs%CGuil#pkk2ngrsz75wtYqsCGuilpkk2ngrsz75w
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAirBrakeUserKey_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAirBrakeUserKey_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package airship
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "123abcdef456ghijkl789mnopqr012stuvwx3455123abcdef456ghijkl789mnopqr012stuvwx3455trdfrq95acr"
|
||||
invalidPattern = "123#abcdef456gh$ijkl789mnopqr012stuvwX3455123abcdef456ghijkl789mnopqr012stuvwx3455trdfrq"
|
||||
)
|
||||
|
||||
func TestAirship_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAirship_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("airship = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airship API
|
||||
[DEBUG] Using Key=O3BV99CUDw3xYUAL0tHGYUe7mOj5PA5vTnLdJwULCTh9dxk9PmmTpL1kI846G3QGIsECVyVSsxZnIbfSwWc8xuX843W
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"O3BV99CUDw3xYUAL0tHGYUe7mOj5PA5vTnLdJwULCTh9dxk9PmmTpL1kI846G3QGIsECVyVSsxZnIbfSwWc8xuX843W"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("airship keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{airship}</id>
|
||||
<secret>{airship AQAAABAAA oVH3yIO1oAoXpK9Rc01EGNNTuw6d4Zyt07YNFmje644Ht00hvAaYwldNOV9vIPQw6dYHJLRgp2f75YdJ9OiICkYVhMI}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"oVH3yIO1oAoXpK9Rc01EGNNTuw6d4Zyt07YNFmje644Ht00hvAaYwldNOV9vIPQw6dYHJLRgp2f75YdJ9OiICkYVhMI"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("airship = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] airship api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=O3BV99CUDw3xYUAL0tHGYUe7mOj5PA5vTnLdJwULCTh9dxk9PmmTpL1kI846G3QGIsECVyVSsxZnIbfSwWc8xuX843W
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airship API
|
||||
[DEBUG] Using Key=O3BV99CUDw3xY#AL0tHGYUe7mOj5PA5vTnLdJwULCTh9dxk9PmmTpL1kI846G3QGIsECVyVSsxZnIbfSwWc8xuX843W
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAirship_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAirship_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
package airtableapikey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
type Scanner struct {
|
||||
client *http.Client
|
||||
|
||||
detectors.DefaultMultiPartCredentialProvider
|
||||
}
|
||||
|
||||
// Ensure the Scanner satisfies the interface at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
|
||||
var (
|
||||
defaultClient = common.SaneHttpClient()
|
||||
|
||||
appPat = regexp.MustCompile(`(app[\w-]{14})`) // could be part of url
|
||||
keyPat = regexp.MustCompile(`\b(key[a-zA-Z0-9_-]{14})\b`)
|
||||
personalPat = regexp.MustCompile(`(\bpat[[:alnum:]]{14}\.[[:alnum:]]{64}\b)`)
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
// Use identifiers in the secret preferably, or the provider name.
|
||||
func (s Scanner) Keywords() []string {
|
||||
return []string{"airtable"}
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Error struct {
|
||||
Type string `json:"type"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// FromData will find and optionally verify AirtableApiKey secrets in a given set of bytes.
|
||||
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
dataStr := string(data)
|
||||
|
||||
appMatches := make(map[string]struct{})
|
||||
for _, matches := range appPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
appMatches[matches[1]] = struct{}{}
|
||||
}
|
||||
keyMatches := make(map[string]struct{})
|
||||
for _, matches := range keyPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
keyMatches[matches[1]] = struct{}{}
|
||||
}
|
||||
for _, matches := range personalPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
keyMatches[matches[1]] = struct{}{}
|
||||
}
|
||||
|
||||
for keyMatch := range keyMatches {
|
||||
var (
|
||||
r *detectors.Result
|
||||
appMatch string
|
||||
)
|
||||
|
||||
for a := range appMatches {
|
||||
appMatch = a
|
||||
|
||||
if verify {
|
||||
client := s.client
|
||||
if client == nil {
|
||||
client = defaultClient
|
||||
}
|
||||
|
||||
isVerified, verificationErr := verifyMatch(ctx, client, appMatch, keyMatch)
|
||||
if isVerified {
|
||||
r = createResult(appMatch, keyMatch, isVerified, verificationErr)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
if len(appMatches) != 1 {
|
||||
appMatch = ""
|
||||
}
|
||||
r = createResult(appMatch, keyMatch, false, nil)
|
||||
}
|
||||
results = append(results, *r)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func createResult(app string, key string, verified bool, err error) *detectors.Result {
|
||||
r := &detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AirtableApiKey,
|
||||
Raw: []byte(key),
|
||||
Redacted: app,
|
||||
Verified: verified,
|
||||
}
|
||||
|
||||
if app != "" {
|
||||
r.RawV2 = []byte(fmt.Sprintf(`%s:%s`, app, key))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.SetVerificationError(err, key)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func verifyMatch(ctx context.Context, client *http.Client, app string, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.airtable.com/v0/"+app+"/Projects", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
case http.StatusForbidden:
|
||||
var resp response
|
||||
if err = json.NewDecoder(res.Body).Decode(&resp); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// check if the error is due to invalid permissions or model not found
|
||||
if resp.Error.Type == "INVALID_PERMISSIONS_OR_MODEL_NOT_FOUND" {
|
||||
// The key is verified as it works, but the user must enumerate the tables or permissions for the key.
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_AirtableApiKey
|
||||
}
|
||||
|
||||
func (s Scanner) Description() string {
|
||||
return "Airtable is a cloud collaboration service that offers database-like features. Airtable API keys can be used to access and modify data within Airtable bases."
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func verifyMatch(ctx context.Context, client *http.Client, token string) (bool,
|
||||
endpoint := "https://api.airtable.com/v0/meta/whoami"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return false, nil, nil
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
@@ -2,21 +2,15 @@ package airtableoauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern1 = "oaajtCy2lVMUN1Cm5.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsImV4cGlyZXNBdCI6IjIwMjUtMDItMDNUMTk6NTY6MzcuMDAwWiIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwic2VjcmV0IjoiMzczNThlNzdlZjlhMjljY2Q5MWIwNmNlNTdkZDYxNDg0MWVmNmIyOWYwYjQ5ZWE0MTMxZGI4NzBkNTAzYTE1NyJ9.0d67c8b334048135a93615610445e4aa90c6af6222392b49eea9419e1d6717d0"
|
||||
validPattern2 = "oaaRYiYSlTFXZzxDM.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwiZXhwaXJlc0F0IjoiMjAyNS0wMS0yOVQwMDowMTo0NC4wMDBaIiwic2VjcmV0IjoiZjYyOWE1MWVkM2M0ZjU5ODlmOTcyMDU1ZjkwODk3NDA4NmU0NjQxY2JhODU5Y2FhZTJkZjliMWQwODg0ZjIzMiJ9.27a8998029ac9bdd599b435572821dcb63c60cbf62b9cb2ba2a73511e5553d66"
|
||||
invalidPattern = "oaaRYiYSlTFXZzxDM.v2.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwiZXhwaXJlc0F0IjoiMjAyNS0wMS0yOVQwMDowMTo0NC4wMDBaIiwic2VjcmV0IjoiZjYyOWE1MWVkM2M0ZjU5ODlmOTcyMDU1ZjkwODk3NDA4NmU0NjQxY2JhODU5Y2FhZTJkZjliMWQwODg0ZjIzMiJ9.27a8998029ac9bdd599b435572821dcb63c60cbf62b9cb2ba2a73511e5553d66"
|
||||
)
|
||||
|
||||
func TestAirtableoauth_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,20 +20,50 @@ func TestAirtableoauth_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "typical pattern",
|
||||
input: fmt.Sprintf("airtable token = '%s'", validPattern1),
|
||||
want: []string{validPattern1},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airtable API
|
||||
[DEBUG] Using Key=oaajtCy2lVMUN1Cm5.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsImV4cGlyZXNBdCI6IjIwMjUtMDItMDNUMTk6NTY6MzcuMDAwWiIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwic2VjcmV0IjoiMzczNThlNzdlZjlhMjljY2Q5MWIwNmNlNTdkZDYxNDg0MWVmNmIyOWYwYjQ5ZWE0MTMxZGI4NzBkNTAzYTE1NyJ9.0d67c8b334048135a93615610445e4aa90c6af6222392b49eea9419e1d6717d0
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"oaajtCy2lVMUN1Cm5.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsImV4cGlyZXNBdCI6IjIwMjUtMDItMDNUMTk6NTY6MzcuMDAwWiIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwic2VjcmV0IjoiMzczNThlNzdlZjlhMjljY2Q5MWIwNmNlNTdkZDYxNDg0MWVmNmIyOWYwYjQ5ZWE0MTMxZGI4NzBkNTAzYTE1NyJ9.0d67c8b334048135a93615610445e4aa90c6af6222392b49eea9419e1d6717d0"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{airtable}</id>
|
||||
<secret>{airtable AQAAABAAA iKMJv6D1mmUvunFTZLfm4RrYhdrt5JCBMv.v1.r8IBnGw7b_vW0fl0MDJqPRUEsDdHtNYW9ANwPFm40V_M4knoEaulKL-5lmtWoRq9fjG-GORe8efob5e658nTiOkdYC.8a8d3}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"iKMJv6D1mmUvunFTZLfm4RrYhdrt5JCBMv.v1.r8IBnGw7b_vW0fl0MDJqPRUEsDdHtNYW9ANwPFm40V_M4knoEaulKL-5lmtWoRq9fjG-GORe8efob5e658nTiOkdYC.8a8d3"},
|
||||
},
|
||||
{
|
||||
name: "finds all matches",
|
||||
input: fmt.Sprintf(`airtable token 1 = '%s'
|
||||
airtabl token 2 = '%s'`, validPattern1, validPattern2),
|
||||
want: []string{validPattern1, validPattern2},
|
||||
input: `
|
||||
[INFO] Sending request to the airtable API
|
||||
[DEBUG] Using Key=oaajtCy2lVMUN1Cm5.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsImV4cGlyZXNBdCI6IjIwMjUtMDItMDNUMTk6NTY6MzcuMDAwWiIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwic2VjcmV0IjoiMzczNThlNzdlZjlhMjljY2Q5MWIwNmNlNTdkZDYxNDg0MWVmNmIyOWYwYjQ5ZWE0MTMxZGI4NzBkNTAzYTE1NyJ9.0d67c8b334048135a93615610445e4aa90c6af6222392b49eea9419e1d6717d0
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
[DEBUG] Using Key=oaaRYiYSlTFXZzxDM.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwiZXhwaXJlc0F0IjoiMjAyNS0wMS0yOVQwMDowMTo0NC4wMDBaIiwic2VjcmV0IjoiZjYyOWE1MWVkM2M0ZjU5ODlmOTcyMDU1ZjkwODk3NDA4NmU0NjQxY2JhODU5Y2FhZTJkZjliMWQwODg0ZjIzMiJ9.27a8998029ac9bdd599b435572821dcb63c60cbf62b9cb2ba2a73511e5553d66
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{
|
||||
"oaajtCy2lVMUN1Cm5.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsImV4cGlyZXNBdCI6IjIwMjUtMDItMDNUMTk6NTY6MzcuMDAwWiIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwic2VjcmV0IjoiMzczNThlNzdlZjlhMjljY2Q5MWIwNmNlNTdkZDYxNDg0MWVmNmIyOWYwYjQ5ZWE0MTMxZGI4NzBkNTAzYTE1NyJ9.0d67c8b334048135a93615610445e4aa90c6af6222392b49eea9419e1d6717d0",
|
||||
"oaaRYiYSlTFXZzxDM.v1.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwiZXhwaXJlc0F0IjoiMjAyNS0wMS0yOVQwMDowMTo0NC4wMDBaIiwic2VjcmV0IjoiZjYyOWE1MWVkM2M0ZjU5ODlmOTcyMDU1ZjkwODk3NDA4NmU0NjQxY2JhODU5Y2FhZTJkZjliMWQwODg0ZjIzMiJ9.27a8998029ac9bdd599b435572821dcb63c60cbf62b9cb2ba2a73511e5553d66",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("airtable token = '%s'", invalidPattern),
|
||||
want: []string{},
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airtable API
|
||||
[DEBUG] Using Key=oaaRYiYSlTFXZzxDM.v2.eyJ1c2VySWQiOiJ1c3JjQ09QVlJudGlrU1lzdyIsIm9hdXRoQXBwbGljYXRpb25JZCI6Im9hcG14aXcyUlRrVGlzcHJIIiwiZXhwaXJlc0F0IjoiMjAyNS0wMS0yOVQwMDowMTo0NC4wMDBaIiwic2VjcmV0IjoiZjYyOWE1MWVkM2M0ZjU5ODlmOTcyMDU1ZjkwODk3NDA4NmU0NjQxY2JhODU5Y2FhZTJkZjliMWQwODg0ZjIzMiJ9.27a8998029ac9bdd599b435572821dcb63c60cbf62b9cb2ba2a73511e5553d66
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -47,22 +71,15 @@ func TestAirtableoauth_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,6 +91,7 @@ func TestAirtableoauth_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -70,7 +70,7 @@ func verifyMatch(ctx context.Context, client *http.Client, token string) (bool,
|
||||
endpoint := "https://api.airtable.com/v0/meta/whoami"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return false, nil, nil
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
@@ -2,21 +2,15 @@ package airtablepersonalaccesstoken
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern1 = "patfqpIZBPU6EAt5x.458546d9c77b21f8a98141f2a4039d5626010f19efc16c20d57c4f41d44c8c85"
|
||||
validPattern2 = "pat0VXr5I2HcapZE8.da2606afb7d97e936719ec952a4a18b44045e385d4ddf4f38dcc246fb63f0165"
|
||||
invalidPattern = "tokfqpIZBPU6EAt5x.458546d9c77b21f8a98141f2a403-d5626010f19efc16c20d57c4f41d44c8c85"
|
||||
)
|
||||
|
||||
func TestAirtablepersonalaccesstoken_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,20 +20,50 @@ func TestAirtablepersonalaccesstoken_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "typical pattern",
|
||||
input: fmt.Sprintf("airtable token = '%s'", validPattern1),
|
||||
want: []string{validPattern1},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airtable API
|
||||
[DEBUG] Using Key=patfqpIZBPU6EAt5x.458546d9c77b21f8a98141f2a4039d5626010f19efc16c20d57c4f41d44c8c85
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"patfqpIZBPU6EAt5x.458546d9c77b21f8a98141f2a4039d5626010f19efc16c20d57c4f41d44c8c85"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{airtable}</id>
|
||||
<secret>{airtable AQAAABAAA pat2kATFGrujqJTbT.e2082656c470902d83b47dc804e693df1deb30161affbda39d879a2cf44bef13}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"pat2kATFGrujqJTbT.e2082656c470902d83b47dc804e693df1deb30161affbda39d879a2cf44bef13"},
|
||||
},
|
||||
{
|
||||
name: "finds all matches",
|
||||
input: fmt.Sprintf(`airtable token 1 = '%s'
|
||||
airtable token 2 = '%s'`, validPattern1, validPattern2),
|
||||
want: []string{validPattern1, validPattern2},
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using airtable Key=patfqpIZBPU6EAt5x.458546d9c77b21f8a98141f2a4039d5626010f19efc16c20d57c4f41d44c8c85
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
[DEBUG] Using airtable Key=pat0VXr5I2HcapZE8.da2606afb7d97e936719ec952a4a18b44045e385d4ddf4f38dcc246fb63f0165
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{
|
||||
"patfqpIZBPU6EAt5x.458546d9c77b21f8a98141f2a4039d5626010f19efc16c20d57c4f41d44c8c85",
|
||||
"pat0VXr5I2HcapZE8.da2606afb7d97e936719ec952a4a18b44045e385d4ddf4f38dcc246fb63f0165",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("airtable token = '%s'", invalidPattern),
|
||||
want: []string{},
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airtable API
|
||||
[DEBUG] Using Key=patfqpIZBPU6EAt5xe.458546d9c77b21f8a98141f2a403-d5626010f19efc16c20d57c4f41d44c8c85
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -47,22 +71,15 @@ func TestAirtablepersonalaccesstoken_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,6 +91,7 @@ func TestAirtablepersonalaccesstoken_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package airvisual
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "qscgyygcsq-wdvvok7slklklaasnd8afafxd"
|
||||
invalidPattern = "wdvvok7slklklaasnd8afafxd"
|
||||
)
|
||||
|
||||
func TestAirVisual_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAirVisual_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("airvisual = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airvisual API
|
||||
[DEBUG] Using Key=qscgyygcsq-wdvvok7slklklaasnd8afafxd
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"qscgyygcsq-wdvvok7slklklaasnd8afafxd"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("airvisual keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{airvisual}</id>
|
||||
<secret>{airvisual AQAAABAAA rtcbsxiee3d5au8ik14g-8iqrsu8thl1pku8}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"rtcbsxiee3d5au8ik14g-8iqrsu8thl1pku8"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("airvisual key: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] airvisual api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=qscgyygcsq-wdvvok7slklklaasnd8afafxd
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the airvisual API
|
||||
[DEBUG] Using Key=wdvvok7slklklaasnd8afafxd
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAirVisual_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAirVisual_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package aiven
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = `ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef123/+==ABCdef12`
|
||||
invalidPattern = `ABCdef123`
|
||||
)
|
||||
|
||||
func TestAiven_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAiven_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("aiven = '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the aiven API
|
||||
[DEBUG] Using Key = yb+Ygm82FfUworm2exB+Uk255p0uQKmmfx4ut1KfsZ3YI3Gp2xPYyxZgrwYabMxXXO4WPsK7xlLJRy0BWIpM2SKnzA2p69P8aOmYbl24ZiVGlLXyQxeVDDy7gru5Yzt=Y1UDLBpsW=hhGIKsrPgc/7hpxuEfEqbXJe5IBYO484F+ekaTmYN4nTF94O==3WuG+WuSW7zaYzXH1V==kZFj07zBtmShS0z/lW=N3HipH=oJjXI2pyFxU+A7vM9yHdUHoiZEOVoWsyp5zO1ajBOqFr=3jIIaXWmbH33dP2ZNQFJhqbeg6JlXA9GpfMFht5=ZCC1IirWCNp=UILbmZtvu9d2M8U0YNHwAGKtjrPS5lZvAU+W5s2Ti
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"yb+Ygm82FfUworm2exB+Uk255p0uQKmmfx4ut1KfsZ3YI3Gp2xPYyxZgrwYabMxXXO4WPsK7xlLJRy0BWIpM2SKnzA2p69P8aOmYbl24ZiVGlLXyQxeVDDy7gru5Yzt=Y1UDLBpsW=hhGIKsrPgc/7hpxuEfEqbXJe5IBYO484F+ekaTmYN4nTF94O==3WuG+WuSW7zaYzXH1V==kZFj07zBtmShS0z/lW=N3HipH=oJjXI2pyFxU+A7vM9yHdUHoiZEOVoWsyp5zO1ajBOqFr=3jIIaXWmbH33dP2ZNQFJhqbeg6JlXA9GpfMFht5=ZCC1IirWCNp=UILbmZtvu9d2M8U0YNHwAGKtjrPS5lZvAU+W5s2Ti"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("aiven keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{aiven}</id>
|
||||
<secret>{aiven AQAAABAAA IGhXNR6g7rogABp/H2iDQu7TgkXpvn9KnwzJfeh+8p7M=JVsI2QoQ38mmQHt450bQC4wBOGFhV+9QT2KGWSMfTOxTUrUXygaLlwsXo/RBxKXyOdh=/L8EGGrqG6=qbd0UzDAfc0xeAfXd30RGj+Ypsrrvdda=ZPa32BBID5r2ClfJSbgpfWIpVC1b5vlqCdy5LIWABZJzjBC5VweqZ04XFaCh+15NuSQ4E0KdGwPdkrfxxjY20I1wDvlKxzxL7dfCly3KVlQv7KBEFSLaLRNRocPYToUXqU4yAXKvXf03K=k1mahpxFUp94c35k/n055LVs=xbyL6AKdW=sCCa1AFIYKBDMBprTsZ6Al7DHx=XA6qLNWYxS7}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"IGhXNR6g7rogABp/H2iDQu7TgkXpvn9KnwzJfeh+8p7M=JVsI2QoQ38mmQHt450bQC4wBOGFhV+9QT2KGWSMfTOxTUrUXygaLlwsXo/RBxKXyOdh=/L8EGGrqG6=qbd0UzDAfc0xeAfXd30RGj+Ypsrrvdda=ZPa32BBID5r2ClfJSbgpfWIpVC1b5vlqCdy5LIWABZJzjBC5VweqZ04XFaCh+15NuSQ4E0KdGwPdkrfxxjY20I1wDvlKxzxL7dfCly3KVlQv7KBEFSLaLRNRocPYToUXqU4yAXKvXf03K=k1mahpxFUp94c35k/n055LVs=xbyL6AKdW=sCCa1AFIYKBDMBprTsZ6Al7DHx=XA6qLNWYxS7"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("aiven = '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] aiven api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=yb+Ygm82FfUworm2exB+Uk255p0uQKmmfx4ut1KfsZ3YI3Gp2xPYyxZgrwYabMxXXO4WPsK7xlLJRy0BWIpM2SKnzA2p69P8aOmYbl24ZiVGlLXyQxeVDDy7gru5Yzt=Y1UDLBpsW=hhGIKsrPgc/7hpxuEfEqbXJe5IBYO484F+ekaTmYN4nTF94O==3WuG+WuSW7zaYzXH1V==kZFj07zBtmShS0z/lW=N3HipH=oJjXI2pyFxU+A7vM9yHdUHoiZEOVoWsyp5zO1ajBOqFr=3jIIaXWmbH33dP2ZNQFJhqbeg6JlXA9GpfMFht5=ZCC1IirWCNp=UILbmZtvu9d2M8U0YNHwAGKtjrPS5lZvAU+W5s2Ti
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the aiven API
|
||||
[DEBUG] Using Key=SSs8PGhwqWzb4qfqiwLV/bNHfiQ2VSKyX88AAYm3+CGHbTe/FYXRNOYYHO=PXwuL/GftiES7j8ffzWW9p1dAyNc6hZZpoazmd+Vf1kbukZSL8QO/LdKFI/YFlupu0dELqQVHeZi/cJlnp6aQeY7zIJiHhJS51ZVdOamc=zOUMebry3BYOo2LhYIz+mLND7s5/cHZZpkEvTXrKnVf4vdYMl+fawv84AYCTo9pry8FQBsqRex2HL98kAiqhVYG+nLyRz/hZCo8owaRkzli1BUT4O63TSKJIgnECOBvyZz7o+yX92BhDe+B2Tllk3y2=qG5TiEl2sCJI8V5GJ1cz52RpXx2hVXMi=1Zl5CHpX8Adr9VMbj$Co
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAiven_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAiven_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
// Use identifiers in the secret preferably, or the provider name.
|
||||
func (s Scanner) Keywords() []string {
|
||||
return []string{"alchemy","alcht_"}
|
||||
return []string{"alchemy", "alcht_"}
|
||||
}
|
||||
|
||||
// FromData will find and optionally verify Alchemy secrets in a given set of bytes.
|
||||
@@ -68,7 +68,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, map[string]string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://eth-mainnet.g.alchemy.com/v2/"+token+"/getNFTs/?owner=vitalik.eth", nil)
|
||||
if err != nil {
|
||||
return false, nil, nil
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
|
||||
@@ -2,10 +2,13 @@ package alchemy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAlchemy_Pattern(t *testing.T) {
|
||||
@@ -17,20 +20,47 @@ func TestAlchemy_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "typical pattern",
|
||||
input: "alchemy_token = '3aBcDFE5678901234567890_1a2b3c4d'",
|
||||
want: []string{"3aBcDFE5678901234567890_1a2b3c4d"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the alchemy API
|
||||
[DEBUG] Using Key=alcht_2Cy8xCLyvrAf7lZKfhQhyCr4RAID9D
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"alcht_2Cy8xCLyvrAf7lZKfhQhyCr4RAID9D"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{alchemy}</id>
|
||||
<secret>{alchemy AQAAABAAA 5iqW7gKQVXvwnykF9xAVfenemmnUJznI}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"5iqW7gKQVXvwnykF9xAVfenemmnUJznI"},
|
||||
},
|
||||
{
|
||||
name: "finds all matches",
|
||||
input: `alchemy_token1 = '3aBcDFE5678901234567890_1a2b3c4d'
|
||||
alchemy_token2 = '3aDcDFE56789012245678a0_1a2b3c2d'`,
|
||||
want: []string{"3aBcDFE5678901234567890_1a2b3c4d", "3aDcDFE56789012245678a0_1a2b3c2d"},
|
||||
input: `
|
||||
[INFO] Sending request to the alchemy API
|
||||
[DEBUG] Using Key=alcht_2Cy8xCLyvrAf7lZKfhQhyCr4RAID9D
|
||||
[ERROR] Response received 401 UnAuthorized
|
||||
[DEBUG] Using alchemy Key=xuQIeWFVEp8k8Uu9FwPx6X5C8IViOe1o
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"alcht_2Cy8xCLyvrAf7lZKfhQhyCr4RAID9D", "xuQIeWFVEp8k8Uu9FwPx6X5C8IViOe1o"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: "alchemy_token = '1a2b3c4d'",
|
||||
want: []string{},
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the alchemy API
|
||||
[DEBUG] Using Key=alcht_a2Cy8xCLyvrAf7lZKfhQhyCr4RAID9D
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -38,22 +68,15 @@ alchemy_token2 = '3aDcDFE56789012245678a0_1a2b3c2d'`,
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -65,6 +88,7 @@ alchemy_token2 = '3aDcDFE56789012245678a0_1a2b3c2d'`,
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package alconost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "wdvnousa87acfxp9ioasrea4tbeasrfa"
|
||||
invalidPattern = "wdvnousa87acfxp9ioasr$a4tBeasrfa"
|
||||
)
|
||||
|
||||
func TestAlconost_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAlconost_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("alconost: '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the alconost API
|
||||
[DEBUG] Using Key=wdvnousa87acfxp9ioasrea4tbeasrfa
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"wdvnousa87acfxp9ioasrea4tbeasrfa"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("alconost keyword is not close to the real key in the data = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{alconost}</id>
|
||||
<secret>{alconost AQAAABAAA Awxzhkwff46dtkt5pnvdlss6t2kA44a7}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"Awxzhkwff46dtkt5pnvdlss6t2kA44a7"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("alconost: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] alconost api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=wdvnousa87acfxp9ioasrea4tbeasrfa
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the alconost API
|
||||
[DEBUG] Using Key=wdvnousa87acfxp9ioasra4tBeasrfa
|
||||
[INFO] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAlconost_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAlconost_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,21 +2,15 @@ package alegra
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "wdvn-usa87a-fxp9ioas/[email protected]"
|
||||
validSpecialCharPattern = "wdvn-usa87a-fxp9ioas / [email protected]"
|
||||
invalidPattern = "wdvn-usa87a-fxp9ioasQQsstestUsQQ@example"
|
||||
)
|
||||
|
||||
func TestAlegra_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -27,43 +21,65 @@ func TestAlegra_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("alegra: %s", validPattern),
|
||||
want: []string{"wdvn-usa87a-fxp9ioas:wdvn-usa87a-fxp9ioas/[email protected]"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using alegra Key=wdvn-usa87a-fxp9ioas
|
||||
[DEBUG] Using alegra Email = testUser.1005@example.com
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"wdvn-usa87a-fxp9ioas:[email protected]"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - with special characters",
|
||||
input: fmt.Sprintf("alegra: %s", validSpecialCharPattern),
|
||||
want: []string{"wdvn-usa87a-fxp9ioas:[email protected]"},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{alegra kk18@example.com}</id>
|
||||
<secret>{alegra AQAAABAAA buihlmkfnh5m1lk5z6do}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"buihlmkfnh5m1lk5z6do:[email protected]"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("alegra keyword is not close to the real key and id = %s", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] alegra api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=wdvn-usa87a-fxp9ioas
|
||||
[DEBUG] Using Email=testUser.1005@example.com
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("alegra: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using alegra Key=wdvn_usa87a-fxp9ioas
|
||||
[DEBUG] Using alegra Email=testUser.1005@example.com
|
||||
[INFO] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 && test.want != nil {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
t.Errorf("expected %d results, got %d", len(test.want), len(results))
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,6 +91,7 @@ func TestAlegra_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package aletheiaapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "TEST01KEYPATTERN2024ALETHEIAAPIN"
|
||||
invalidPattern = "TEST01kEYPATTERN2024ALETHEIAAPIN"
|
||||
)
|
||||
|
||||
func TestAleTheIaAPI_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAleTheIaAPI_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("aletheiaapi: '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the aletheiaapi
|
||||
[DEBUG] Using Key=LY027C40U2KNNZLFO1WEU3XQZ13LW515
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"LY027C40U2KNNZLFO1WEU3XQZ13LW515"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("aletheiaapi keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{aletheiaapi}</id>
|
||||
<secret>{aletheiaapi AQAAABAAA K7SOW2B8QH9QE435NLH07PH22XL4YOPG}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"K7SOW2B8QH9QE435NLH07PH22XL4YOPG"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("aletheiaapi: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] aletheiaapi api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=LY027C40U2KNNZLFO1WEU3XQZ13LW515
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the aletheiaapi
|
||||
[DEBUG] Using Key=LY027c40U2KNNZLFO1WEU3XQZ13LW515
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAleTheIaAPI_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAleTheIaAPI_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -3,14 +3,18 @@ package algoliaadminkey
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/cache/simple"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
@@ -28,6 +32,10 @@ var (
|
||||
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
|
||||
idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"algolia", "docsearch", "appId"}) + `\b([A-Z0-9]{10})\b`)
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"algolia", "docsearch", "apiKey"}) + `\b([a-zA-Z0-9]{32})\b`)
|
||||
|
||||
invalidHosts = simple.NewCache[struct{}]()
|
||||
|
||||
errNoHost = errors.New("no such host")
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
@@ -38,6 +46,7 @@ func (s Scanner) Keywords() []string {
|
||||
|
||||
// FromData will find and optionally verify AlgoliaAdminKey secrets in a given set of bytes.
|
||||
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
logger := logContext.AddLogger(ctx).Logger().WithName("algoliaadminkey")
|
||||
dataStr := string(data)
|
||||
|
||||
// Deduplicate matches.
|
||||
@@ -59,6 +68,12 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
// Test matches.
|
||||
for key := range keyMatches {
|
||||
for id := range idMatches {
|
||||
if invalidHosts.Exists(id) {
|
||||
logger.V(3).Info("Skipping application id: no such host", "host", id)
|
||||
delete(idMatches, id)
|
||||
continue
|
||||
}
|
||||
|
||||
r := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AlgoliaAdminKey,
|
||||
Raw: []byte(key),
|
||||
@@ -70,7 +85,14 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
isVerified, extraData, verificationErr := verifyMatch(ctx, id, key)
|
||||
r.Verified = isVerified
|
||||
r.ExtraData = extraData
|
||||
r.SetVerificationError(verificationErr, key)
|
||||
if verificationErr != nil {
|
||||
if errors.Is(verificationErr, errNoHost) {
|
||||
invalidHosts.Set(id, struct{}{})
|
||||
continue
|
||||
}
|
||||
|
||||
r.SetVerificationError(verificationErr, key)
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, r)
|
||||
@@ -101,6 +123,11 @@ func verifyMatch(ctx context.Context, appId, apiKey string) (bool, map[string]st
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
// lookup xyz.algolia.net: no such host
|
||||
if strings.Contains(err.Error(), "no such host") {
|
||||
return false, nil, errNoHost
|
||||
}
|
||||
|
||||
return false, nil, err
|
||||
}
|
||||
defer func() {
|
||||
|
||||
@@ -2,20 +2,15 @@ package algoliaadminkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "AlgoliaAdminKey010TESTINGPlmiOPC/ADMIN01KEY"
|
||||
invalidPattern = "AlgoliaAdminN$TKey010TESTINGPlmiOPC/NonAdmin01ID"
|
||||
)
|
||||
|
||||
func TestAlgoliaAdminKey_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,48 @@ func TestAlgoliaAdminKey_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("algolia: '%s'", validPattern),
|
||||
want: []string{"ADMIN01KEY:AlgoliaAdminKey010TESTINGPlmiOPC"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using algolia Key=BsDaN7ZU7kFiUX5CpN8CUf3nkMaSeZYn
|
||||
[DEBUG] Using docsearch ID=844XQV5SUA
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"844XQV5SUA:BsDaN7ZU7kFiUX5CpN8CUf3nkMaSeZYn"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("algolia keyword is not close to the real key and secret = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{appId 0VJ9I1WV78}</id>
|
||||
<secret>{algolia AQAAABAAA 4AYm3wz7nfnX7Bqtw5e5Qo3Z5vfBe0eS}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"0VJ9I1WV78:4AYm3wz7nfnX7Bqtw5e5Qo3Z5vfBe0eS"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("algolia: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to the algolia API
|
||||
[DEBUG] Using Key=BsDaN7ZU7kFiUX5CpN8CUf3nkMaSeZYn
|
||||
[DEBUG] Using ID=844XQV5SUA
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using algolia Key=BsD-N7ZU7kFiUX5CpN8CUf3nkMaSeZYn
|
||||
[DEBUG] Using docsearch ID=844XqV5SUA
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +70,15 @@ func TestAlgoliaAdminKey_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +90,7 @@ func TestAlgoliaAdminKey_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package alibaba
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "abcDEF123ghiJKL456mnoPQR789std/LTAI123ABCdef456ghijkLMN"
|
||||
invalidPattern = "abcDEF123ghiJKL456m$oPQR789/123ABCdef456ghijkLMN;"
|
||||
)
|
||||
|
||||
func TestAliBaba_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,48 @@ func TestAliBaba_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("LTAI: '%s'", validPattern),
|
||||
want: []string{"abcDEF123ghiJKL456mnoPQR789stdLTAI123ABCdef456ghijkLMN"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=CwgR2UwgaWd7hgUdQkwFnK9vvEeO4R
|
||||
[DEBUG] Using ID=LTAIXgRPqwF1DhBf6Q1uZ5DrM
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"CwgR2UwgaWd7hgUdQkwFnK9vvEeO4RLTAIXgRPqwF1DhBf6Q1uZ5DrM"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - ignore special characters at end",
|
||||
input: fmt.Sprintf("LTAI: '%s\"''", validPattern),
|
||||
want: []string{"abcDEF123ghiJKL456mnoPQR789stdLTAI123ABCdef456ghijkLMN"},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{WX6OtM8pbcrXWMIGc5evYousFWBlBm}</id>
|
||||
<secret>{AQAAABAAA LTAImg3ZeAPatbAtEDS9HVZ}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"WX6OtM8pbcrXWMIGc5evYousFWBlBmLTAImg3ZeAPatbAtEDS9HVZ"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("LTAI: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - ignore special characters at end",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=CwgR2UwgaWd7hgUdQkwFnK9vvEeO4R
|
||||
[DEBUG] Using ID=LTAIXgRPqwF1DhBf6Q1uZ5DrM;
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"CwgR2UwgaWd7hgUdQkwFnK9vvEeO4RLTAIXgRPqwF1DhBf6Q1uZ5DrM"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=CwgR2UwgaWd7hgUdQkwFnK9vvEeO4
|
||||
[DEBUG] Using ID=LTAIXgRPqwF1DhBf6Q1uZ5DrMYPW
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +70,15 @@ func TestAliBaba_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +90,7 @@ func TestAliBaba_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package alienvault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "a3b9f8c1e2d4f5b6c7d8e9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0"
|
||||
invalidPattern = "A$3b9f8c1e2d4f5b6c7d8e9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d"
|
||||
)
|
||||
|
||||
func TestAlienVault_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAlienVault_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("alienvault: '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the alienvault API
|
||||
[DEBUG] Using Key=3em7p52ec9ut4k9ccqha19rz3oyeqnij3mn3ivml577f8pb2179yz9totr648hmy
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"3em7p52ec9ut4k9ccqha19rz3oyeqnij3mn3ivml577f8pb2179yz9totr648hmy"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("alienvault keyword is not close to the real key in the data\n = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{alienvault}</id>
|
||||
<secret>{AQAAABAAA xyi7bj56t5b0hkinw4vz8qgffqhfb2ypemdnt407bke6s0ouuswvcdf5c1qpvse0}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"xyi7bj56t5b0hkinw4vz8qgffqhfb2ypemdnt407bke6s0ouuswvcdf5c1qpvse0"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("alienvault: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[INFO] Fetching data from alienvault
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=3em7p52ec9ut4k9ccqha19rz3oyeqnij3mn3ivml577f8pb2179yz9totr648hmy
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the alienvault API
|
||||
[DEBUG] Using Key=3em7p52ec9ut4k9ccqha19rz3o_eqnij3mn3ivml577f8pb2179yz9totr648hmy
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAlienVault_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAlienVault_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package allsports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "d1f2e3c4b5a6d7e8f9g0h1i2j3k4l5m6n7o8p9q0r1s2t3u4v5w6x7y8z9a0b1ce"
|
||||
invalidPattern = "Ad1f2e3c4b5a6d7e8f9g0h1i2j3k4l5m6n7o8p9q0r1sRt3u4v5w6x7y8z9a0b1cE"
|
||||
)
|
||||
|
||||
func TestAllSports_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAllSports_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("allsports: '%s'", validPattern),
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the allsports API
|
||||
[DEBUG] Using Key=cq73u5azj3p3shfvzz3lw1typfqu6uduq7bophtq4veta7cnvd4s5htkb8lgk4vr
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"cq73u5azj3p3shfvzz3lw1typfqu6uduq7bophtq4veta7cnvd4s5htkb8lgk4vr"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("allsports keyword is not close to the real key in the data\n = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{allsports}</id>
|
||||
<secret>{AQAAABAAA bj8yzu3awie5akwiwcb7esqygqx14gt65j9lrcpec0v28ckkswtyza1x9747gap5}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"bj8yzu3awie5akwiwcb7esqygqx14gt65j9lrcpec0v28ckkswtyza1x9747gap5"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("allsports: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[DEBUG] allsports api processing
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=cq73u5azj3p3shfvzz3lw1typfqu6uduq7bophtq4veta7cnvd4s5htkb8lgk4vr
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the allsports API
|
||||
[DEBUG] Using Key=d1f2e3c4b5a6d7e8f9G0h1i2j3k4l5m6n7o8p9q0r1s2t3u4v5w6x7y8z9a0b1ce
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAllSports_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAllSports_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package amadeus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6 / A1b2C3d4E5f6G7ho"
|
||||
invalidPattern = "A1b2$3d4E5f6G7h8I9j(K1l2M3n4O5)6 / A1b2C3d4E^f6G7#8"
|
||||
)
|
||||
|
||||
func TestAmadeus_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,48 @@ func TestAmadeus_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("amadeus: '%s'", validPattern),
|
||||
want: []string{"A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6A1b2C3d4E5f6G7ho"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using amadeus Key=ttdveNai3Gj6Zrjvgz4fyBEWRLARCG6a
|
||||
[DEBUG] Using amadeus Secret=9wqrSr2qveaqgQns
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"ttdveNai3Gj6Zrjvgz4fyBEWRLARCG6a9wqrSr2qveaqgQns"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("amadeus keyword is not close to the real key in the data\n = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{amadeus ey6U46qCx26dqzMVWAGiibt6m65mM5w9}</id>
|
||||
<secret>{amadeus AQAAABAAA Ew3TfmLHYaRjPnYO}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"ey6U46qCx26dqzMVWAGiibt6m65mM5w9Ew3TfmLHYaRjPnYO"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("amadeus: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[INFO] Sending request to the amadeus API
|
||||
[DEBUG] Using Key=ttdveNai3Gj6Zrjvgz4fyBEWRLARCG6a
|
||||
[DEBUG] Using Secret=9wqrSr2qveaqgQns
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the amadeus API
|
||||
[DEBUG] Using amadeus Key=tthdveNai3Gj6Zrjvgz4fyBEWRLARCG6a
|
||||
[DEBUG] Using amadeus Secret=9wqrSr2qveacqgQns
|
||||
[INFO] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +70,15 @@ func TestAmadeus_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +90,7 @@ func TestAmadeus_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package ambee
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4"
|
||||
invalidPattern = "X3b4c5d6e7f8a9b0c1d2e3f4a5b6c7Z8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4"
|
||||
)
|
||||
|
||||
func TestAmbee_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,19 +21,46 @@ func TestAmbee_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("ambee: '%s'", validPattern),
|
||||
want: []string{"a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the ambee API
|
||||
[DEBUG] Using Key=eccb41cc2d4dab96b748ed040e9b308161279820447ef4553ba6e6d20ecb9962
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"eccb41cc2d4dab96b748ed040e9b308161279820447ef4553ba6e6d20ecb9962"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("ambee keyword is not close to the real key in the data\n = '%s'", validPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{ambee}</id>
|
||||
<secret>{ambee AQAAABAAA b91280c63e1571ad928d52947cc31a14ad1bf5a83088d0346b94f6683cf22138}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"b91280c63e1571ad928d52947cc31a14ad1bf5a83088d0346b94f6683cf22138"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("ambee: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: `
|
||||
[INFO] Fetching data from ambee
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=eccb41cc2d4dab96b748ed040e9b308161279820447ef4553ba6e6d20ecb9962
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the ambee API
|
||||
[DEBUG] Using Key=eccb41cc2d4dab96y748ed040e9b308161279820447ef4553ba6e6d20ecb9962
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +68,15 @@ func TestAmbee_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +88,7 @@ func TestAmbee_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,26 +2,15 @@ package amplitudeapikey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = `
|
||||
amplitude key = 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
|
||||
amplitude secret = 5b3c4d5e5f7a8b9c0d1e2f2f3a4b4c6e
|
||||
`
|
||||
invalidPattern = `
|
||||
amplitude key = 1a2b3c4d5e6f7a8g9c0d1e2f3a4b5c6d
|
||||
amplitude secret = 5b3c4d5e5f7a8b9c0d1r2f2f3a4b4c6e
|
||||
`
|
||||
)
|
||||
|
||||
func TestAmplitudeAPIKey_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -32,17 +21,44 @@ func TestAmplitudeAPIKey_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("amplitude: '%s'", validPattern),
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using amplitude Key=c2167730016af34b89e200ecf55710e8
|
||||
[DEBUG] Using amplitude Secret=5488620aa9073c09f1a16e2b1dc357b6
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{
|
||||
"1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d5b3c4d5e5f7a8b9c0d1e2f2f3a4b4c6e",
|
||||
"5b3c4d5e5f7a8b9c0d1e2f2f3a4b4c6e1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d",
|
||||
"c2167730016af34b89e200ecf55710e85488620aa9073c09f1a16e2b1dc357b6",
|
||||
"5488620aa9073c09f1a16e2b1dc357b6c2167730016af34b89e200ecf55710e8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("amplitude: '%s'", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{amplitude aac639f65d80ec2eec96e775f598ce13}</id>
|
||||
<secret>{amplitude AQAAABAAA 8ac62041353622f9c5e4657807ff1eac}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{
|
||||
"aac639f65d80ec2eec96e775f598ce138ac62041353622f9c5e4657807ff1eac",
|
||||
"8ac62041353622f9c5e4657807ff1eacaac639f65d80ec2eec96e775f598ce13",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using amplitude Key=c2167730016rf34b89e200ecf55710e8
|
||||
[DEBUG] Using amplitude Secret=5488620aa9073q09f1a16e2b1dc357b6
|
||||
[INFO] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -50,22 +66,15 @@ func TestAmplitudeAPIKey_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,6 +86,7 @@ func TestAmplitudeAPIKey_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,26 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = `
|
||||
System Log - Authentication Token Issued
|
||||
Date: 2025-02-04 14:32:10 UTC
|
||||
Server: api-secure-03.internal
|
||||
Service: Anthropic API Gateway
|
||||
API Key: sk-ant-api03-abc123xyz-456def789ghij-klmnopqrstuvwx-3456yza789bcde-1234fghijklmnopby56aaaogaopaaaabc123xyzAA
|
||||
Admin Key: sk-ant-admin01-abc12fake-456def789ghij-klmnopqrstuvwx-3456yza789bcde-12fakehijklmnopby56aaaogaopaaaabc123xyzAA
|
||||
|
||||
Log Entry:
|
||||
A new API and Admin key has been generated for service authentication. Please ensure that this key remains confidential and is not exposed in any public repositories or logs.
|
||||
`
|
||||
invalidPattern = "sk-ant-api03-abc123xyz-456de-klMnopqrstuvwx-3456yza789bcde-1234fghijklmnopAA"
|
||||
)
|
||||
|
||||
func TestAnthropic_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -35,17 +21,50 @@ func TestAnthropic_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
System Log - Authentication Token Issued
|
||||
Date: 2025-02-04 14:32:10 UTC
|
||||
Server: api-secure-03.internal
|
||||
Service: Anthropic API Gateway
|
||||
API Key: sk-ant-api03-abc123xyz-456def789ghij-klmnopqrstuvwx-3456yza789bcde-1234fghijklmnopby56aaaogaopaaaabc123xyzAA
|
||||
Admin Key: sk-ant-admin01-abc12fake-456def789ghij-klmnopqrstuvwx-3456yza789bcde-12fakehijklmnopby56aaaogaopaaaabc123xyzAA
|
||||
|
||||
Log Entry:
|
||||
A new API and Admin key has been generated for service authentication. Please ensure that this key remains confidential and is not exposed in any public repositories or logs.
|
||||
`,
|
||||
want: []string{
|
||||
"sk-ant-api03-abc123xyz-456def789ghij-klmnopqrstuvwx-3456yza789bcde-1234fghijklmnopby56aaaogaopaaaabc123xyzAA",
|
||||
"sk-ant-admin01-abc12fake-456def789ghij-klmnopqrstuvwx-3456yza789bcde-12fakehijklmnopby56aaaogaopaaaabc123xyzAA",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{anthropic}</id>
|
||||
<secret>{AQAAABAAA sk-ant-api03-Dtjm9IZ_rYhS_ihHLZmPXhjJ6PN8UPp7vNO7qO3735RRDpf8xbWGinsch0McONXznUm-4KWoA7WU2otvvwHBR5QRjiLakAA}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"sk-ant-api03-Dtjm9IZ_rYhS_ihHLZmPXhjJ6PN8UPp7vNO7qO3735RRDpf8xbWGinsch0McONXznUm-4KWoA7WU2otvvwHBR5QRjiLakAA"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
System Log - Authentication Token Issued
|
||||
Date: 2025-02-04 14:32:10 UTC
|
||||
Server: api-secure-03.internal
|
||||
Service: Anthropic API Gateway
|
||||
API Key: sk-ant-api03-abc123xyz-456de-klMnopqrstuvwx-3456yza789bcde-1234fghijklmnopAA
|
||||
|
||||
Log Entry:
|
||||
A new API key has been generated for service authentication. Please ensure that this key remains confidential and is not exposed in any public repositories or logs.
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -53,22 +72,15 @@ func TestAnthropic_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,6 +92,7 @@ func TestAnthropic_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,41 +2,15 @@ package anypoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6 / org: abcd1234-ef56-gh78-ij90-klmn1234opqr"
|
||||
complexPattern = `
|
||||
# Secret Configuration File
|
||||
# Organization details
|
||||
ORG_NAME=my_organization
|
||||
ORG_ID=abcd1234-ef56-gh78-ij90-klmn1234opqr
|
||||
|
||||
# Database credentials
|
||||
DB_USERNAME=iamnotadmin
|
||||
DB_PASSWORD=8f3b6d3e7c9a2f5e
|
||||
|
||||
# OAuth tokens
|
||||
ACCESS_TOKEN=abcxyz123
|
||||
REFRESH_TOKEN=zyxwvutsrqponmlkji9876543210abcd
|
||||
|
||||
# API keys
|
||||
API_KEY=sk-ant-api03-nothing-just-some-random-api-key-1234fghijklmnopAA
|
||||
SECRET_KEY=1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6
|
||||
|
||||
# Endpoints
|
||||
SERVICE_URL=https://api.example.com/v1/resource
|
||||
`
|
||||
invalidPattern = "1a2b3C4d-5E6f-7g8H-9i0J-k1l2M3n4o5p6 / abcd1234-eF56-gH78-ij90-klmn1234opqr"
|
||||
)
|
||||
|
||||
func TestAnypoint_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -47,19 +21,58 @@ func TestAnypoint_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("anypoint credentials: %s", validPattern),
|
||||
want: []string{"1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6abcd1234-ef56-gh78-ij90-klmn1234opqr"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
# Anypoint Secret Configuration File
|
||||
# Organization details
|
||||
ORG_NAME=my_organization
|
||||
ORG_ID=abcd1234-ef56-gh78-ij90-klmn1234opqr
|
||||
|
||||
# OAuth tokens
|
||||
ACCESS_TOKEN=abcxyz123
|
||||
REFRESH_TOKEN=zyxwvutsrqponmlkji9876543210abcd
|
||||
|
||||
# API keys
|
||||
SECRET_KEY=1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6
|
||||
|
||||
# Endpoints
|
||||
SERVICE_URL=https://api.example.com/v1/resource
|
||||
`,
|
||||
want: []string{"1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6abcd1234-ef56-gh78-ij90-klmn1234opqr"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - complex",
|
||||
input: fmt.Sprintf("anypoint credentials: %s", complexPattern),
|
||||
want: []string{"1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6abcd1234-ef56-gh78-ij90-klmn1234opqr"},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{anypoint org rdogw4dd-6x3l-2nm3-jvl5-qi8dyheccgj7}</id>
|
||||
<secret>{AQAAABAAA 7jhlugw8-3tfb-7ju2-0i0y-7un6qxvknbvz}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"7jhlugw8-3tfb-7ju2-0i0y-7un6qxvknbvzrdogw4dd-6x3l-2nm3-jvl5-qi8dyheccgj7"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("anypoint credentials: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
# Anypoint Secret Configuration File
|
||||
# Organization details
|
||||
ORG_NAME=my_organization
|
||||
ORG_ID=abcd1234-ef56-gh78-ij90-klmn1234opqr
|
||||
|
||||
# OAuth tokens
|
||||
ACCESS_TOKEN=abcxyz123
|
||||
REFRESH_TOKEN=zyxwvutsrqponmlkji9876543210abcd
|
||||
|
||||
# API keys
|
||||
SECRET_KEY=1a2b3C4d-5E6f-7g8H-9i0J-k1l2M3n4o5p6
|
||||
|
||||
# Endpoints
|
||||
SERVICE_URL=https://api.example.com/v1/resource
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -67,22 +80,15 @@ func TestAnypoint_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -94,6 +100,7 @@ func TestAnypoint_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package anypointoauth2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
type Scanner struct {
|
||||
client *http.Client
|
||||
detectors.DefaultMultiPartCredentialProvider
|
||||
}
|
||||
|
||||
// Ensure the Scanner satisfies the interface at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
|
||||
var (
|
||||
defaultClient = common.SaneHttpClient()
|
||||
|
||||
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
|
||||
idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"anypoint", "id"}) + `\b([0-9a-f]{32})\b`)
|
||||
secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"anypoint", "secret"}) + `\b([0-9a-fA-F]{32})\b`)
|
||||
|
||||
verificationUrl = "https://anypoint.mulesoft.com/accounts/oauth2/token"
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
// Use identifiers in the secret preferably, or the provider name.
|
||||
func (s Scanner) Keywords() []string {
|
||||
return []string{"anypoint"}
|
||||
}
|
||||
|
||||
func (s Scanner) getClient() *http.Client {
|
||||
if s.client != nil {
|
||||
return s.client
|
||||
}
|
||||
return defaultClient
|
||||
}
|
||||
|
||||
// FromData will find and optionally verify Anypoint secrets in a given set of bytes.
|
||||
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
dataStr := string(data)
|
||||
|
||||
var uniqueIDs, uniqueSecrets = make(map[string]struct{}), make(map[string]struct{})
|
||||
|
||||
for _, matches := range idPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
uniqueIDs[matches[1]] = struct{}{}
|
||||
}
|
||||
|
||||
for _, matches := range secretPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
uniqueSecrets[matches[1]] = struct{}{}
|
||||
}
|
||||
|
||||
for id := range uniqueIDs {
|
||||
for secret := range uniqueSecrets {
|
||||
if id == secret {
|
||||
// Avoid processing the same string for both id and secret.
|
||||
continue
|
||||
}
|
||||
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_AnypointOAuth2,
|
||||
Raw: []byte(secret),
|
||||
RawV2: []byte(fmt.Sprintf("%s:%s", id, secret)),
|
||||
}
|
||||
|
||||
if verify {
|
||||
client := s.getClient()
|
||||
isVerified, verificationErr := verifyMatch(ctx, client, id, secret)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
|
||||
if s1.Verified {
|
||||
// Anypoint client IDs and secrets are mapped one-to-one, so if a pair
|
||||
// is verified, we can remove that secret from the uniqueSecrets map.
|
||||
delete(uniqueSecrets, secret)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func verifyMatch(ctx context.Context, client *http.Client, id, secret string) (bool, error) {
|
||||
payload := strings.NewReader(`{"grant_type":"client_credentials","client_id":"` + id + `","client_secret":"` + secret + `"}`)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, verificationUrl, payload)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
switch res.StatusCode {
|
||||
// The endpoint responds with status 200 for valid Organization credentials and 422 for Client credentials.
|
||||
case http.StatusOK, http.StatusUnprocessableEntity:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_AnypointOAuth2
|
||||
}
|
||||
|
||||
func (s Scanner) Description() string {
|
||||
return "Anypoint is a unified platform that allows organizations to build and manage APIs and integrations. Anypoint credentials can be used to access and manipulate these integrations and API data."
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//go:build detectors
|
||||
// +build detectors
|
||||
|
||||
package anypointoauth2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
func TestAnypointOAuth2_FromChunk(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors6")
|
||||
if err != nil {
|
||||
t.Fatalf("could not get test secrets from GCP: %s", err)
|
||||
}
|
||||
clientID := testSecrets.MustGetField("ANYPOINT_CLIENT_ID")
|
||||
clientSecret := testSecrets.MustGetField("ANYPOINT_CLIENT_SECRET")
|
||||
inactiveSecret := testSecrets.MustGetField("ANYPOINT_INACTIVE_SECRET")
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
data []byte
|
||||
verify bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
s Scanner
|
||||
args args
|
||||
want []detectors.Result
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "found, verified",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find an anypoint secret %s within anypoint organization id %s", clientSecret, clientID)),
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AnypointOAuth2,
|
||||
Verified: true,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "found, unverified",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(fmt.Sprintf("You can find an anypoint secret %s within anypoint organization id %s but not valid", inactiveSecret, clientID)), // the secret would satisfy the regex but not pass validation
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AnypointOAuth2,
|
||||
Verified: false,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte("You cannot find the secret within"),
|
||||
verify: true,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Scanner{}
|
||||
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Anypoint.FromData() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
for i := range got {
|
||||
if len(got[i].Raw) == 0 {
|
||||
t.Fatalf("no raw secret present: \n %+v", got[i])
|
||||
}
|
||||
gotErr := ""
|
||||
if got[i].VerificationError() != nil {
|
||||
gotErr = got[i].VerificationError().Error()
|
||||
}
|
||||
wantErr := ""
|
||||
if tt.want[i].VerificationError() != nil {
|
||||
wantErr = tt.want[i].VerificationError().Error()
|
||||
}
|
||||
if gotErr != wantErr {
|
||||
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.want[i].VerificationError(), got[i].VerificationError())
|
||||
}
|
||||
}
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError", "primarySecret")
|
||||
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
|
||||
t.Errorf("AnypointOAuth2.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromData(benchmark *testing.B) {
|
||||
ctx := context.Background()
|
||||
s := Scanner{}
|
||||
for name, data := range detectors.MustGetBenchmarkData() {
|
||||
benchmark.Run(name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package anypointoauth2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
func TestAnypoint_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
# Anypoint Secret Configuration File
|
||||
# Organization details
|
||||
ORG_NAME=my_organization
|
||||
ORG_ID=abcd1234-ef56-gh78-ij90-klmn1234opqr
|
||||
|
||||
# OAuth tokens
|
||||
CLIENT_ID=e3cd10a87f53b2dfa4b5fd606e7d9eca
|
||||
CLIENT_SECRET=ACE9d7E606Df5B4AFD2B35f78A01DC3E
|
||||
|
||||
# API keys
|
||||
SECRET_KEY=1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6
|
||||
|
||||
# Endpoints
|
||||
SERVICE_URL=https://api.example.com/v1/resource
|
||||
`,
|
||||
want: []string{"e3cd10a87f53b2dfa4b5fd606e7d9eca:ACE9d7E606Df5B4AFD2B35f78A01DC3E"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{anypoint id 17c55fba5c93de5646b10507c36fbc23}</id>
|
||||
<secret>{AQAAABAAA 8E6Ef8F8d5De05d8BF1491e1ecC37b31}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"17c55fba5c93de5646b10507c36fbc23:8E6Ef8F8d5De05d8BF1491e1ecC37b31"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
# Anypoint Secret Configuration File
|
||||
# Organization details
|
||||
ORG_NAME=my_organization
|
||||
ORG_ID=abcd1234-ef56-gh78-ij90-klmn1234opqr
|
||||
|
||||
# OAuth tokens
|
||||
CLIENT_ID=k4lzc5ty98tnfu3a11y8gnv5vb1281as
|
||||
CLIENT_SECRET=ACE9d7E606Df5B4AFD2B35f78A01DC3E
|
||||
|
||||
# API keys
|
||||
SECRET_KEY=1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6
|
||||
|
||||
# Endpoints
|
||||
SERVICE_URL=https://api.example.com/v1/resource
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
actual := make(map[string]struct{}, len(results))
|
||||
for _, r := range results {
|
||||
if len(r.RawV2) > 0 {
|
||||
actual[string(r.RawV2)] = struct{}{}
|
||||
} else {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||
t.Errorf("%s diff: (-want +got)\n%s", test.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,47 +2,15 @@ package apacta
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "abcd1234-ef56-gh78-ij90-klmn1234opqr"
|
||||
complexPattern = `
|
||||
func main() {
|
||||
url := "https://api.example.com/v1/resource"
|
||||
|
||||
// Create a new request with the secret as a header
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte("{}")))
|
||||
if err != nil {
|
||||
fmt.Println("Error creating request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
apactaSecret := "Bearer abcd1234-ef56-gh78-ij90-klmn1234opqr"
|
||||
req.Header.Set("Authorization", apactaSecret)
|
||||
|
||||
// Perform the request
|
||||
client := &http.Client{}
|
||||
resp, _ := client.Do(req)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Check response status
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
fmt.Println("Request successful!")
|
||||
} else {
|
||||
fmt.Println("Request failed with status:", resp.Status)
|
||||
}
|
||||
}
|
||||
`
|
||||
invalidPattern = "abcD$1234-ef56-gH78-ij90-klmn1234opqr"
|
||||
)
|
||||
|
||||
func TestApacta_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -53,19 +21,62 @@ func TestApacta_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("apacta credentials: %s", validPattern),
|
||||
want: []string{"abcd1234-ef56-gh78-ij90-klmn1234opqr"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func main() {
|
||||
// Create a new request with the secret as a header
|
||||
req, err := http.NewRequest("POST", "https://api.example.com/v1/resource", bytes.NewBuffer([]byte("{}")))
|
||||
if err != nil {
|
||||
fmt.Println("Error creating request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
apactaSecret := "Bearer abcd1234-ef56-gh78-ij90-klmn1234opqr"
|
||||
req.Header.Set("Authorization", apactaSecret)
|
||||
|
||||
// Perform the request
|
||||
client := &http.Client{}
|
||||
resp, _ := client.Do(req)
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
`,
|
||||
want: []string{"abcd1234-ef56-gh78-ij90-klmn1234opqr"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - complex",
|
||||
input: complexPattern,
|
||||
want: []string{"abcd1234-ef56-gh78-ij90-klmn1234opqr"},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apacta}</id>
|
||||
<secret>{AQAAABAAA w8-p59rc70q0unyupknadu5sr8bf5us04mpt}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"w8-p59rc70q0unyupknadu5sr8bf5us04mpt"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("apacta credentials: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func main() {
|
||||
// Create a new request with the secret as a header
|
||||
req, err := http.NewRequest("POST", "https://api.example.com/v1/resource", bytes.NewBuffer([]byte("{}")))
|
||||
if err != nil {
|
||||
fmt.Println("Error creating request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
apactaSecret := "Bearer abcD$1234-ef56-gH78-ij90-klmn1234opqr"
|
||||
req.Header.Set("Authorization", apactaSecret)
|
||||
|
||||
// Perform the request
|
||||
client := &http.Client{}
|
||||
resp, _ := client.Do(req)
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -73,22 +84,15 @@ func TestApacta_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -100,6 +104,7 @@ func TestApacta_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,27 +2,15 @@ package api2cart
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "0123456789abcdef0123456789abcdef"
|
||||
complexPattern = `
|
||||
To integrate with API2Cart, ensure you have the following credentials in your configuration file. Your API2CART key is 1234567890abcdef1234567890abcdef, which you will need to access the API securely.
|
||||
|
||||
The following endpoints are available for your use:
|
||||
- Get Products: https://api.api2cart.com/v1.0/products/get
|
||||
- Add Product: https://api.api2cart.com/v1.0/products/add
|
||||
`
|
||||
invalidPattern = "0123456789Gbcde^0123456789abcdef"
|
||||
)
|
||||
|
||||
func TestApi2Cart_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -33,19 +21,42 @@ func TestApi2Cart_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("api2cart credentials: %s", validPattern),
|
||||
want: []string{"0123456789abcdef0123456789abcdef"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
To integrate with API2Cart, ensure you have the following credentials in your configuration file.
|
||||
Your API2CART key is 2afddb813193eb9d3b5bd99bf5d834cd, which you will need to access the API securely.
|
||||
|
||||
The following endpoints are available for your use:
|
||||
- Get Products: https://api.api2cart.com/v1.0/products/get
|
||||
- Add Product: https://api.api2cart.com/v1.0/products/add
|
||||
`,
|
||||
want: []string{"2afddb813193eb9d3b5bd99bf5d834cd"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern -complex",
|
||||
input: complexPattern,
|
||||
want: []string{"1234567890abcdef1234567890abcdef"},
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{api2cart}</id>
|
||||
<secret>{AQAAABAAA b36c17e9dc0dba67480e864cf69879c3}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"b36c17e9dc0dba67480e864cf69879c3"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("api2cart credentials: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
To integrate with API2Cart, ensure you have the following credentials in your configuration file.
|
||||
Your API2CART key is 68d746609J4240840734c22836725d76, which you will need to access the API securely.
|
||||
|
||||
The following endpoints are available for your use:
|
||||
- Get Products: https://api.api2cart.com/v1.0/products/get
|
||||
- Add Product: https://api.api2cart.com/v1.0/products/add
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -53,22 +64,15 @@ func TestApi2Cart_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,6 +84,7 @@ func TestApi2Cart_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,22 +2,15 @@ package apideck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validKeyPattern = "sk_live_abc123def456ghi789jklmno123456pqrstuvwxyz-1234567890123456789012345678901234567890AAui09pU8as"
|
||||
validIdPattern = "apideck_id: 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t"
|
||||
invalidPattern = strings.ReplaceAll(validKeyPattern, "sk_live", "sk_inactive") + strings.ReplaceAll(validIdPattern, "1", "$")
|
||||
)
|
||||
|
||||
func TestApiDeck_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -28,16 +21,40 @@ func TestApiDeck_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("credentials: %s \n %s", validKeyPattern, validIdPattern),
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apideck API
|
||||
[DEBUG] Using Key=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
|
||||
[DEBUG] Using apideck ID=VfKE9Zh2ZatnqmrloqDu3PCnkNBR6Io4TlSbsG1P
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{
|
||||
validKeyPattern + "1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t",
|
||||
"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",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("credentials: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apideck id J6rYP2lzThxp9JeGg74TDgAXvfQsvzonsHpYHDsG}</id>
|
||||
<secret>{apideck AQAAABAAA sk_live_R5S2B88smT6QfTsUc3o3DedI2hbbcnZwvQKjyudQ41V0T38L8qUDPUTlBDcVE2NwRp1PowPYqnmAHlZ-W1Yr7AWGvpCvT}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"sk_live_R5S2B88smT6QfTsUc3o3DedI2hbbcnZwvQKjyudQ41V0T38L8qUDPUTlBDcVE2NwRp1PowPYqnmAHlZ-W1Yr7AWGvpCvTJ6rYP2lzThxp9JeGg74TDgAXvfQsvzonsHpYHDsG"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apideck API
|
||||
[DEBUG] Using Key=sk_live_GKE08ADdkDV1DQ4vDfaW4ejDHy-TkotfxDmHvQMLX0HRvhtfPwku6olGvsG2vXBg869A0hsOPHHOw48SAF2GO7jBMs6Rt
|
||||
[DEBUG] Using apideck ID=VfKE9Zh2ZatnqmrloqDu3PC_kNBR6Io4TlSbsG1P
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -45,22 +62,15 @@ func TestApiDeck_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -72,6 +82,7 @@ func TestApiDeck_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,22 +2,15 @@ package apiflash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = `
|
||||
apiflash_key: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
|
||||
`
|
||||
invalidPattern = "0123456789Gbcde^0123456789abcdef"
|
||||
)
|
||||
|
||||
func TestApiFlash_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -28,14 +21,36 @@ func TestApiFlash_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("apiflash credentials: %s", validPattern),
|
||||
want: []string{"a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apiflash API
|
||||
[DEBUG] Using Key=grevetn5owrs1ybhxtcen0ibvg2mi85x
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"grevetn5owrs1ybhxtcen0ibvg2mi85x"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("apiflash credentials: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apiflash}</id>
|
||||
<secret>{AQAAABAAA axlzvcf9m7jyyts833f9gmtcpqe5b26o}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"axlzvcf9m7jyyts833f9gmtcpqe5b26o"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apiflash API
|
||||
[DEBUG] Using Key=grevetn5owRs1ybhxtcen0ibvg2mi85x
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -43,22 +58,15 @@ func TestApiFlash_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,6 +78,7 @@ func TestApiFlash_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package apifonica
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "abc123def45-gh67-ij89-kl01-mn234op56789"
|
||||
invalidPattern = "0123456789Gbcde^0123456789abcdef"
|
||||
)
|
||||
|
||||
func TestApiFonica_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,14 +21,36 @@ func TestApiFonica_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("apifonica credentials: %s", validPattern),
|
||||
want: []string{"abc123def45-gh67-ij89-kl01-mn234op56789"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apifonica API
|
||||
[DEBUG] Using Key=4rv0hdx5188-3q48-2luk-e8v5-dyuuf8l44ib7
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"4rv0hdx5188-3q48-2luk-e8v5-dyuuf8l44ib7"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("apifonica credentials: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apifonica}</id>
|
||||
<secret>{AQAAABAAA fvzlzj17xzz-lwon-842u-46bs-5spcl2g7u9eb}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"fvzlzj17xzz-lwon-842u-46bs-5spcl2g7u9eb"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apifonica API
|
||||
[DEBUG] Using Key=4rv0hdx51889-3q48-2luk-e8wv5-dyuuf8l44ib7
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,22 +58,15 @@ func TestApiFonica_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,6 +78,7 @@ func TestApiFonica_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "apify_api_ab12CD34ef56GH78ij90KL12mn34OP56qr78"
|
||||
invalidPattern = "apify_api_ab12C#34ef56GH7$ij90KL12mn34OP56qr7)"
|
||||
)
|
||||
|
||||
func TestApiFy_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -25,14 +21,36 @@ func TestApiFy_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{validPattern},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=apify_api_dXB1vLsglgTexUYm3JTAx2BHTjVuDBbvPl8R
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"apify_api_dXB1vLsglgTexUYm3JTAx2BHTjVuDBbvPl8R"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{user-id}</id>
|
||||
<secret>{AQAAABAAA apify_api_RpTLEX9U18xfGl90wDaT2V9R-YX0TlMpxIzi}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"apify_api_RpTLEX9U18xfGl90wDaT2V9R-YX0TlMpxIzi"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the API
|
||||
[DEBUG] Using Key=apify_api_dXB1vLPglgTex_UYm3JTAx2BHTjVuDBbvPl8R
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,22 +58,15 @@ func TestApiFy_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,6 +78,7 @@ func TestApiFy_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "apilayerkey: abcdef1234567890abcdef1234567890"
|
||||
invalidPattern = "apilayerkey: abcdef123$5678*0abcde()1234567890)"
|
||||
)
|
||||
|
||||
func TestApiLayer_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -25,14 +21,36 @@ func TestApiLayer_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{"abcdef1234567890abcdef1234567890"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apilayer API
|
||||
[DEBUG] Using Key=qnHT110fihCn49wOm5b2h3ACTRmksbg0
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: []string{"qnHT110fihCn49wOm5b2h3ACTRmksbg0"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apilayer}</id>
|
||||
<secret>{AQAAABAAA HHTi3DYZIqt57j5WVHvXHHboXpCnm6CW}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"HHTi3DYZIqt57j5WVHvXHHboXpCnm6CW"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the apilayer API
|
||||
[DEBUG] Using Key=qnHT110fiha-Cn49wOm5b2h3ACTRmksbg0
|
||||
[INFO] Response received: 200 OK
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,22 +58,15 @@ func TestApiLayer_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,6 +78,7 @@ func TestApiLayer_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,22 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = `
|
||||
func validateApiMatic() bool {
|
||||
apiMaticKey := "rc6iLoUEFGGAWNLsuBJnmsh4tZB-oCxcDUmc45HIPcuiQvfUEuqo8wb9YrUd2LyB"
|
||||
|
||||
return isActive(apiMaticKey, apiMaticPass)
|
||||
}
|
||||
`
|
||||
invalidPattern = "apiMaticKey: rc6iL$UEFGGAWNLsuBJnmsh4tZB)oCxcDUmc45HIPcuiQvfUEuqo8wb9YrUd2LyB"
|
||||
)
|
||||
|
||||
func TestApiMatic_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -31,14 +21,40 @@ func TestApiMatic_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{"rc6iLoUEFGGAWNLsuBJnmsh4tZB-oCxcDUmc45HIPcuiQvfUEuqo8wb9YrUd2LyB"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateApiMatic() bool {
|
||||
apiMaticKey := "rc6iLoUEFGGAWNLsuBJnmsh4tZB-oCxcDUmc45HIPcuiQvfUEuqo8wb9YrUd2LyB"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(apiMaticKey)
|
||||
}`,
|
||||
want: []string{"rc6iLoUEFGGAWNLsuBJnmsh4tZB-oCxcDUmc45HIPcuiQvfUEuqo8wb9YrUd2LyB"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apimatic}</id>
|
||||
<secret>{AQAAABAAA 2eqQBh9HkE-5Mq5Ma_vOEvvyt-x9shcZ-T5B7hSY1C5xvTl7qLMwGL6QAoNYmMcF}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"2eqQBh9HkE-5Mq5Ma_vOEvvyt-x9shcZ-T5B7hSY1C5xvTl7qLMwGL6QAoNYmMcF"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateApiMatic() bool {
|
||||
apiMaticKey := "rc6iLoUEFGGAWNLsuBJnmsh4tZB@oCxcDUmc45HIPcuiQvfUEuqo8wb9YrUd2LyB"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(apiMaticKey)
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -46,22 +62,15 @@ func TestApiMatic_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,6 +82,7 @@ func TestApiMatic_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "apimetricskey: 5po8TFGawiYNCc1ct4ofWkBqzIfA6IeO"
|
||||
invalidPattern = "apimetricskey: A1b2C3d4 E5f6G7h8I9 j0K1l2M3n4O5p6"
|
||||
)
|
||||
|
||||
func TestApiMetrics_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -25,14 +21,40 @@ func TestApiMetrics_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{"5po8TFGawiYNCc1ct4ofWkBqzIfA6IeO"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateApiMetrics() bool {
|
||||
apiMetrics := "5po8TFGawiYNCc1ct4ofWkBqzIfA6IeO"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(apiMetrics)
|
||||
}`,
|
||||
want: []string{"5po8TFGawiYNCc1ct4ofWkBqzIfA6IeO"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apimetrics}</id>
|
||||
<secret>{AQAAABAAA XpLTBFZccOgbbtVht4OaZzsrgKdh42RX}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"XpLTBFZccOgbbtVht4OaZzsrgKdh42RX"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateApiMetrics() bool {
|
||||
apiMetrics := "5po8TFGawiYNCc1c4ofWkBqzIfA6IeO"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(apiMetrics)
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,22 +62,15 @@ func TestApiMetrics_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,6 +82,7 @@ func TestApiMetrics_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "apitemplatekey: a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T"
|
||||
invalidPattern = "apitemplatekey: sk_0C2c3D4e5F6@7H8i9J$k1L2m3N4o5P6q7R8s9T"
|
||||
)
|
||||
|
||||
func TestApiTemplate_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -25,14 +21,40 @@ func TestApiTemplate_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{"a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateKey() bool {
|
||||
apiTemplate := "EeOPHL7PyBlUk0qkJX72sDtdNL3WLdpxg1czllR"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(apiTemplate)
|
||||
}`,
|
||||
want: []string{"EeOPHL7PyBlUk0qkJX72sDtdNL3WLdpxg1czllR"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apitemplate}</id>
|
||||
<secret>{AQAAABAAA oVqX8yfzlUtzudNnvlWKNI4pNKTKTwlaKmxlcX5}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"oVqX8yfzlUtzudNnvlWKNI4pNKTKTwlaKmxlcX5"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateKey() bool {
|
||||
apiTemplate := "EeOPHL7PyBlUk0qkJAX72sDtdNL3WLdpxg1czllR"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(apiTemplate)
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,22 +62,15 @@ func TestApiTemplate_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,6 +82,7 @@ func TestApiTemplate_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -5,16 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "apollo: A1b2C3d4E5f6G7h8I9j0K1"
|
||||
invalidPattern = "apollo: A1b2C3d4-5f6G7h8I9j0K1@$"
|
||||
)
|
||||
|
||||
func TestApollo_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -25,14 +21,52 @@ func TestApollo_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{"A1b2C3d4E5f6G7h8I9j0K1"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateApolloKey() bool {
|
||||
apiKey := "897TJ1HevanW9Ye6nv6Ojj"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(apiKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: []string{"897TJ1HevanW9Ye6nv6Ojj"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{apollo}</id>
|
||||
<secret>{AQAAABAAA S2wg2NMlgalg9AUsrXPd1O}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"S2wg2NMlgalg9AUsrXPd1O"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateApolloKey() bool {
|
||||
apiKey := "897TJ1HevanW9Ye-nv6Ojj"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(apiKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,22 +74,15 @@ func TestApollo_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,6 +94,7 @@ func TestApollo_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -3,6 +3,7 @@ package appcues
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
type Scanner struct{
|
||||
type Scanner struct {
|
||||
detectors.DefaultMultiPartCredentialProvider
|
||||
}
|
||||
|
||||
@@ -60,18 +61,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
RawV2: []byte(resMatch + resUserMatch),
|
||||
}
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.appcues.com/v2/accounts/%s/flows", resIdMatch), nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.SetBasicAuth(resUserMatch, resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, err := verifyMatch(ctx, client, resUserMatch, resMatch, resIdMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(err, resUserMatch, resMatch, resIdMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -82,6 +74,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func verifyMatch(ctx context.Context, client *http.Client, resUserMatch, resMatch, resIdMatch string) (bool, error) {
|
||||
// Reference: https://api.appcues.com/v2/docs?_gl=1#responses
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://api.appcues.com/v2/accounts/%s/flows", resIdMatch), http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
req.SetBasicAuth(resUserMatch, resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized, http.StatusForbidden, http.StatusBadRequest:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Appcues
|
||||
}
|
||||
|
||||
@@ -5,24 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = `
|
||||
appcues abcdef12-3456-7890-1234-56789abcdef0
|
||||
appcues abcdef123456789012345678901234567890123
|
||||
appcues 12345
|
||||
`
|
||||
invalidPattern = `
|
||||
appcues abcdef12#3456-7890-1234-56789abcdef0
|
||||
appcues abcdef12345678901&345678901234567890123
|
||||
appcues 123456
|
||||
`
|
||||
)
|
||||
|
||||
func TestAppCues_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -33,14 +21,41 @@ func TestAppCues_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: validPattern,
|
||||
want: []string{"abcdef12-3456-7890-1234-56789abcdef0abcdef123456789012345678901234567890123"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the appcues API
|
||||
[DEBUG] Using appcues Key=5g5n4yazu-dpqp3g6qt3gn59wrxhqf2mqipm
|
||||
[DEBUG] Using appcues User=truffle-security-lrv10a8l4u23xp5gkvg819
|
||||
[INFO] Response received: 200 OK
|
||||
[INFO] APPCUES_ID=57843
|
||||
`,
|
||||
want: []string{"5g5n4yazu-dpqp3g6qt3gn59wrxhqf2mqipmtruffle-security-lrv10a8l4u23xp5gkvg819"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: invalidPattern,
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{appcues 91712}</id>
|
||||
<username>{appcues ubdcpht45hlfdywxv89ympnvtcnydl3uv-0umfu}</username>
|
||||
<secret>{appcues AQAAABAAA w9hyyfghqirj8uwcmtv05-n4fppzl-in223u}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"w9hyyfghqirj8uwcmtv05-n4fppzl-in223uubdcpht45hlfdywxv89ympnvtcnydl3uv-0umfu"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
[INFO] Sending request to the appcues API
|
||||
[DEBUG] Using appcues Key=5g5n4yazu-dpqp3g6qt3gn59wrxhqf2mqipm
|
||||
[DEBUG] Using appcues User=truffle_security-lrv10a8l4u23xp5gkvg819
|
||||
[ERROR] Response received: 401 UnAuthorized
|
||||
[INFO] ID=57843
|
||||
`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,22 +63,15 @@ func TestAppCues_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,6 +83,7 @@ func TestAppCues_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,6 +2,8 @@ package appfollow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.appfollow.io/api/v2/account/users", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("X-AppFollow-API-Token", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, err := verifyMatch(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -65,6 +58,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
|
||||
// Reference: https://docs.api.appfollow.io/reference/users_list_api_v2_account_users_get-1
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.appfollow.io/api/v2/account/users", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
req.Header.Add("X-AppFollow-API-Token", token)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK, http.StatusPaymentRequired, http.StatusUnprocessableEntity:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Appfollow
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@ package appfollow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.333HbjEo1oxVUFcASR0sQ8cMuIJRLcMd5H9iJWDbovCw6ESjNtuoEMQQGPN9aSoxponxrTPvn1.btADhgNetsaUBuwoyHo5ip0Jab6N6MEBnSaT6CHiO6z"
|
||||
invalidPattern = "eyQ0eXAiOiJMM1QiLDJhbGciOiJIUXI1NiK7.abcdefgh1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcd1234efgh5678ijkl9012.qwerty12345-ASDFG67890_zxcvb_ABCDE"
|
||||
)
|
||||
|
||||
func TestAppFollow_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,14 +21,46 @@ func TestAppFollow_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("appfollow credential: %s", validPattern),
|
||||
want: []string{"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.333HbjEo1oxVUFcASR0sQ8cMuIJRLcMd5H9iJWDbovCw6ESjNtuoEMQQGPN9aSoxponxrTPvn1.btADhgNetsaUBuwoyHo5ip0Jab6N6MEBnSaT6CHiO6z"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateAppFollowKey() bool {
|
||||
key := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.hdMLjiIayyb5cgbcVtjKywQwqeNKnsxZEhnJnX6wzhnblpmpjF4c2mbdmVVylTayE6M8ZE3h4V.fmnUM4cjvbe1JMFDuBSwWNEYQFHrD5AEm6p2Ir9w7K6"
|
||||
|
||||
// isActive check if the key is active or not
|
||||
return isActive(key)
|
||||
}`,
|
||||
want: []string{"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.hdMLjiIayyb5cgbcVtjKywQwqeNKnsxZEhnJnX6wzhnblpmpjF4c2mbdmVVylTayE6M8ZE3h4V.fmnUM4cjvbe1JMFDuBSwWNEYQFHrD5AEm6p2Ir9w7K6"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("appfollow credential: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{appfollow}</id>
|
||||
<secret>{AQAAABAAA eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.YwK6gJ8sMVylaDNuXRiGFLRR1kgZaLF45EbJ0qHSRaW4CRtWaqWciTZZXxkk4a4wLh8f7cTTlb.wvTVCRC1RLCpd98q4WK3ef6M3TBrb08AkS9-jNOdA_r}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.YwK6gJ8sMVylaDNuXRiGFLRR1kgZaLF45EbJ0qHSRaW4CRtWaqWciTZZXxkk4a4wLh8f7cTTlb.wvTVCRC1RLCpd98q4WK3ef6M3TBrb08AkS9-jNOdA_r"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateAppFollowKey() bool {
|
||||
apiKey := "eyJ0eXAiOiJKV1QiLCJhbGCiOiJIUzI1NiJ9.hdMLjiIayyb5cgbcVtjKywQwqeNKnsxZEhnJnX6wzhnblpmpjF4c2mbdVylTayE6M8ZE3h4V.fmnUM4cjvbe1JMFDuBSwWNEYQFHrDEm6p2Ir9w7K6"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(apiKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,22 +68,15 @@ func TestAppFollow_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,6 +88,7 @@ func TestAppFollow_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,11 +2,13 @@ package appointedd
|
||||
|
||||
import (
|
||||
"context"
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
@@ -44,24 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
Raw: []byte(resMatch),
|
||||
}
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.appointedd.com/v1/availability/slots", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("X-API-KEY", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
|
||||
if strings.Contains(body, "total") {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, err := verifyMatch(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -70,6 +57,35 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func verifyMatch(ctx context.Context, client *http.Client, secret string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.appointedd.com/v1/availability/slots", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
req.Header.Add("X-API-KEY", secret)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.Contains(string(bodyBytes), "total"), nil
|
||||
case http.StatusUnauthorized, http.StatusForbidden:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Appointedd
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@ package appointedd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "Ci0a2bSpRyFcZyEXBEr9RHzf3xXllqO=XVoh+t0L0s8T2s3MFntfWhBlovqLaqEadtuJ9=Jy6yCOXmhbpEZPfY7Y"
|
||||
invalidPattern = "Ci0a2bSpRyFcZyEXBEr9RHzf3xXllqO^XVoh+t0L0s8T2s3MFntfWhBlovqLaqEadtuJ9$Jy6yCOXmhbpEZPfY7Y"
|
||||
)
|
||||
|
||||
func TestAppFollow_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,14 +21,52 @@ func TestAppFollow_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("appointedd credential: %s", validPattern),
|
||||
want: []string{"Ci0a2bSpRyFcZyEXBEr9RHzf3xXllqO=XVoh+t0L0s8T2s3MFntfWhBlovqLaqEadtuJ9=Jy6yCOXmhbpEZPfY7Y"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateAppointeddKey() bool {
|
||||
appointeddKey := "Ci0a2bSpRyFcZyEXBEr9RHzf3xXllqO=XVoh+t0L0s8T2s3MFntfWhBlovqLaqEadtuJ9=Jy6yCOXmhbpEZPfY7Y"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(appointeddKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: []string{"Ci0a2bSpRyFcZyEXBEr9RHzf3xXllqO=XVoh+t0L0s8T2s3MFntfWhBlovqLaqEadtuJ9=Jy6yCOXmhbpEZPfY7Y"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("appointedd credential: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{appointedd}</id>
|
||||
<secret>{AQAAABAAA 2pRMKW=JrG9+xYmqlJMa4Omf9goqsSqsM3mIaqG8tG4lwnVrKIslbn=IpLIz7GTDEJUcQ0wlr6B+UjfvSY9XKXwu}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"2pRMKW=JrG9+xYmqlJMa4Omf9goqsSqsM3mIaqG8tG4lwnVrKIslbn=IpLIz7GTDEJUcQ0wlr6B+UjfvSY9XKXwu"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateAppointeddKey() bool {
|
||||
appointeddKey := "Ci0a2bSpRyFcZyEXBEr9RHzf3xXllqO-XVoh+t0L0s8T2s3MFntfWhBlovqLaqEadtuJ9-Jy6yCOXmhbpEZPfY7Y"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(appointeddKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,22 +74,15 @@ func TestAppFollow_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,6 +94,7 @@ func TestAppFollow_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
@@ -2,20 +2,15 @@ package appoptics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "IABJPR08RmvsGrebJhr1TUdo27-KtTn0mLCKkJJqj5lyba-otXPklygO9DK62o3QSPoIJ4E"
|
||||
invalidPattern = "IABJPR08Rmvs.GrebJhr1TUdo27_KtTn0mLCKkJJqj5lyba&otXPklygO9DK62o3QSPoIJ4E"
|
||||
)
|
||||
|
||||
func TestAppOptics_Pattern(t *testing.T) {
|
||||
d := Scanner{}
|
||||
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
|
||||
@@ -26,14 +21,52 @@ func TestAppOptics_Pattern(t *testing.T) {
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid pattern",
|
||||
input: fmt.Sprintf("appoptics credential: %s", validPattern),
|
||||
want: []string{"IABJPR08RmvsGrebJhr1TUdo27-KtTn0mLCKkJJqj5lyba-otXPklygO9DK62o3QSPoIJ4E"},
|
||||
name: "valid pattern",
|
||||
input: `
|
||||
func validateAppOpticsKey() bool {
|
||||
appopticsKey := "Xwl4ViaAFDLrAmFX9g1blkUVC5dJj2he3a1tzkpJ4-PznQukQruRjqMFbEG73L92LJyBGMZ"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(appopticsKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: []string{"Xwl4ViaAFDLrAmFX9g1blkUVC5dJj2he3a1tzkpJ4-PznQukQruRjqMFbEG73L92LJyBGMZ"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: fmt.Sprintf("appoptics credential: %s", invalidPattern),
|
||||
want: nil,
|
||||
name: "valid pattern - xml",
|
||||
input: `
|
||||
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
<scope>GLOBAL</scope>
|
||||
<id>{appoptics}</id>
|
||||
<secret>{AQAAABAAA zxsb8yzT0RbIJ1TAalB87LOVUcT1b4uEgvT4tXCcSqv_gcmlrx5aQRleHPDFKePjpHFof5J}</secret>
|
||||
<description>configuration for production</description>
|
||||
<creationDate>2023-05-18T14:32:10Z</creationDate>
|
||||
<owner>jenkins-admin</owner>
|
||||
</com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
|
||||
`,
|
||||
want: []string{"zxsb8yzT0RbIJ1TAalB87LOVUcT1b4uEgvT4tXCcSqv_gcmlrx5aQRleHPDFKePjpHFof5J"},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern",
|
||||
input: `
|
||||
func validateAppOpticsKey() bool {
|
||||
appopticsKey := "Xwl4ViaAFDLrAmFX9g1blkUVC5dJj2h:3a1tzkpJ43PznQukQruRjqMFbEG73L92LJyBGMZ"
|
||||
log.Println("Checking API key status...")
|
||||
|
||||
if !isActive(appopticsKey) {
|
||||
log.Println("API key is inactive or invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
log.Println("API key is valid and active.")
|
||||
return true
|
||||
}`,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -41,22 +74,15 @@ func TestAppOptics_Pattern(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
|
||||
if len(matchedDetectors) == 0 {
|
||||
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
|
||||
t.Errorf("test %q failed: expected keywords %v to be found in the input", test.name, d.Keywords())
|
||||
return
|
||||
}
|
||||
|
||||
results, err := d.FromData(context.Background(), false, []byte(test.input))
|
||||
if err != nil {
|
||||
t.Errorf("error = %v", err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(results) != len(test.want) {
|
||||
if len(results) == 0 {
|
||||
t.Errorf("did not receive result")
|
||||
} else {
|
||||
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
|
||||
}
|
||||
t.Errorf("mismatch in result count: expected %d, got %d", len(test.want), len(results))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,6 +94,7 @@ func TestAppOptics_Pattern(t *testing.T) {
|
||||
actual[string(r.Raw)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{}, len(test.want))
|
||||
for _, v := range test.want {
|
||||
expected[v] = struct{}{}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user