pax_global_header00006660000000000000000000000064145133210240014505gustar00rootroot0000000000000052 comment=d4d4d0ec9a2380ebeb0c10d95186681d4ee48d78 trufflehog-3.60.0/000077500000000000000000000000001451332102400137405ustar00rootroot00000000000000trufflehog-3.60.0/.gitattributes000066400000000000000000000000411451332102400166260ustar00rootroot00000000000000*.go text eol=lf *.md text eol=lftrufflehog-3.60.0/.github/000077500000000000000000000000001451332102400153005ustar00rootroot00000000000000trufflehog-3.60.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001451332102400174635ustar00rootroot00000000000000trufflehog-3.60.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000027021451332102400221560ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "" labels: bug, needs triage assignees: trufflesecurity/product-eng --- Please review the [Community Note](https://github.com/trufflesecurity/trufflehog/blob/main/.github/community_note.md) before submitting ### TruffleHog Version ### Trace Output ### Expected Behavior ### Actual Behavior ### Steps to Reproduce 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error ## Environment * OS: [e.g. iOS] * Version [e.g. 22] ## Additional Context ### References * #0000 trufflehog-3.60.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000020121451332102400232030ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "" labels: enhancement, needs triage assignees: trufflesecurity/product-eng --- Please review the [Community Note](https://github.com/trufflesecurity/trufflehog/blob/main/.github/community_note.md) before submitting ## Description ### Preferred Solution ### Additional Context #### References trufflehog-3.60.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000006741451332102400211100ustar00rootroot00000000000000 ### Description: Explain the purpose of the PR. ### Checklist: * [ ] Tests passing (`make test-community`)? * [ ] Lint passing (`make lint` this requires [golangci-lint](https://golangci-lint.run/usage/install/#local-installation))? trufflehog-3.60.0/.github/community_note.md000066400000000000000000000006771451332102400207050ustar00rootroot00000000000000## Community Note Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request. Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request. If you are interested in working on this issue or have submitted a pull request, please leave a comment. trufflehog-3.60.0/.github/dependabot.yml000066400000000000000000000011341451332102400201270ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "monthly" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" trufflehog-3.60.0/.github/workflows/000077500000000000000000000000001451332102400173355ustar00rootroot00000000000000trufflehog-3.60.0/.github/workflows/TESTING.md000066400000000000000000000025121451332102400207740ustar00rootroot00000000000000# Testing Most testing is handled automatically by our GitHub Actions workflows. ## Local GitHub Action Testing In some cases you may wish to submit changes to the Trufflehog GitHub Action. Unfortunately GitHub does not provide a 1st-party testing environment for testing actions outside of GitHub Actions. Fortunately [nektos/act](https://github.com/nektos/act) enables local testing of GitHub Actions. ### Instructions 1. Please follow [the installation instructions](http://https://github.com/nektos/act#installation) for your OS. 2. The first run of `act` will ask you to specify an image. `Medium` should suffice. 3. You'll need to configure a personal-access-token(PAT) with: `repo:status`, `repo_deployment`, and `public_repo` permissions. 4. Set an environment variable named `GITHUB_TOKEN` with the PAT from the previous step as the value: `$ export GITHUB_TOKEN=` 5. Run the following command from the repository root: `act pull_request -j test -W .github/workflows/secrets.yml -s GITHUB_TOKEN --defaultbranch main` 6. If the job was successful, you should expect to see output from the scanner showing several detected secrets. 7. If you want to omit the context of a pull request event and just test that the action starts successfully, run: `act -j test -W .github/workflows/secrets.yml -s GITHUB_TOKEN --defaultbranch main` trufflehog-3.60.0/.github/workflows/codeql-analysis.yml000066400000000000000000000044321451332102400231530ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ main ] pull_request: # The branches below must be a subset of the branches above branches: [ main ] schedule: - cron: '35 11 * * 2' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 trufflehog-3.60.0/.github/workflows/detector-tests.yml000066400000000000000000000031331451332102400230310ustar00rootroot00000000000000name: detector test aggregation on: workflow_dispatch: schedule: - cron: "0 8 * * *" jobs: test-detectors: runs-on: ubuntu-latest permissions: actions: "read" contents: "read" id-token: "write" steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: "1.21" - name: Checkout code uses: actions/checkout@v4 - id: "auth" uses: "google-github-actions/auth@v1" with: workload_identity_provider: "projects/811013774421/locations/global/workloadIdentityPools/github-pool/providers/github-provider" service_account: "github-ci-external@trufflehog-testing.iam.gserviceaccount.com" - name: Test run: make test-integration - name: Set up gotestsum run: | go install gotest.tools/gotestsum@latest mkdir -p tmp/test-results - name: Test run: | CGO_ENABLED=1 gotestsum --junitfile tmp/test-results/test.xml --raw-command -- go test -json -tags=detectors -timeout=15m $(go list ./... | grep pkg/detectors) - name: Upload test results to BuildPulse for flaky test detection if: ${{ !cancelled() }} # Run this step even when the tests fail. Skip if the workflow is cancelled. uses: buildpulse/buildpulse-action@main with: account: 79229934 repository: 694446374 path: | tmp/test-results/*.xml key: ${{ secrets.BUILDPULSE_DETECTORS_ACCESS_KEY_ID }} secret: ${{ secrets.BUILDPULSE_DETECTORS_SECRET_ACCESS_KEY }} tags: detectors trufflehog-3.60.0/.github/workflows/lint.yml000066400000000000000000000017671451332102400210410ustar00rootroot00000000000000name: Lint on: push: tags: - v* pull_request: permissions: contents: read pull-requests: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/setup-go@v4 with: go-version: '1.21' - uses: actions/checkout@v4 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: latest # Optional: working directory, useful for monorepos # working-directory: somedir # Optional: golangci-lint command line arguments. args: --enable bodyclose --enable exportloopref --timeout 10m # Optional: if set to true then the action don't cache or restore ~/go/pkg. # skip-pkg-cache: true # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. # skip-build-cache: true trufflehog-3.60.0/.github/workflows/performance.yml000066400000000000000000000053411451332102400223640ustar00rootroot00000000000000name: Performance Test on: [pull_request] jobs: speed: # skip if PR is from a fork. # TODO: this could probabaly be refactored a bit so that it runs on forks if: ${{ ! github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: '1.21' - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.head_ref }} - name: Run Head run: | go build -o current . repo_tmp=$(mktemp -d) git clone https://github.com/trufflesecurity/trufflehog.git $repo_tmp user_time_sum=0 for i in {1..5} do tmpfile=$(mktemp) /usr/bin/time -o $tmpfile $GITHUB_WORKSPACE/current git "file://$repo_tmp" --no-verification --no-update > out.txt cat $tmpfile time_output=$(cat $tmpfile) rm $tmpfile user_time=$(echo $time_output | awk '{print $1}' | sed 's/user//') # Add the user time to the sum user_time_sum=$(echo "$user_time_sum + $user_time" | bc) done average_user_time=$(echo "scale=3; $user_time_sum / 5" | bc) echo HEAD_TIME=$average_user_time >> $GITHUB_ENV - name: Figure out previous tag run: | git fetch --tags git tag -l --sort=-v:refname | head -n 1 > previous_tag.txt echo PREVIOUS_TAG=$(cat previous_tag.txt) >> $GITHUB_ENV - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ env.PREVIOUS_TAG }} - name: Run Previous run: | go build -o previous . repo_tmp=$(mktemp -d) git clone https://github.com/trufflesecurity/trufflehog.git $repo_tmp user_time_sum=0 for i in {1..5} do tmpfile=$(mktemp) /usr/bin/time -o $tmpfile $GITHUB_WORKSPACE/previous git "file://$repo_tmp" --no-verification --no-update > out.txt cat $tmpfile time_output=$(cat $tmpfile) rm $tmpfile user_time=$(echo $time_output | awk '{print $1}' | sed 's/user//') # Add the user time to the sum user_time_sum=$(echo "$user_time_sum + $user_time" | bc) done average_user_time=$(echo "scale=3; $user_time_sum / 5" | bc) echo PREVIOUS_TIME=$average_user_time >> $GITHUB_ENV - name: Compare Results run: | echo "head ($GITHUB_SHA) avg time (n=5): $HEAD_TIME" echo "$PREVIOUS_TAG avg time (n=5): $PREVIOUS_TIME" if [ $(echo "$HEAD_TIME > $PREVIOUS_TIME * 1.1" | bc) -eq 1 ] then echo "HEAD run time is at least 10% slower than PREVIOUS run time" exit 1 fi trufflehog-3.60.0/.github/workflows/release.yml000066400000000000000000000022741451332102400215050ustar00rootroot00000000000000name: Release on: push: tags: - v* permissions: contents: write packages: write jobs: Release: runs-on: ubuntu-latest env: DOCKER_CLI_EXPERIMENTAL: "enabled" steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Docker Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Docker Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.21' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5 with: distribution: goreleaser version: latest args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} trufflehog-3.60.0/.github/workflows/secrets.yml000066400000000000000000000010761451332102400215340ustar00rootroot00000000000000 name: Scan for secrets on: push: tags: - v* branches: - main pull_request: jobs: test: runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: '1.21' - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.head_ref }} - name: Dogfood uses: ./ id: dogfood with: path: ./ base: ${{ github.event.repository.default_branch }} head: HEAD extra_args: --only-verified trufflehog-3.60.0/.github/workflows/smoke.yml000066400000000000000000000006741451332102400212050ustar00rootroot00000000000000 name: Smoke on: pull_request: jobs: smoke: runs-on: ubuntu-latest steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: '1.21' - name: Checkout code uses: actions/checkout@v4 - name: Smoke run: | set -e go run . git https://github.com/dustin-decker/secretsandstuff.git go run . github --repo https://github.com/dustin-decker/secretsandstuff.git trufflehog-3.60.0/.github/workflows/snifftest.yml000066400000000000000000000005731451332102400220720ustar00rootroot00000000000000name: Snifftest on: push: tags: - v* branches: - main permissions: contents: read pull-requests: read jobs: Snifftest: name: Run Snifftest runs-on: ubuntu-latest steps: - uses: actions/setup-go@v4 with: go-version: '1.21' - uses: actions/checkout@v4 - name: Run Snifftest run: make snifftest trufflehog-3.60.0/.github/workflows/test.yml000066400000000000000000000044171451332102400210450ustar00rootroot00000000000000name: Test on: push: tags: - v* branches: - main pull_request: jobs: test: if: ${{ ! github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest permissions: actions: "read" contents: "read" id-token: "write" steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: "1.21" - name: Checkout code uses: actions/checkout@v4 - id: "auth" uses: "google-github-actions/auth@v1" with: workload_identity_provider: "projects/811013774421/locations/global/workloadIdentityPools/github-pool/providers/github-provider" service_account: "github-ci-external@trufflehog-testing.iam.gserviceaccount.com" - name: Set up gotestsum run: | go install gotest.tools/gotestsum@latest mkdir -p tmp/test-results - name: Test run: | CGO_ENABLED=1 gotestsum --junitfile tmp/test-results/test.xml --raw-command -- go test -json -tags=sources $(go list ./... | grep -v /vendor/ | grep -v pkg/detectors) if: ${{ success() || failure() }} # always run this step, even if there were previous errors - name: Upload test results to BuildPulse for flaky test detection if: ${{ !cancelled() }} # Run this step even when the tests fail. Skip if the workflow is cancelled. uses: buildpulse/buildpulse-action@main with: account: 79229934 repository: 77726177 path: | tmp/test-results/*.xml key: ${{ secrets.BUILDPULSE_ACCESS_KEY_ID }} secret: ${{ secrets.BUILDPULSE_SECRET_ACCESS_KEY }} tags: integration - name: Annotate test results uses: mikepenz/action-junit-report@v4 if: success() || failure() # always run even if the previous step fails with: report_paths: "tmp/test-results/*.xml" test-community: if: ${{ github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest permissions: actions: "read" contents: "read" steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: "1.21" - name: Checkout code uses: actions/checkout@v4 - name: Test run: make test-community trufflehog-3.60.0/.gitignore000066400000000000000000000000541451332102400157270ustar00rootroot00000000000000.idea dist .env *.test # binary trufflehog trufflehog-3.60.0/.goreleaser.yml000066400000000000000000000106411451332102400166730ustar00rootroot00000000000000builds: - binary: trufflehog ldflags: - -X 'github.com/trufflesecurity/trufflehog/v3/pkg/version.BuildVersion={{ .Version }}' env: [CGO_ENABLED=0] goos: - linux - windows - darwin goarch: - amd64 - arm64 dockers: - image_templates: ["trufflesecurity/{{ .ProjectName }}:{{ .Version }}-amd64"] dockerfile: Dockerfile.goreleaser extra_files: - entrypoint.sh use: buildx build_flag_templates: - --platform=linux/amd64 - --label=org.opencontainers.image.title={{ .ProjectName }} - --label=org.opencontainers.image.description={{ .ProjectName }} - --label=org.opencontainers.image.url=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.source=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.version={{ .Version }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - image_templates: ["trufflesecurity/{{ .ProjectName }}:{{ .Version }}-arm64v8"] goarch: arm64 dockerfile: Dockerfile.goreleaser extra_files: - entrypoint.sh use: buildx build_flag_templates: - --platform=linux/arm64/v8 - --label=org.opencontainers.image.title={{ .ProjectName }} - --label=org.opencontainers.image.description={{ .ProjectName }} - --label=org.opencontainers.image.url=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.source=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.version={{ .Version }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - image_templates: ["ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }}-amd64"] dockerfile: Dockerfile.goreleaser extra_files: - entrypoint.sh use: buildx build_flag_templates: - --platform=linux/amd64 - --label=org.opencontainers.image.title={{ .ProjectName }} - --label=org.opencontainers.image.description={{ .ProjectName }} - --label=org.opencontainers.image.url=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.source=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.version={{ .Version }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - image_templates: ["ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }}-arm64v8"] goarch: arm64 dockerfile: Dockerfile.goreleaser extra_files: - entrypoint.sh use: buildx build_flag_templates: - --platform=linux/arm64/v8 - --label=org.opencontainers.image.title={{ .ProjectName }} - --label=org.opencontainers.image.description={{ .ProjectName }} - --label=org.opencontainers.image.url=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.source=https://github.com/trufflesecurity/{{ .ProjectName }} - --label=org.opencontainers.image.version={{ .Version }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 docker_manifests: - name_template: trufflesecurity/{{ .ProjectName }}:{{ .Version }} image_templates: - trufflesecurity/{{ .ProjectName }}:{{ .Version }}-amd64 - trufflesecurity/{{ .ProjectName }}:{{ .Version }}-arm64v8 - name_template: trufflesecurity/{{ .ProjectName }}:latest image_templates: - trufflesecurity/{{ .ProjectName }}:{{ .Version }}-amd64 - trufflesecurity/{{ .ProjectName }}:{{ .Version }}-arm64v8 - name_template: ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }} image_templates: - ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }}-amd64 - ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }}-arm64v8 - name_template: ghcr.io/trufflesecurity/{{ .ProjectName }}:latest image_templates: - ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }}-amd64 - ghcr.io/trufflesecurity/{{ .ProjectName }}:{{ .Version }}-arm64v8 brews: - tap: owner: trufflesecurity name: homebrew-trufflehog token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" description: "Find credentials all over the place" name: "trufflehog" homepage: "https://github.com/trufflesecurity/trufflehog" install: | bin.install "trufflehog" trufflehog-3.60.0/.pre-commit-config.yaml000066400000000000000000000003271451332102400202230ustar00rootroot00000000000000repos: - repo: https://github.com/rhysd/actionlint rev: v1.6.24 hooks: - id: actionlint - repo: https://github.com/mpalmer/action-validator rev: v0.5.1 hooks: - id: action-validator trufflehog-3.60.0/.pre-commit-hooks.yaml000066400000000000000000000003261451332102400201000ustar00rootroot00000000000000- id: trufflehog name: TruffleHog description: Detect secrets in your data with TruffleHog. entry: trufflehog git file://. --since-commit HEAD --only-verified --fail language: golang pass_filenames: falsetrufflehog-3.60.0/CODEOWNERS000066400000000000000000000015651451332102400153420ustar00rootroot00000000000000# catch-all * @trufflesecurity/product-eng # teams pkg/sources/ @trufflesecurity/backend pkg/detectors/ @trufflesecurity/detection # critical detectors pkg/detectors/aws/ @trufflesecurity/backend pkg/detectors/gcp/ @trufflesecurity/backend pkg/detectors/azure/ @trufflesecurity/backend pkg/detectors/okta/ @trufflesecurity/backend pkg/detectors/privatekey/ @trufflesecurity/backend pkg/detectors/slack/ @trufflesecurity/backend pkg/detectors/slackwebhook/ @trufflesecurity/backend pkg/detectors/microsoftteamswebhook/ @trufflesecurity/backend pkg/detectors/twilio/ @trufflesecurity/backend pkg/detectors/sendgrid/ @trufflesecurity/backend pkg/detectors/gitlab/ @trufflesecurity/backend pkg/detectors/gitlabv2/ @trufflesecurity/backend pkg/detectors/github/ @trufflesecurity/backend pkg/detectors/github_old/ @trufflesecurity/backend pkg/detectors/githubapp/ @trufflesecurity/backend trufflehog-3.60.0/CODE_OF_CONDUCT.md000066400000000000000000000064341451332102400165460ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at community@trufflesec.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq trufflehog-3.60.0/CONTRIBUTING.md000066400000000000000000000013041451332102400161670ustar00rootroot00000000000000# Contribution guidelines Please create an issue to collect feedback prior to feature additions. If possible try to keep PRs scoped to one feature, and add tests for new features. When showing interest in a bug, enhancement, PR, or issue, please use the thumbs up/thumbs down emoji on the original message rather than adding comments expressing the same. Contributors need to [sign our CLA](https://cla-assistant.io/trufflesecurity/trufflehog) before we are able to accept contributions. # Resources ## Adding new secret detectors We have published some [documentation and tooling to get started on adding new secret detectors](hack/docs/Adding_Detectors_external.md). Let's improve detection together!trufflehog-3.60.0/Dockerfile000066400000000000000000000010731451332102400157330ustar00rootroot00000000000000FROM --platform=${BUILDPLATFORM} golang:bullseye as builder WORKDIR /build COPY . . ENV CGO_ENABLED=0 ARG TARGETOS TARGETARCH 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.15 RUN apk add --no-cache bash git openssh-client ca-certificates \ && update-ca-certificates COPY --from=builder /build/trufflehog /usr/bin/trufflehog COPY entrypoint.sh /etc/entrypoint.sh RUN chmod +x /etc/entrypoint.sh ENTRYPOINT ["/etc/entrypoint.sh"] trufflehog-3.60.0/Dockerfile.goreleaser000066400000000000000000000004361451332102400200640ustar00rootroot00000000000000FROM alpine:3.15 RUN apk add --no-cache bash git openssh-client ca-certificates \ && rm -rf /var/cache/apk/* && \ update-ca-certificates WORKDIR /usr/bin/ COPY trufflehog . COPY entrypoint.sh /etc/entrypoint.sh RUN chmod +x /etc/entrypoint.sh ENTRYPOINT ["/etc/entrypoint.sh"] trufflehog-3.60.0/LICENSE000066400000000000000000001051711451332102400147520ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . Copy license text to clipboard Suggest this license Make a pull request to suggest this license for a project that is not licensed. Please be polite: see if a license has already been suggested, try to suggest a license fitting for the project’s community, and keep your communication with project maintainers friendly. Enter GitHub repository URL How to apply this license Create a text file (typically named LICENSE or LICENSE.txt) in the root of your source code and copy the text of the license into the file. Optional steps The Free Software Foundation recommends taking the additional step of adding a boilerplate notice to the top of each file. The boilerplate can be found at the end of the license. Add AGPL-3.0-or-later (or AGPL-3.0-only to disallow future versions) to your project’s package description, if applicable (e.g., Node.js, Ruby, and Rust). This will ensure the license is displayed in package directories. Source trufflehog-3.60.0/Makefile000066400000000000000000000037721451332102400154110ustar00rootroot00000000000000PROTOS_IMAGE ?= trufflesecurity/protos:1.21-0 .PHONY: check .PHONY: lint .PHONY: test .PHONY: test-race .PHONY: run .PHONY: install .PHONY: protos .PHONY: protos-windows .PHONY: vendor .PHONY: dogfood dogfood: CGO_ENABLED=0 go run . git file://. --json --debug install: CGO_ENABLED=0 go install . check: go fmt $(shell go list ./... | grep -v /vendor/) go vet $(shell go list ./... | grep -v /vendor/) lint: golangci-lint run --enable bodyclose --enable exportloopref --out-format=colored-line-number --timeout 10m test-failing: CGO_ENABLED=0 go test -timeout=5m $(shell go list ./... | grep -v /vendor/) | grep FAIL test: CGO_ENABLED=0 go test -timeout=5m $(shell go list ./... | grep -v /vendor/ | grep -v pkg/detectors) test-integration: CGO_ENABLED=0 go test -timeout=5m -tags=integration $(shell go list ./... | grep -v /vendor/ | grep -v pkg/detectors) test-race: CGO_ENABLED=1 go test -timeout=5m -race $(shell go list ./... | grep -v /vendor/ | grep -v pkg/detectors) test-detectors: CGO_ENABLED=0 go test -tags=detectors -timeout=5m $(shell go list ./... | grep pkg/detectors) test-community: CGO_ENABLED=0 go test -timeout=5m $(shell go list ./... | grep -v /vendor/ | grep -v pkg/detectors | grep -v pkg/sources) bench: CGO_ENABLED=0 go test $(shell go list ./pkg/secrets/... | grep -v /vendor/) -benchmem -run=xxx -bench . run: CGO_ENABLED=0 go run . git file://. --json run-debug: CGO_ENABLED=0 go run . git file://. --json --debug protos: docker run -u "$(shell id -u)" -v "$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))":/pwd "${PROTOS_IMAGE}" bash -c "cd /pwd; /pwd/scripts/gen_proto.sh" protos-windows: docker run -v "$(shell cygpath -w $(shell pwd))":/pwd "${PROTOS_IMAGE}" bash -c "cd /pwd; ./scripts/gen_proto.sh" release-protos-image: docker buildx build --push --platform=linux/amd64,linux/arm64 \ -t trufflesecurity/protos:1.21-0 -f hack/Dockerfile.protos . snifftest: ./hack/snifftest/snifftest.sh test-release: goreleaser release --rm-dist --skip-publish --snapshot trufflehog-3.60.0/README.md000066400000000000000000000374631451332102400152340ustar00rootroot00000000000000

GoReleaser Logo

TruffleHog

Find leaked credentials.

---
[![Go Report Card](https://goreportcard.com/badge/github.com/trufflesecurity/trufflehog/v3)](https://goreportcard.com/report/github.com/trufflesecurity/trufflehog/v3) [![License](https://img.shields.io/badge/license-AGPL--3.0-brightgreen)](/LICENSE) [![Total Detectors](https://img.shields.io/github/directory-file-count/trufflesecurity/truffleHog/pkg/detectors?label=Total%20Detectors&type=dir)](/pkg/detectors)
--- # :mag_right: _Now Scanning_
**...and more**
# :loudspeaker: Join Our Community Have questions? Feedback? Jump in slack or discord and hang out with us Join our [Slack Community](https://join.slack.com/t/trufflehog-community/shared_invite/zt-pw2qbi43-Aa86hkiimstfdKH9UCpPzQ) Join the [Secret Scanning Discord](https://discord.gg/8Hzbrnkr7E) # :tv: Demo ![GitHub scanning demo](https://storage.googleapis.com/truffle-demos/non-interactive.svg) ```bash docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --org=trufflesecurity ``` # :floppy_disk: Installation Several options available for you: ```bash # MacOS users brew install trufflesecurity/trufflehog/trufflehog # Docker docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo https://github.com/trufflesecurity/test_keys # Docker for M1 and M2 Mac docker run --platform linux/arm64 --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo https://github.com/trufflesecurity/test_keys # Binary releases Download and unpack from https://github.com/trufflesecurity/trufflehog/releases # Compile from source git clone https://github.com/trufflesecurity/trufflehog.git cd trufflehog; go install # Using installation script curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin # Using installation script to install a specific version curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin ``` # :rocket: Quick Start ## 1: Scan a repo for only verified secrets Command: ```bash trufflehog git https://github.com/trufflesecurity/test_keys --only-verified ``` Expected output: ``` 🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷 Found verified result 🐷🔑 Detector Type: AWS Decoder Type: PLAIN Raw result: AKIAYVP4CIPPERUVIFXG Line: 4 Commit: fbc14303ffbf8fb1c2c1914e8dda7d0121633aca File: keys Email: counter Repository: https://github.com/trufflesecurity/test_keys Timestamp: 2022-06-16 10:17:40 -0700 PDT ... ``` ## 2: Scan a GitHub Org for only verified secrets ```bash trufflehog github --org=trufflesecurity --only-verified ``` ## 3: Scan a GitHub Repo for only verified keys and get JSON output Command: ```bash trufflehog git https://github.com/trufflesecurity/test_keys --only-verified --json ``` Expected output: ``` {"SourceMetadata":{"Data":{"Git":{"commit":"fbc14303ffbf8fb1c2c1914e8dda7d0121633aca","file":"keys","email":"counter \u003ccounter@counters-MacBook-Air.local\u003e","repository":"https://github.com/trufflesecurity/test_keys","timestamp":"2022-06-16 10:17:40 -0700 PDT","line":4}}},"SourceID":0,"SourceType":16,"SourceName":"trufflehog - git","DetectorType":2,"DetectorName":"AWS","DecoderName":"PLAIN","Verified":true,"Raw":"AKIAYVP4CIPPERUVIFXG","Redacted":"AKIAYVP4CIPPERUVIFXG","ExtraData":{"account":"595918472158","arn":"arn:aws:iam::595918472158:user/canarytokens.com@@mirux23ppyky6hx3l6vclmhnj","user_id":"AIDAYVP4CIPPJ5M54LRCY"},"StructuredData":null} ... ``` ## 4: Scan a GitHub Repo + its Issues and Pull Requests. ```bash trufflehog github --repo=https://github.com/trufflesecurity/test_keys --issue-comments --pr-comments ``` ## 5: Scan an S3 bucket for verified keys ```bash trufflehog s3 --bucket= --only-verified ``` ## 6: Scan S3 buckets using IAM Roles ```bash trufflehog s3 --role-arn= ``` ## 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 ``` ## 8: Scan individual files or directories ```bash trufflehog filesystem path/to/file1.txt path/to/file2.txt path/to/dir ``` ## 9: Scan GCS buckets for verified secrets. ```bash trufflehog gcs --project-id= --cloud-environment --only-verified ``` ## 10: Scan a Docker image for verified secrets. Use the `--image` flag multiple times to scan multiple images. ```bash trufflehog docker --image trufflesecurity/secrets --only-verified ``` # :question: FAQ + All I see is `🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷` and the program exits, what gives? + That means no secrets were detected + Why is the scan is 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/) + 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. # :newspaper: What's new in v3? TruffleHog v3 is a complete rewrite in Go with many new powerful features. - We've **added over 700 credential detectors that support active verification against their respective APIs**. - We've also added native **support for scanning GitHub, GitLab, filesystems, S3, GCS and Circle CI**. - **Instantly verify private keys** against millions of github users and **billions** of TLS certificates using our [Driftwood](https://trufflesecurity.com/blog/driftwood) technology. - Scan binaries and other file formats - Available as a GitHub Action and a pre-commit hook ## 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. # :memo: Usage TruffleHog has a sub-command for each source of data that you may want to scan: - git - github - gitlab - docker - S3 - filesystem (files and directories) - syslog - circleci - GCS (Google Cloud Storage) - stdin (coming soon) Each subcommand can have options that you can see with the `--help` flag provided to the sub command: ``` $ trufflehog git --help usage: TruffleHog git [] Find credentials in git repositories. Flags: --help Show context-sensitive help (also try --help-long and --help-man). --debug Run in debug mode. --trace Run in trace mode. --profile Enables profiling and sets a pprof and fgprof server on :18066. -j, --json Output in JSON format. --json-legacy Use the pre-v3.0 JSON format. Only works with git, gitlab, and github sources. --concurrency=10 Number of concurrent workers. --no-verification Don't verify the results. --only-verified Only output verified results. --filter-unverified Only output first unverified result per chunk per detector if there are more than one results. --config=CONFIG Path to configuration file. --print-avg-detector-time Print the average time spent on each detector. --no-update Don't check for updates. --fail Exit with code 183 if results are found. --version Show application version. Args: Git repository URL. https://, file://, or ssh:// schema expected. ``` For example, to scan a `git` repository, start with ``` $ trufflehog git https://github.com/trufflesecurity/trufflehog.git ``` ## S3 The S3 source supports assuming IAM roles for scanning in addition to IAM users. This makes it easier for users to scan multiple AWS accounts without needing to rely on hardcoded credentials for each account. The IAM identity that TruffleHog uses initially will need to have `AssumeRole` privileges as a principal in the [trust policy](https://aws.amazon.com/blogs/security/how-to-use-trust-policies-with-iam-roles/) of each IAM role to assume. To scan a specific bucket using locally set credentials or instance metadata if on an EC2 instance: ```bash trufflehog s3 --bucket= ``` To scan a specific bucket using an assumed role: ```bash trufflehog s3 --bucket= --role-arn= ``` Multiple roles can be passed as separate arguments. The following command will attempt to scan every bucket each role has permissions to list in the S3 API: ```bash trufflehog s3 --role-arn= --role-arn= ``` Exit Codes: - 0: No errors and no results were found. - 1: An error was encountered. Sources may not have completed scans. - 183: No errors were encountered, but results were found. Will only be returned if `--fail` flag is used. # :octocat: TruffleHog Github Action ```yaml - name: TruffleHog uses: trufflesecurity/trufflehog@main with: # Repository path path: # Start scanning from here (usually main branch). base: # Scan commits until here (usually dev branch). head: # optional # Extra args to be passed to the trufflehog cli. extra_args: --debug --only-verified ``` The TruffleHog OSS Github Action can be used to scan a range of commits for leaked credentials. The action will fail if any results are found. For example, to scan the contents of pull requests you could use the following workflow: ```yaml name: TruffleHog Secrets Scan on: [pull_request] jobs: TruffleHog: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 with: fetch-depth: 0 - name: TruffleHog OSS uses: trufflesecurity/trufflehog@main with: path: ./ base: ${{ github.event.repository.default_branch }} head: HEAD extra_args: --debug --only-verified ``` # Pre-commit Hook Trufflehog can be used in a pre-commit hook to prevent credentials from leaking before they ever leave your computer. An example `.pre-commit-config.yaml` is provided (see [pre-commit.com](https://pre-commit.com/) for installation). ```yaml repos: - repo: local hooks: - id: trufflehog name: TruffleHog description: Detect secrets in your data. entry: bash -c 'trufflehog git file://. --since-commit HEAD --only-verified --fail' # For running trufflehog in docker, use the following entry instead: # entry: bash -c 'docker run --rm -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail' language: system stages: ["commit", "push"] ``` # Regex Detector (alpha) Trufflehog supports detection and verification of custom regular expressions. For detection, at least one **regular expression** and **keyword** is required. A **keyword** is a fixed literal string identifier that appears in or around the regex to be detected. To allow maximum flexibility for verification, a 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. **NB:** This feature is alpha and subject to change. ## Regex Detector Example ```yaml # config.yaml detectors: - name: hog detector keywords: - hog regex: adjective: hogs are (\S+) verify: - endpoint: http://localhost:8000/ # unsafe must be set if the endpoint is HTTP unsafe: true headers: - 'Authorization: super secret authorization header' ``` ``` $ trufflehog filesystem /tmp --config config.yaml --only-verified 🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷 Found verified result 🐷🔑 Detector Type: CustomRegex Decoder Type: PLAIN Raw result: hogs are cool File: /tmp/hog-facts.txt ``` ## Verification Server Example (Python) Unless you run a verification server, secrets found by the custom regex detector will be unverified. Here is an example Python implementation of a verification server for the above `config.yaml` file. ```python import json from http.server import BaseHTTPRequestHandler, HTTPServer AUTH_HEADER = 'super secret authorization header' class Verifier(BaseHTTPRequestHandler): def do_GET(self): self.send_response(405) self.end_headers() def do_POST(self): try: if self.headers['Authorization'] != AUTH_HEADER: self.send_response(401) self.end_headers() return # read the body length = int(self.headers['Content-Length']) request = json.loads(self.rfile.read(length)) self.log_message("%s", request) # check the match if request['hog detector']['adjective'][-1] == 'cool': self.send_response(200) self.end_headers() else: # any other response besides 200 self.send_response(406) self.end_headers() except Exception: self.send_response(400) self.end_headers() with HTTPServer(('', 8000), Verifier) as server: try: server.serve_forever() except KeyboardInterrupt: pass ``` # :heart: Contributors This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. # :computer: Contributing Contributions are very welcome! Please see our [contribution guidelines first](CONTRIBUTING.md). We no longer accept contributions to TruffleHog v2, but that code is available in the `v2` branch. ## Adding new secret detectors We have published some [documentation and tooling to get started on adding new secret detectors](hack/docs/Adding_Detectors_external.md). Let's improve detection together! # Use as a library Currently, trufflehog is in heavy development and no guarantees can be made on the stability of the public APIs at this time. # License Change Since v3.0, TruffleHog is released under a AGPL 3 license, included in [`LICENSE`](LICENSE). TruffleHog v3.0 uses none of the previous codebase, but care was taken to preserve backwards compatibility on the command line interface. The work previous to this release is still available licensed under GPL 2.0 in the history of this repository and the previous package releases and tags. A completed CLA is required for us to accept contributions going forward. # :money_with_wings: Enterprise product Are you interested in continuously monitoring your Git, Jira, Slack, Confluence, etc.. for credentials? We have an enterprise product that can help. Reach out here to learn more https://trufflesecurity.com/contact/ We take the revenue from the enterprise product to fund more awesome open source projects that the whole community can benefit from. trufflehog-3.60.0/SECURITY.md000066400000000000000000000001441451332102400155300ustar00rootroot00000000000000Please report any security issues to security@trufflesec.com and include `trufflehog` in the subjecttrufflehog-3.60.0/action.yml000066400000000000000000000015361451332102400157450ustar00rootroot00000000000000name: 'TruffleHog OSS' description: 'Scan Github Actions with TruffleHog' author: Truffle Security Co. inputs: path: description: Repository path required: true base: description: Start scanning from here (usually main branch). required: false default: '' head: description: Scan commits until here (usually dev branch). required: false extra_args: default: '' description: Extra args to be passed to the trufflehog cli. required: false branding: icon: "shield" color: "green" runs: using: "docker" image: "docker://ghcr.io/trufflesecurity/trufflehog:latest" args: - git - file://${{ inputs.path }} - --since-commit - ${{ inputs.base }} - --branch - ${{ inputs.head }} - --fail - --no-update - --github-actions - ${{ inputs.extra_args }} trufflehog-3.60.0/assets/000077500000000000000000000000001451332102400152425ustar00rootroot00000000000000trufflehog-3.60.0/assets/scanning_logos.svg000066400000000000000000000264511451332102400207760ustar00rootroot00000000000000 trufflehog-3.60.0/entrypoint.sh000077500000000000000000000003231451332102400165100ustar00rootroot00000000000000#!/usr/bin/env bash # Parse the last argument into an array of extra_args. mapfile -t extra_args < <(bash -c "for arg in ${*: -1}; do echo \$arg; done") /usr/bin/trufflehog "${@: 1: $#-1}" "${extra_args[@]}" trufflehog-3.60.0/examples/000077500000000000000000000000001451332102400155565ustar00rootroot00000000000000trufflehog-3.60.0/examples/README.md000066400000000000000000000013251451332102400170360ustar00rootroot00000000000000# Examples This folder contains various examples like custom detectors, scripts, etc. Feel free to contribute! ### Generic Detector An often requested feature for TruffleHog is a generic detector. By default, we do not support generic detection as it would result in lots of false positives. However, if you want to attempt detect generic secrets you can use a custom detector. #### Try it out: ``` wget https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/examples/generic.yml trufflehog filesystem --config=$PWD/generic.yml $PWD # to filter so that _only_ generic credentials are logged: trufflehog filesystem --config=$PWD/generic.yml --json --no-verification $PWD | awk '/generic-api-key/{print $0}' ``` trufflehog-3.60.0/examples/generic.yml000066400000000000000000000007071451332102400177210ustar00rootroot00000000000000detectors: - name: generic-api-key keywords: - key - api - token - secret - client - passwd - password - auth - access regex: # borrowing the gitleaks generic-api-key regex generic-api-key: "(?i)(?:key|api|token|secret|client|passwd|password|auth|access)(?:[0-9a-z\\-_\\t .]{0,20})(?:[\\s|']|[\\s|\"]){0,3}(?:=|>|:{1,3}=|\\|\\|:|<=|=>|:|\\?=)(?:'|\"|\\s|=|\\x60){0,5}([0-9a-z\\-_.=]{10,150})(?:['|\"|\\n|\\r|\\s|\\x60|;]|$)" trufflehog-3.60.0/go.mod000066400000000000000000000314401451332102400150500ustar00rootroot00000000000000module github.com/trufflesecurity/trufflehog/v3 go 1.21 replace github.com/jpillora/overseer => github.com/trufflesecurity/overseer v1.1.7-custom5 // go wants to pull in the latest but after v10.2.5 has a breaking API change replace github.com/couchbase/gocbcore/v10 => github.com/couchbase/gocbcore/v10 v10.2.4 require ( cloud.google.com/go/secretmanager v1.11.1 cloud.google.com/go/storage v1.33.0 github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 github.com/BobuSumisu/aho-corasick v1.0.3 github.com/TheZeroSlave/zapsentry v1.17.0 github.com/aws/aws-sdk-go v1.45.19 github.com/aymanbagabas/go-osc52 v1.2.1 github.com/bill-rich/disk-buffer-reader v0.1.7 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.7.0 github.com/charmbracelet/bubbles v0.16.1 github.com/charmbracelet/bubbletea v0.24.2 github.com/charmbracelet/glamour v0.6.0 github.com/charmbracelet/lipgloss v0.7.1 github.com/couchbase/gocb/v2 v2.6.3 github.com/crewjam/rfc5424 v0.1.0 github.com/denisenkom/go-mssqldb v0.12.3 github.com/envoyproxy/protoc-gen-validate v1.0.2 github.com/fatih/color v1.15.0 github.com/felixge/fgprof v0.9.3 github.com/getsentry/sentry-go v0.24.1 github.com/go-errors/errors v1.4.2 github.com/go-git/go-git/v5 v5.8.1 github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-logr/logr v1.2.4 github.com/go-logr/zapr v1.2.4 github.com/go-redis/redis v6.15.9+incompatible github.com/go-sql-driver/mysql v1.7.1 github.com/gobwas/glob v0.2.3 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/go-cmp v0.5.9 github.com/google/go-containerregistry v0.15.2 github.com/google/go-github/v42 v42.0.0 github.com/googleapis/gax-go/v2 v2.12.0 github.com/h2non/filetype v1.1.3 github.com/hashicorp/go-retryablehttp v0.7.4 github.com/hashicorp/golang-lru v0.5.1 github.com/jlaffaye/ftp v0.2.0 github.com/joho/godotenv v1.5.1 github.com/jpillora/overseer v1.1.6 github.com/kylelemons/godebug v1.1.0 github.com/launchdarkly/go-server-sdk/v6 v6.1.0 github.com/lib/pq v1.10.9 github.com/lrstanley/bubblezone v0.0.0-20221222153816-e95291e2243e github.com/marusama/semaphore/v2 v2.5.0 github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-sqlite3 v1.14.17 github.com/mholt/archiver/v4 v4.0.0-alpha.8 github.com/muesli/reflow v0.3.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/paulbellamy/ratecounter v0.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 github.com/rabbitmq/amqp091-go v1.8.1 github.com/sergi/go-diff v1.3.1 github.com/snowflakedb/gosnowflake v1.6.23 github.com/stretchr/testify v1.8.4 github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 github.com/xanzy/go-gitlab v0.92.3 go.mongodb.org/mongo-driver v1.12.1 go.uber.org/mock v0.2.0 go.uber.org/zap v1.24.0 golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230206171751-46f607a40771 golang.org/x/oauth2 v0.12.0 golang.org/x/sync v0.3.0 golang.org/x/text v0.13.0 google.golang.org/api v0.132.0 google.golang.org/protobuf v1.31.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/h2non/gock.v1 v1.1.2 sigs.k8s.io/yaml v1.3.0 ) require ( cloud.google.com/go v0.110.4 // indirect cloud.google.com/go/compute v1.20.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.24 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/alecthomas/chroma v0.10.0 // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/apache/arrow/go/v12 v12.0.1 // indirect github.com/apache/thrift v0.16.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2 v1.17.7 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.18 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/benbjohnson/clock v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bodgit/plumbing v1.2.0 // indirect github.com/bodgit/sevenzip v1.3.0 // indirect github.com/bodgit/windows v1.0.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/connesc/cipherio v0.2.1 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/couchbase/gocbcore/v10 v10.2.4 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/cli v23.0.5+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker v23.0.5+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/goccy/go-json v0.10.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v23.1.21+incompatible // indirect github.com/google/go-github/v55 v55.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.3.1 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gregjones/httpcache v0.0.0-20171119193500-2bcd89a1743f // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/s3 v1.1.4 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/asmfmt v1.3.2 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/launchdarkly/ccache v1.1.0 // indirect github.com/launchdarkly/eventsource v1.6.2 // indirect github.com/launchdarkly/go-jsonstream/v3 v3.0.0 // indirect github.com/launchdarkly/go-sdk-common/v3 v3.0.1 // indirect github.com/launchdarkly/go-sdk-events/v2 v2.0.1 // indirect github.com/launchdarkly/go-semver v1.0.2 // indirect github.com/launchdarkly/go-server-sdk-evaluation/v2 v2.0.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/microcosm-cc/bluemonday v1.0.23 // indirect github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/montanaflynn/stats v0.7.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.1 // indirect github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.23.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rivo/uniseg v0.4.2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/skeema/knownhosts v1.2.0 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/yuin/goldmark v1.5.2 // indirect github.com/yuin/goldmark-emoji v1.0.1 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/grpc v1.56.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) trufflehog-3.60.0/go.sum000066400000000000000000002771261451332102400151120ustar00rootroot00000000000000cloud.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= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 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.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= 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 v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= 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.11.1 h1:cLTCwAjFh9fKvU6F13Y4L9vPcx9yiWPyWXE4+zkuEQs= cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= 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.33.0 h1:PVrDOkIC8qQVa1P3SXGpQvfuJhN2LHOoyZvWs8D2X5M= cloud.google.com/go/storage v1.33.0/go.mod h1:Hhh/dogNRGca7IWv1RC2YqEn0c0G77ctA/OxflYkiD8= 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= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 h1:rTnT/Jrcm+figWlYz4Ixzt0SJVR2cMC8lvZcimipiEY= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 h1:u/LLAOFgsMv7HmNL4Qufg58y+qElGOt5qv0z1mURkRY= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0/go.mod h1:2e8rMJtl2+2j+HXbTBwnyGpm5Nou7KhvSfxOq8JpTag= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 h1:hVeq+yCyUi+MsoO/CU95yqCIcdzra5ovzk8Q2BBpV2M= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8a+4nPE9g= github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/TheZeroSlave/zapsentry v1.17.0 h1:RIQCG89U7vWWZVmmCxeUz/g32WEcAYXUrXHoMlbSDmc= github.com/TheZeroSlave/zapsentry v1.17.0/go.mod h1:D1YMfSuu6xnkhwFXxrronesmsiyDhIqo+86I3Ok+r64= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 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-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg= github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 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/aws/aws-sdk-go v1.45.19 h1:+4yXWhldhCVXWFOQRF99ZTJ92t4DtoHROZIbN7Ujk/U= github.com/aws/aws-sdk-go v1.45.19/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw= github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY= github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c= github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59 h1:E3Y+OfzOK1+rmRo/K2G0ml8Vs+Xqk0kOnf4nS0kUtBc= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59/go.mod h1:1M4PLSBUVfBI0aP+C9XI7SM6kZPCGYyI6izWz0TGprE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ= github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s= github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM= github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 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/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 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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/disk-buffer-reader v0.1.7 h1:oPgDjsbeqErOrFJeWID/51LhIOGjU3cqN1Tj3V6RiwE= github.com/bill-rich/disk-buffer-reader v0.1.7/go.mod h1:VVzzsK1Ac2AnpOfp/5r9JlIFaFkZ9uSf7zisZayCt0Y= 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.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM= github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8= github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY= github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM= github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bradleyfalzon/ghinstallation/v2 v2.7.0 h1:ranXaC3Zz/F6G/f0Joj3LrFp2OzOKfJZev5Q7OaMc88= github.com/bradleyfalzon/ghinstallation/v2 v2.7.0/go.mod h1:ymxfmloxXBFXvvF1KpeUhOQM6Dfz9NYtfvTiJyk82UE= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw= github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/couchbase/gocb/v2 v2.6.3 h1:5RsMo+RRfK0mVxHLAfpBz3/tHlgXZb1WBNItLk9Ab+c= github.com/couchbase/gocb/v2 v2.6.3/go.mod h1:yF5F6BHTZ/ZowhEuZbySbXrlI4rHd1TIhm5azOaMbJU= github.com/couchbase/gocbcore/v10 v10.2.4 h1:TDTQ1mSBUw9eajuV71ZDJcLRbmfGcsvZV9SMeUDAVGc= github.com/couchbase/gocbcore/v10 v10.2.4/go.mod h1:lYQIIk+tzoMcwtwU5GzPbDdqEkwkH3isI2rkSpfL0oM= github.com/couchbaselabs/gocaves/client v0.0.0-20230307083111-cc3960c624b1/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY= github.com/couchbaselabs/gocaves/client v0.0.0-20230404095311-05e3ba4f0259 h1:2TXy68EGEzIMHOx9UvczR5ApVecwCfQZ0LjkmwMI6g4= github.com/couchbaselabs/gocaves/client v0.0.0-20230404095311-05e3ba4f0259/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/rfc5424 v0.1.0 h1:MSeXJm22oKovLzWj44AHwaItjIMUMugYGkEzfa831H8= github.com/crewjam/rfc5424 v0.1.0/go.mod h1:RCi9M3xHVOeerf6ULZzqv2xOGRO/zYaVUeRyPnBW3gQ= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw= github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 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/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v23.0.5+incompatible h1:DaxtlTJjFSnLOXVNUBU1+6kXGz2lpDoEAH6QoxaSg8k= github.com/docker/docker v23.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/getsentry/sentry-go v0.24.1 h1:W6/0GyTy8J6ge6lVCc94WB6Gx2ZuLrgopnn9w8Hiwuk= github.com/getsentry/sentry-go v0.24.1/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= 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-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 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/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/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/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= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/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/flatbuffers v23.1.21+incompatible h1:bUqzx/MXCDxuS0hRJL2EfjyZL3uQrPbMocUa8zGqsTA= github.com/google/flatbuffers v23.1.21+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec= github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg= github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= 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.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20171119193500-2bcd89a1743f h1:kOkUP6rcVVqC+KlKKENKtgfFfJyDySYhqL9srXooghY= github.com/gregjones/httpcache v0.0.0-20171119193500-2bcd89a1743f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 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.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 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/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/s3 v1.1.4 h1:YCCKDWzb/Ye9EBNd83ATRF/8wPEy0xd43Rezb6u6fzc= github.com/jpillora/s3 v1.1.4/go.mod h1:yedE603V+crlFi1Kl/5vZJaBu9pUzE9wvKegU/lF2zs= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003 h1:vJ0Snvo+SLMY72r5J4sEfkuE7AFbixEP2qRbEcum/wA= github.com/karlseguin/expect v1.0.2-0.20190806010014-778a5f0c6003/go.mod h1:zNBxMY8P21owkeogJELCLeHIt+voOSduHYTFUbwRAV8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/launchdarkly/ccache v1.1.0 h1:voD1M+ZJXR3MREOKtBwgTF9hYHl1jg+vFKS/+VAkR2k= github.com/launchdarkly/ccache v1.1.0/go.mod h1:TlxzrlnzvYeXiLHmesMuvoZetu4Z97cV1SsdqqBJi1Q= github.com/launchdarkly/eventsource v1.6.2 h1:5SbcIqzUomn+/zmJDrkb4LYw7ryoKFzH/0TbR0/3Bdg= github.com/launchdarkly/eventsource v1.6.2/go.mod h1:LHxSeb4OnqznNZxCSXbFghxS/CjIQfzHovNoAqbO/Wk= github.com/launchdarkly/go-jsonstream/v3 v3.0.0 h1:qJF/WI09EUJ7kSpmP5d1Rhc81NQdYUhP17McKfUq17E= github.com/launchdarkly/go-jsonstream/v3 v3.0.0/go.mod h1:/1Gyml6fnD309JOvunOSfyysWbZ/ZzcA120gF/cQtC4= github.com/launchdarkly/go-sdk-common/v3 v3.0.1 h1:rVdLusAIViduNvyjNKy06RA+SPwk0Eq+NocNd1opDhk= github.com/launchdarkly/go-sdk-common/v3 v3.0.1/go.mod h1:H/zISoCNhviHTTqqBjIKQy2YgSHT8ioL1FtgBKpiEGg= github.com/launchdarkly/go-sdk-events/v2 v2.0.1 h1:vnUN2Y7og/5wtOCcCZW7wYpmZcS++GAyclasc7gaTIY= github.com/launchdarkly/go-sdk-events/v2 v2.0.1/go.mod h1:Msqbl6brgFO83RUxmLaJAUx2sYG+WKULcy+Vf3+tKww= github.com/launchdarkly/go-semver v1.0.2 h1:sYVRnuKyvxlmQCnCUyDkAhtmzSFRoX6rG2Xa21Mhg+w= github.com/launchdarkly/go-semver v1.0.2/go.mod h1:xFmMwXba5Mb+3h72Z+VeSs9ahCvKo2QFUTHRNHVqR28= github.com/launchdarkly/go-server-sdk-evaluation/v2 v2.0.2 h1:PAM0GvE0nIUBeOkjdiymIEKI+8FFLJ+fEsWTupW1yGU= github.com/launchdarkly/go-server-sdk-evaluation/v2 v2.0.2/go.mod h1:Mztipcz+7ZMatXVun3k/IfPa8IOgUnAqiZawtFh2MRg= github.com/launchdarkly/go-server-sdk/v6 v6.1.0 h1:pp/VBIon5JNbtSw7ih7I5MXN9mXHfo6T5xikKVy52dI= github.com/launchdarkly/go-server-sdk/v6 v6.1.0/go.mod h1:3TfS6vNsNksPT4LGeGuGKtT3zTjTbbOsCUK3nrj1neA= github.com/launchdarkly/go-test-helpers/v2 v2.2.0 h1:L3kGILP/6ewikhzhdNkHy1b5y4zs50LueWenVF0sBbs= github.com/launchdarkly/go-test-helpers/v2 v2.2.0/go.mod h1:L7+th5govYp5oKU9iN7To5PgznBuIjBPn+ejqKR0avw= github.com/launchdarkly/go-test-helpers/v3 v3.0.2 h1:rh0085g1rVJM5qIukdaQ8z1XTWZztbJ49vRZuveqiuU= github.com/launchdarkly/go-test-helpers/v3 v3.0.2/go.mod h1:u2ZvJlc/DDJTFrshWW50tWMZHLVYXofuSHUfTU/eIwM= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lrstanley/bubblezone v0.0.0-20221222153816-e95291e2243e h1:XoxHx8K6ZKoMtjzWOMDuM69LCdjDDsTOtTfWGrT/fns= github.com/lrstanley/bubblezone v0.0.0-20221222153816-e95291e2243e/go.mod h1:v5lEwWaguF1o2MW/ucO0ZIA/IZymdBYJJ+2cMRLE7LU= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM= github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/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.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY= github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= 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.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= 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= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= 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-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/paulbellamy/ratecounter v0.2.0 h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK/zFXVJGs= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 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= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA= github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/gunit v1.1.3 h1:32x+htJCu3aMswhPw3teoJ+PnWPONqdNgaGs6Qt8ZaU= github.com/smartystreets/gunit v1.1.3/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= github.com/snowflakedb/gosnowflake v1.6.23 h1:uO+zMTXJcSHzOm6ks5To8ergNjt5Dy6cr5QtStpRFT8= github.com/snowflakedb/gosnowflake v1.6.23/go.mod h1:KfO4F7bk+aXPUIvBqYxvPhxLlu2/w4TtSC8Rw/yr5Mg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/trufflesecurity/overseer v1.1.7-custom5 h1:xu+Fg6fkSRifUPzUCl7N8HmobJ6WGOkIApGnM7mJS6w= github.com/trufflesecurity/overseer v1.1.7-custom5/go.mod h1:nT9w37AiO1Nop2VhVhNfzAFaPjthvxgpDV3XKsxYkcI= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/xanzy/go-gitlab v0.92.3 h1:bMtUHSV5BIhKeka6RyjLOOMZ31byVGDN5pGWmqBsIUs= github.com/xanzy/go-gitlab v0.92.3/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/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.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= 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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/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-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 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= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 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.4.2/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20210220032951-036812b2e83c/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 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= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 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.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc= google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0= 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= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 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-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8= google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU= google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= 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= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= 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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= 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= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= trufflehog-3.60.0/hack/000077500000000000000000000000001451332102400146465ustar00rootroot00000000000000trufflehog-3.60.0/hack/Dockerfile.protos000066400000000000000000000016671451332102400201770ustar00rootroot00000000000000# trufflesecurity/protos:1.18-0 FROM golang:1.21-bullseye ARG TARGETARCH ARG TARGETOS ENV PROTOC_VER=3.20.0 ENV GORELEASER_VER=v1.7.0 ENV SQLBOILER_VER=v4.9.0 ENV PROTOC_GEN_GO_VER=v1.5.2 RUN echo "building $TARGETARCH" RUN apt-get update; apt-get install -y --no-install-recommends git unzip && rm -rf /var/lib/apt/lists/* RUN set -e; \ arch=$(echo $TARGETARCH | sed -e s/amd64/x86_64/ -e s/arm64/aarch_64/); \ wget -q https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-${TARGETOS}-${arch}.zip && unzip protoc-${PROTOC_VER}-${TARGETOS}-${arch}.zip -d /usr/local RUN go install "github.com/golang/protobuf/protoc-gen-go@${PROTOC_GEN_GO_VER}" RUN GO111MODULE=off go get -d "github.com/envoyproxy/protoc-gen-validate" && \ cd $GOPATH/src/github.com/envoyproxy/protoc-gen-validate && \ git checkout v0.6.7 && \ ln -s /usr/local/protoc/include/google google && \ make build CMD ["bash"] trufflehog-3.60.0/hack/bench/000077500000000000000000000000001451332102400157255ustar00rootroot00000000000000trufflehog-3.60.0/hack/bench/plot.gp000066400000000000000000000004101451332102400172260ustar00rootroot00000000000000set terminal png size 800,600 set output "hack/bench/versions.png" set title "User Time vs. Version" set xlabel "Version" set ylabel "Average User Time (s)" set xtics rotate by -45 plot "hack/bench/plot.txt" using 2:xtic(1) with linespoints linestyle 1 notitle trufflehog-3.60.0/hack/bench/plot.sh000077500000000000000000000005161451332102400172440ustar00rootroot00000000000000#!/bin/bash if [ $# -ne 2 ]; then echo "Usage: $0 " exit 1 fi # Get the number of versions back to test from command line argument num_versions="$2" test_repo="$1" bash hack/bench/versions.sh $test_repo $num_versions | tee hack/bench/plot.txt gnuplot hack/bench/plot.gp trufflehog-3.60.0/hack/bench/plot.txt000066400000000000000000000005501451332102400174440ustar00rootroot00000000000000v3.33.0: 1.402 v3.32.2: 1.298 v3.32.1: 1.332 v3.32.0: 1.348 v3.31.6: 2.470 v3.31.5: 2.462 v3.31.4: 2.460 v3.31.3: 2.418 v3.31.2: 1.384 v3.31.1: 1.344 v3.31.0: 1.354 v3.30.0: 1.392 v3.29.1: 1.382 v3.29.0: 1.340 v3.28.7: 1.380 v3.28.6: 1.308 v3.28.5: 2.596 v3.28.4: 2.554 v3.28.3: 2.582 v3.28.1: 2.578 v3.28.2: 2.566 v3.28.0: 2.552 v3.27.1: 2.574 v3.26.0: 2.538 trufflehog-3.60.0/hack/bench/versions.png000066400000000000000000000224461451332102400203130ustar00rootroot00000000000000‰PNG  IHDR X­¨rB2PLTE˙˙˙   ˙Ā€˙Ā˙îîĀ@ČČAiá˙Ā €@Ā€˙0`€‹@€˙€˙˙ÔĨ**˙˙@āĐ333MMMfff™™™ŗŗŗĀĀĀĖĖĖååå˙˙˙đ22î­ØæđUđā˙˙îŨ‚˙ļÁ¯îî˙×˙d˙"‹".‹W˙‹p€Í‡Îë˙˙˙˙ÎŅ˙“˙Pđ€€˙Eú€ré–zđæŒŊˇk¸† õõÜ € ˙Ĩî‚î”ĶŨ ŨP@Uk/€€€@€@€€`Ā€`˙€€˙€@˙ @˙ `˙ p˙ĀĀ˙˙€˙˙Ā͡žđ˙đ ļÍÁ˙ÁͰ|˙@ ˙ žžžŋŋŋŸŸŸßßß???___?^ø pHYsÄÄ•+ IDATxœí–Ŧ Eeîgցi˙[S›EÔĘĒwĪųĄĄŧÄÖ,€ęē˙Įë8¨1#šÉ)†+SđŦY‚ã%Žĸyn¤[ĪÍķ6HAdĻyß XÄÖύmû!/LŅͅLûŸiN4 îv.dĘö¸ŧLJcσ[š~&U7sK?†’^…ĢÆöSŦš¤ũ)¤X°H;JĢn$į­*ĘNÆØĒú8 ¤-ĘMaĢÆT‹ƒûFĒn1ˇ41œ ö˙ėŌļ-ÉŗŪ€Åǰ`Čo–UõcŗļË_ãŗ@šî˜fÂ|pLŪŽæk}íNĢÆ§û’NeŨ*Ĩ7;ú@ZgĸČmö[Q—íŲ`õ[|H÷Ķn4w™îč~;Ãúĩ´ øŨQ›Æļ†6Æ3YÛ8°H;ŲéåxI×ú äwđĐJÕũđ×Ō‚îüЙÚ4ū;°ũ0\áĒ Šqv4žlcėītų ¤ZļVäų8ÛęZZ–ÔM?‡Ú7Ūˆ3`Ę8b›y+oą¸Č|pO^×ŋ:›MÁvanϟŽī~´^ƒ@ €ŨeÖ*ī/•YeĒģœÔ_EōČ|pOÕOļĻ–Tã•ÛUã́ëĢX`DˇũĐoZ”ĩ1Ķ>H¯LGõgĸ_KųB$ŋ)×Ēņ߁›} @,ōåŊÖŨ—:ņå4&ęūώQ!vŧ ĐSM_îčžß–­ž ø}CĄē|š Ÿåg Ö ėø-;ĘáKÖXƒ° ú=wŦŋ‰“ԝŋÔđĻ*~_G[?•hqQÍĪ­Ŧv1|z}Œß¤^^æu[šđ!Ye…~=oúo eŽ]áOąüēi6‹ôoF¯Đ¯WM˙eČŋg÷UÃ35ĘÍ3ūžŊBŋ^ $sIäēōSÃČˆ”•š6=ŒņS‰ŧČÅúIRYĄ_9•ȇoŽ–oúE"~Å~ĢōߎĶĻøuÔĢÉqŦ€´ŦFšsŊžŠ× âo˙3ˇfȀ@@vcÜĢŌxØõË!”Wâ@@öíR"‹áîŋ9Ęs\ž @Äá¯öķąwkÖtŌöop?ČPÛŊ}"NFŲņœˆpIņh‰˛¨ōįôD8Îërú÷ßO&~GÎĪ]œĻQ€ctÎķ 0—Ŗ–Ús_#vŲŋ{qšD΁ß_Č v­öđņļÔįŗ?D\$č^ÆšÉlš€y×Z}†@—ŋŖxąŗz_™ĢŖ!@O<2ŧPˆ$l>@ €ļ_Iž˜A € ļyN+!R &Ęĩ7D’aЍŠ°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇR°ˇ"T áo$áoåŠũÆЉ†@ R­[ R{5RS)Z —o€„|Ȑš,Ņɖīî>zc*B†ÔdiČöĮ‡øÕGHM–R—^kœ!5SˆĢņFjĻ w)$â‰Ô<éˆO]HÄ ŠY.’nƒD<š#ÄĢhäąų@|[;H›Äŋ-ąƒ€ąš@î´&vŧFlf {íA#ĮˆM rģIącá b“dĩ¨f+Tļŧ ÛmbÅ}$Ļ‚vĐVĢ3FĻá "rTä4@ ŧ-|Éi€@x[ø’Ķ X ēMōȸä4@ ĖM|Éi€@˜›ø’́°ˇÁÉY€@ØÛāä,@ ėmđGr ö6ø#9 {ü‘œ„Ŋ ūHÎÂŪ$gaoƒ?’ŗ°ˇÁŅI+(Ũ&zlø": #ܝ„ŋîˆNō+ŧä Vx#:ČŦđFt /Xáč@ _°ÂŅ9€@ž`…7ĸs|Á oDįų‚ۈÎō+ŧä Vx#:bŠ×D?d§ųˆžČÎō;|‘˛Ą›Ļ(?PZņ‰…ė P Ũē°Yiʃ”VnÄBvˆ†nelûwSė?PZš Ų ēš9ųFvH‡n]œ|€@#;”C×.—k%ČÎáĐ­Šæä"Ų ēUQŸ|€@D#;dC×qūh­ PŲō‰…Ô ĐZ§>œAĸõšÔááė P ŨՔj=ŋ"´âOŧ^“=><ĸĄ[ŽįŖ*ËM5 ļr$˛ õfE$Âã‡@žc‰'Âã‡@žc‰'Âã‡@žc‰'Âã‡@žc‰'Âã‡@žc‰'Âã‡@žc‰'Âã‡@žc‰'Âã‡@žc‰'Âã*˜Ŋ&|„\!<|äSļ"<|äSļ"<|äSļ"<|äSļ"<|äSļø!=zäSļø!=zäSļø!=zŋĄkķĻnrØ !H,¤Gī1tĢîYíu]ˇįáŦЁÄBzô×C×u9|ģŧ*ëÕK (­ÄBzô×Cˇļ‹vûŧ*+Ä@ ąŊĖEzÜ^“>FœHŪčÚ˛ē>čĩ hHŪkčÚbxō• j…$ԃ÷ēu“eES5Ī žV@ĸ!=x¯Ą[T™mOÕãqˆEzđ^C×tī­õ=øąB hHŪoŠ•Ûîyí%ĻX Ŧ1Czđ~‹tcÚYVfžíúZ!‰†ôā=‡Žū i… $âcĮFá×ŦņB|ė×C7w|ĸŗB  ņą_ŨĻČ[čUž}ˇ™Z XˆŨcčÚÚÔMžįMaV7.ŌZĄ$v¯‰%įˆŨī*VĢ cŠįߘ‚@¤">t‘‹t$âC‡@>hâC‡@>hâC‡@>hâC‡@>hâC÷ē•a…$âC÷ēeaLfž>ô‘‹øĐŊ†nišŌdųgîæ…@ĸ!>tŋo–ũŨWĶZĄ‰…üČ}ŋQ8ũ g…ŽčŨ&œœ ?đ;gō#gøŊ&œœ ?p¯Ą››ÜšĒ|üh^D*ō÷ēšéxvĢģŋ* hČÜsčVeųâÁŠˆTä.q'‰†üĀũ†neÂZĄ‰†üĀ=7 G‚Z!‰†üĀũ.ķ6Õ@P+d@ Ґ¸ßFá‹õšŋ2 hČÜīŅŖ6†2 hČÜkčVESöøļÚŊösy0"ų{î¤ß[¤×…mö …Hˆ‚rŒü¸ũéų­EHÕŋŠĒYÜšE~ÜŪwķŪ&_Ԋ*& ĸ lŋEú“oJÕŠÎ H4„íûÂŧįFÃ6Ų‰†‚°ũĻX÷wŌĢÕcŽ!™(;ĐĐ­ŠÕëÚ ™(Ûëũ ųßVĢÍk^ŪĘu$ĸÃö´æūkĢœA„ĸ ėģo˜ōĸØž‘‰‚°ŊÎ 7ųŨ?ī.B 2Qv„iÛ$ŊĻ`¨  jäÃF“Ŗ j43áŦDCAÔ)fÂYĄ#M§)*(ˆZŪ ‰‡‚¨!/[MŒ† !/[MŒ† ¯‡îķŽŪąB AË{˛" AC _ļš AC _ļš AC _ļš Aû}åöKOVLÔkË 1Ë{ô( 1û=ËÆ°B 1{>YņūSMî[Ą!U§i,[4ÄęŠ&ŦЁÄCCĖâŽbA ņĐŗ÷sl +@ ņĐŗßĐ-‹vze/A ™hˆŲ÷ŅŖĨi—ęOŅëo…$*BöģĖ[öÚ/|Ŗ‰†ŠŊ_đûÎ H4T„|į Râ ÂĪnBT„ėšQ˜[S•6 9N†Šˆũ†îđ’Âįß-„@$ĸ"bĪĄ[•å›[z!‰¨ˆØčÚ ‰'tĻb¸ŦPą×ĐíŽīvO¤ļA­ÄCEÄ~/ņlWES5ÛˇĐZ!‰‡Šˆũ.ķV™mOÕöA x¨ˆØwŖ0/|~l…$*"ö›båļ{ŠZ‰)KËŠPąß"Ũ˜v–•}áfE$*"öēvú+¤ hčXÚ7 öšŽ3Ŗ#ŪëĄkGŪ<úˆŽx=^đŖxŧ@$ĸ#^˙3HŲ`'Ģé$čˆ÷ÎĐÍų_æMŲi:ˌŽxī ŨŠ˙Ļ xčˆ÷ÖĐåŋ“ÄCGŧw†îځÄCGŧ7†nUķ˙Ę-ņ^Ũz¤0×čˆDtÄë/æų6"ņ ģÕ‰‡Žx!!ÆãŖ#\Dˆņøčb<:Jĸ…@„Ž’heŊ@'m§)2#Jĸ•õ$Jĸ•õ$Jĸ•õ$Jĸ%|Ž‹Û)YmīZ!‰‡’hé^ c‹Š­ŌØvBfoZ!‰‡’hÉ^ SšY Ũŗ|ŗbņ:DJĸĨzŽ5y>ĩÕkŖV'Ôæãĸ$XĒčTU–¯ĻXĨž)VjķqQ,áН’5íųf9ƒ@äĄ$XŋEúHŨXĮQŗ@ęĸ;ƒ,ČCI°^Cˇ1E“7…ij×Ŗą&Ø~GqqB@ĸ$X? ëíŧqžaRDiēåŠ5ķĸe|6ãS'}IŨgŠíĮD~Ŧ7m1 õîšXŽã'Tũy&Á$u§Ĩļ-ąú Äö˙ØÂųļYM[Áņ× Š;-ĩũ˜h‰ÕoŠUt×xģˇLšž>:$īfVyaŠWąRwZjû1ŅĢßĐm~…oč…@ÄĄ%VīÂŧß(|ø’DZbŊ5tŋC‡–X=īÅŸ­ÖĘk’wZrâĄ%TĪEzcšVܟ͛ŧĶ’;-Ąú^æíž˙Ô@ ܈‡–P}ŋQ؉ƒũ t’wZrâĄ%TߝôūŦA¸;-Ąú}Ŗ°(+SV Π܈‡–P}7 ûŊBæũIßgé=ˆ†–Pũž‹ÕߝëūJá{+¯Ißgé=ˆ†–PũéĪĨqÃĘkŌ÷YzbĄ&Ō;wķļōšô–ŪƒX¨‰Ôķą?MŪÔĘkŌwZzbĄ&Rŋ)–yų•@Dj"•ô~ÆĀ…8¨ TŌûAtâ &PIīaĐi \ˆƒš@%Ŋ„A§1p!j•ô~ÆĀ…8¨ ”đũ o­ŧ†A§1p!jĨ{?Čk+oáĐg|ˆ‚š@ÉŪōŪĘ[8ôĸ &PĒ÷ƒPXy ‡>ãāCÔJõ~+īāĐg|ˆ‚š@ũÚ`cXy ‡>ãāC ´Äé9tkSŧ͈đp"ãáEp”„™ų ]ۘîīĢ1CI˜™ĪĐ5u?ĩ‚@ü`âF`tDŲq=tĮ;!?˜¸Qvx ŨaøÁč°¨rĀočv됆ųë˜t7Âĸ"ČīĄÛ]Éǃ[y“NcâFXT9pcčļS­VÃ¤Ķ˜¸AČyx5“NcâFXT9 F \úŒ‹AQäB ?‚ĸ"Č„.~EE1\üŠŠ  jØ8 1Ž@ Ô°q$ b@¨aãH@4Ä8PÃÆ‘€hˆqĄ†#ŅãB GÂĄ Ä ÂĄkįļl÷(ß0VÎ`Ķil ‡‚'膮-ĻļŦ)û?ŦœÂĻĶØ8!N ŨŌĖŠģwQ5‹{à „OŸņņ$ Bœ ēÖäĶ—ĢŨP Q(q‚jčVU6 Ěøk>}ÆĮ“`(q‚pčNig[eûWÔ5Ÿ>ããI0„8F ŨŲ#WēáäJ äG8F ļ˙{~Č" ų΄Hĩȝ­ŒúŒ‘+aų UhžĖËɕ@ȏp&ˆ@ēēÚE:'W!?ÂräŨĖĒ,ē YAŦœĀ¨ĪšųÎHšY‘QŸ1r%ō#œ@ČaäJĸDˆ@8õ'_‚ >Ā%9œ| ‚ø—@ äpō%â\Ãɗ ˆp B+g =ž=Ŧœ €ôøV@ ô°r&Ōã[ĐÃʙHoB+g =ž=Ŧœ €ôøV@ ô°r&Ōã[ĐÃʙHoB+g =ž2ÂĢËxyCôøV@ ôđō†áám€@ĀĖbdGˇ 3wˆ‘Ũ$ĖÜ!Fvt[ 0s‡ŲŅm@ĀĖbdGˇ 3wˆ‘Ũ$ĖÜ!Fvt[ 0s‡ŲŅm@ĀĖbdGˇ 3wˆ‘ŨáÖeÜüĄEvt[ pķ‡ŲŅm@BĀÎ!JDˇ ;‡‘ÛHØ9DˆäØ€@BĀÎ!B$Įvv"9ļ °sˆÉą„€C„HŽí$ė"Drl@ !`į!’c; ;‡‘ÛÂ¯ËøyD‡äØ€@‚ĀĐ%*‡v†.Q!8´#  0t‰ ÁĄĄKTí$ ]ĸBphG@ A`čr#; C—ˆŲ1HēD„ÜȎ@‚ĀĐ%"äFv †.!7˛c  0t‰š‘#@ 쌪O4ȍė$ ,ĸ@l`'@ a`éb; K§(Ø HX:EØĀN ēvÕVŅ„ą˛‡e—ątŠą@7tmąl+7ˆHÄvŲĐ-ÍR Ö@ 2Ø TCך<_´Uä˜b‰Dj\§P ŨĒĘÉ ŦAd"5ŽS‡î,ĘXD&Rã:%ˆ@ęWąR;ŠqB ík)ŋŋ?ĶūĄ3´†g—…ô*eÄ<ŗŠÍ@5[ųë@gkĶ. č…qĶ5˜ģ6 Ã%–i—A 2ˆ%p™eÚeĄÜúÔúĩųT†SA.|œYA Úũë›ū;Ę˗u™f;ņnV 6bĩû’0nõŠø[ũ`'„’ˆwķĘ-Į. 3úĩxxژMB ”ÄŧŨ=Đ/Õ ­žæ/#ˆ_so¤éS—iļÃõû A˛Ë´Ë†…]sūM=–æŸGMĻŲGÜ/L…H/Ķ.gC$ŪŨ;%<”æ8=ÃdMäoČ/ķ.{/‘ģ-<’æbyãŦČ<Ûô@ ÁyˇZ^ųÎŦlu¨Ŗûd“û;éäūD—=åīÎ?žĩw‡WûDļI‰ūĐōkŸÄíâŪH˙͒bLĪŽ9­ö‘l˙Š&Ä9ūL—Ũ턗ĀÜ*;-Ŋ÷cÁ$xėņöikAņ?#ĐnĸœŸn¯6>”m"R<‹4Ëßę2Ÿq`ūøž­tŋ•m ’<8Ž2Í_ë˛ķÛEˆîqšuüāēJöŊlŋįÛI|ī÷3În8\ū$”Ų{ģ$ûƒ>–jŌ=ČfûIwiēƟ÷ĢŖŠéJĶUæę)6smLēKĶUū¨_ĶaÆu›Ä›Ōt•CûUžqQų%‹Éƒ›ėĨG— ÜÅū•Ũ~Ũ.åąÁlí6d[öäĩŠÛd—ÕĶŌöƒĢé}qĀĘŅüĒļ'åuŠÍ°ܘÜ_}Y—î.¸‹oTvûuˇ”Åŗ-LaÖ!—Æ=u™åf[ė_ZÕÎĻ÷Å+Įō+Û)dYÚ&›đФkÖ“ÛĢ/ÛRëŽlo”.ŠĢ6ĘĻ.ÉJĶUžJķîRũ6ä|Ęnnš]˛}KÛbëhú¨8`åH~ul˛Ę—sFûŠÍÆäæęËĻtw1ÁYė_Ųļįɍ4ߔĻĢü+͇iņöZÖÔ§Ļ9(ö,͚æ÷—wqĀĘqüęŲ,6~ĨMH},7&mvt ūrÛrwšĀ]ė_šũmŧņMiēĘCi9ödŊģ–5T°ÃTl[ėSÚŊŗ¸Käî‘äî―ÃûÕS6í<ęčŦš›vūôííbŌf§W_KÍŖËîbĪĘGÃô]iēĘmi5œŽâÔõi5ŽUöwÜ]—–Ļąe‘—õqĶįÅ+‡öĢĨjGf–Ú>ŲuāW8¯ÍŦ.Ü-ž¨Üs2L JĶTß)üĶɊ˛ęN ]–ĪŨŋ,íUÚvyzp{…ģ8`åĀ~uÉ8™EĩĨC˛Ë°w/:¯Íl.&Ü+ž¨ģųF_Ú˙īh;h¨;\9ø­ôĢ|ŧ 0(ß4îžŊĘæE:=ŗy|Ãė×ųŠƒkëåϟŗéėbÃgČįévm'­‡ú*ŋØ|~ÔEČošvėžÍ•fĶÎë'ˇĮt‘ėWųŠƒkëåĻßÅö™sÃgÜɜßūžÅGķĢUåGÛ COƒēųMĶîŨŗ—ŲtĨķúÉmį1]%ûUžâ°ÜãÚy˛ē–uÜm“˙ûbgĶë Ÿ}ņ¸}VŸZÎzPē܍ÔEČ/š^ížŅgĶ•Ng63wLÉ~•¯8,ļŠŽĩ­.įîJWūī‹ŨM/7|Ž+wsŠŗĘŨ„`øņqĶĶ6hÜ .B~ÕôØ,ôK)ø&Ãû×=ĻڙÉģgöÛÚĩĻXsIûC( hbxlZĶŊîšl'\­ŒąUÕ ŖęÄĶūÛtOę„ŌŊĸãė ėȤ?%˜ŧę~/‰á=”v(îJēŗGˇHž"í~-˛čF~iĒaĩŪūgŧnUÃÃÖM^ö-ė3hščNíYĄ{Æ`Į(ƒn~Õ/\āĒ  “ēŠúåĮī4Ķ"Ã6Ŋ(pĒiWææˇSد4ĻUx÷ŗÕú¨†KˇM˙Ļ™|”A÷fĻĒV$ķU,(¤ŽXåããČĮw6u/÷ŗÛ} ųåq†HQÁ‹"IENDŽB`‚trufflehog-3.60.0/hack/bench/versions.sh000066400000000000000000000040211451332102400201260ustar00rootroot00000000000000#!/bin/bash if [ $# -ne 2 ]; then echo "Usage: $0 " exit 1 fi # Get the number of versions back to test from command line argument num_versions="$2" test_repo="$1" num_iterations=5 # Create a temporary folder to clone the repository repo_tmp=$(mktemp -d) # Set up a trap to remove the temporary folder on exit or failure trap "rm -rf $repo_tmp" EXIT # Clone the test repository to a temporary folder git clone --quiet "$test_repo" $repo_tmp # Get list of git tags, sorted from newest to oldest tags=$(git tag --sort=-creatordate) # Counter to keep track of number of tags checked out count=0 # Loop over tags and checkout each one in turn, up to the specified number of versions for tag in $tags do if [[ $count -eq $num_versions ]]; then break fi # Skip RC tags if [[ $tag == *"rc"* ]]; then continue fi # Skip alpha tags if [[ $tag == *"alpha"* ]]; then continue fi # Use git checkout with the quiet flag to suppress output git checkout $tag --quiet # Run make install with suppressed output make install > /dev/null # Initialize the variable to store the sum of user times user_time_sum=0 # Run each iteration 5 times and calculate the average user time for i in {1..$num_iterations} do # Run trufflehog with suppressed output and capture user time with /usr/bin/time tmpfile=$(mktemp) /usr/bin/time -o $tmpfile trufflehog git "file://$repo_tmp" --no-verification --no-update >/dev/null 2>&1 time_output=$(cat $tmpfile) rm $tmpfile # Extract the user time from the output user_time=$(echo $time_output | awk '{print $3}') # Add the user time to the sum user_time_sum=$(echo "$user_time_sum + $user_time" | bc) done # Calculate the average user time average_user_time=$(echo "scale=3; $user_time_sum / $num_iterations" | bc) # Print the average user time output for this iteration in the specified format echo "$tag: $average_user_time" # Increment the counter count=$((count+1)) done trufflehog-3.60.0/hack/docs/000077500000000000000000000000001451332102400155765ustar00rootroot00000000000000trufflehog-3.60.0/hack/docs/Adding_Detectors_Internal.md000066400000000000000000000167101451332102400231630ustar00rootroot00000000000000# Secret Detectors Secret Detectors have these two major functions: 1. Given some bytes, extract possible secrets, typically using a regex. 2. Validate the secrets against the target API, typically using a HTTP client. The purpose of Secret Detectors is to discover secrets with exceptionally high signal. High rates of false positives are not accepted. ## Table of Contents - [Secret Detectors](#secret-detectors) - [Table of Contents](#table-of-contents) - [Getting Started](#getting-started) - [Sourcing Guidelines](#sourcing-guidelines) - [Development Guidelines](#development-guidelines) - [Development Dependencies](#development-dependencies) - [Creating a new Secret Scanner](#creating-a-new-secret-detector) - [Addendum](#addendum) - [Managing Test Secrets](#managing-test-secrets) - [Setting up Google Cloud SDK](#setting-up-google-cloud-sdk) ## Getting Started ### Sourcing Guidelines We are interested in detectors for services that meet at least one of these criteria - host data (they store any sort of data provided) - have paid services (having a free or trial tier is okay though) If you think that something should be included outside of these guidelines, please let us know. ### Development Guidelines - When reasonable, favor using the `net/http` library to make requests instead of bringing in another library. - Use the [`common.SaneHttpClient`](pkg/common/http.go) for the `http.Client` whenever possible. - We recommend an editor with gopls integration (such as Vscode with Go plugin) for benefits like easily running tests, autocompletion, linting, type checking, etc. ### Development Dependencies - A GitLab account - A Google account - [Google Cloud SDK installed](#setting-up-google-cloud-sdk) - Go 1.17+ - Make ### Adding New Token Formats to an Existing Scanner In some instances, services will update their token format, requiring a new regex to properly detect secrets in addition to supporting the previous token format. Accomodating this can be done without adding a net-new detector. [We provide a `Versioner` interface](https://github.com/trufflesecurity/trufflehog/blob/e18cfd5e0af1469a9f05b8d5732bcc94c39da49c/pkg/detectors/detectors.go#L30) that can be implemented. 1. Create a copy of the package and append `_v2` to the package and file names. Ex: `/` -> `_v2`, `.go` -> `_v2.go` Note: Be sure to update the tests to reference the new secret values in GSM, or the tests will fail. 2. Implement the `Versioner` interface. [GitHub example implementation.](/pkg/detectors/github_old/github_old.go#L22) 3. Proceed from step 3 of [Creating a new Secret Scanner](#creating-a-new-secret-scanner) ### Creating a new Secret Scanner 1. Identify the Secret Detector name from the [/proto/detectors.proto](/proto/detectors.proto) `DetectorType` enum. 2. Generate the Secret Detector ```bash go run hack/generate/generate.go detector ``` 3. Complete the secret detector. The previous step templated a boilerplate + some example code as a package in the `pkg/detectors` folder for you to work on. The secret detector can be completed with these general steps: 1. Add the test secret to GCP Secrets. See [managing test secrets](#managing-test-secrets) 2. Update the pattern regex and keywords. Try iterating with [regex101.com](http://regex101.com/). 3. 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). 4. Update the tests with these test cases at minimum: 1. Found and verified (using a credential loaded from GCP Secrets) 2. Found and unverified (determinately, i.e. the secret is invalid) 3. Found and unverified (indeterminately due to timeout) 4. Found and unverified (indeterminately due to an unexpected API response) 5. Not found 6. Any false positive cases that you come across 5. Create a merge request for review. CI tests must be passing. ## Addendum ### Verification indeterminacy There are two types of reasons that secret verification can fail: * The candidate secret is not actually a valid secret. * Something went wrong in the process unrelated to the candidate secret, such as a transient network error or an unexpected API response. In Trufflehog parlance, the first type of verification response is called _determinate_ and the second type is called _indeterminate_. Verification code should distinguish between the two by returning an error object in the result struct **only** for indeterminate failures. In general, a verifier should return an error (indicating an indeterminate failure) in all cases that haven't been explicitly identified as determinate failure states. For example, consider a hypothetical authentication endpoint that returns `200 OK` for valid credentials and `403 Forbidden` for invalid credentials. The verifier for this endpoint could make an HTTP request and use the response status code to decide what to return: * A `200` response would indicate that verification succeeded. (Or maybe any `2xx` response.) * A `403` response would indicate that verification failed **determinately** and no error object should be returned. * Any other response would indicate that verification failed **indeterminately** and an error object should be returned. ### Managing Test Secrets Do not embed test credentials in the test code. Instead, use GCP Secrets Manager. 1. Access the latest secret version for modification. Note: `/tmp/s` is a valid path on Linux. You will need to change that for Windows or OSX, otherwise you will see an error. On Windows you will also need to install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install). ```bash gcloud secrets versions access --project trufflehog-testing --secret detectors5 latest > /tmp/s ``` 2. Add the secret that you need for testing. The command above saved it to `/tmp/s`. The format is standard env file format, ```bash SECRET_TYPE_ONE=value SECRET_TYPE_ONE_INACTIVE=v@lue ``` 3. Update the secret version with your modification. ```bash gcloud secrets versions add --project trufflehog-testing detectors5 --data-file /tmp/s ``` Note: We increment the detectors file name `detectors(n+1)` once the previous one exceeds the max size allowed by GSM (65kb). 4. Access the secret value as shown in the [example code](pkg/detectors/heroku/heroku_test.go). ### Setting up Google Cloud SDK 1. Install the Google Cloud SDK: https://cloud.google.com/sdk/docs/install 2. Authenticate with `gcloud auth login --update-adc` using your Google account ### Adding Protos in Windows 1. Install Ubuntu App in Microsoft Store https://www.microsoft.com/en-us/p/ubuntu/9nblggh4msv6. 2. Install Docker Desktop https://www.docker.com/products/docker-desktop. Enable WSL integration to Ubuntu. In Docker app, go to Settings->Resources->WSL INTEGRATION->enable Ubuntu. 3. Open Ubuntu cli and install `dos2unix`. ```bash sudo apt install dos2unix ``` 4. Identify the `trufflehog` local directory and convert `scripts/gen_proto.sh` file in Unix format. ```bash dos2unix ./scripts/gen_proto.sh ``` 5. Open [/proto/detectors.proto](/proto/detectors.proto) file and add new detectors then save it. Make sure Docker is running and run this in Ubuntu command line. ```bash make protos ``` ### Testing a detector ```bash go test ./pkg/detectors/ -tags=detectors ``` trufflehog-3.60.0/hack/docs/Adding_Detectors_external.md000066400000000000000000000173171451332102400232350ustar00rootroot00000000000000# Secret Detectors Secret Detectors have these two major functions: 1. Given some bytes, extract possible secrets, typically using a regex. 2. Validate the secrets against the target API, typically using a HTTP client. The purpose of Secret Detectors is to discover secrets with exceptionally high signal. High rates of false positives are not accepted. ## Table of Contents - [Secret Detectors](#secret-detectors) * [Table of Contents](#table-of-contents) * [Getting Started](#getting-started) + [Sourcing Guidelines](#sourcing-guidelines) + [Development Guidelines](#development-guidelines) + [Development Dependencies](#development-dependencies) + [Creating a new Secret Detector](#creating-a-new-secret-detector) + [Testing the Detector](#testing-the-detector) * [Addendum](#addendum) + [Adding Protos in Windows](#adding-protos-in-windows) ## Getting Started ### Sourcing Guidelines We are interested in detectors for services that meet at least one of these criteria - host data (they store any sort of data provided) - have paid services (having a free or trial tier is okay though) If you think that something should be included outside of these guidelines, please let us know. ### Development Guidelines - When reasonable, favor using the `net/http` library to make requests instead of bringing in another library. - Use the [`common.SaneHttpClient`](/pkg/common/http.go) for the `http.Client` whenever possible. ### Development Dependencies - Go 1.17+ - Make ### Adding New Token Formats to an Existing Scanner In some instances, services will update their token format, requiring a new regex to properly detect secrets in addition to supporting the previous token format. Accomodating this can be done without adding a net-new detector. [We provide a `Versioner` interface](https://github.com/trufflesecurity/trufflehog/blob/e18cfd5e0af1469a9f05b8d5732bcc94c39da49c/pkg/detectors/detectors.go#L30) that can be implemented. 1. Create a copy of the package and append `_v2` to the package and file names. Ex: `/` -> `_v2`, `.go` -> `_v2.go` Note: Be sure to update the tests to reference the new secret values in GSM, or the tests will fail. 2. Implement the `Versioner` interface. [GitHub example implementation.](/pkg/detectors/github_old/github_old.go#L22) 3. Proceed from step 3 of [Creating a new Secret Scanner](#creating-a-new-secret-scanner) ### Creating a new Secret Detector 1. Add a new Secret Detector enum to the [`DetectorType` list here](/proto/detectors.proto). 2. Run `make protos` to update the `.pb` files. 3. Generate the Secret Detector ```bash go run hack/generate/generate.go detector example: go run hack/generate/generate.go detector SampleAPI ``` 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/`](https://github.com/trufflesecurity/trufflehog/blob/b71ea27a696bdf1c3141f637fda4ee4936c2f2d6/pkg/engine/defaults.go#L9) and [`.Scanner{}`](https://github.com/trufflesecurity/trufflehog/blob/b71ea27a696bdf1c3141f637fda4ee4936c2f2d6/pkg/engine/defaults.go#L1546) 5. Complete the Secret Detector. The previous step templated a boilerplate + some example code as a package in the `pkg/detectors` folder for you to work on. The Secret Detector can be completed with these general steps: 1. Update the pattern regex and keywords. Try iterating with [regex101.com](http://regex101.com/). 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. Create a pull request for review. ### Testing the Detector To ensure the quality of your PR, make sure your tests are passing with verified credentials. 1. Create a file called `.env` with this env file format: ```bash SECRET_TYPE_ONE=value SECRET_TYPE_ONE_INACTIVE=v@lue ``` 2. Export the `TEST_SECRET_FILE` variable, pointing to the env file: ```bash export TEST_SECRET_FILE=".env" ``` The `.env` file should be in the new detector's directory like this: ``` ├── tailscale │   ├── .env │   ├── tailscale.go │   └── tailscale_test.go ``` Now that a `.env` file is present, the test file can load secrets locally. 3. Next, update the tests as necessary. A test file has already been generated by the `go run hack/generate/generate.go` command from earlier. There are 5 cases that have been generated: 1. Found and verified (using a credential loaded from the .env file) 2. Found and unverified (determinately, i.e. the secret is invalid) 3. Found and unverified (indeterminately due to timeout) 4. Found and unverified (indeterminately due to an unexpected API response) 5. Not found Make any necessary updates to the tests. Note there might not be any changes required as the tests generated by the `go run hack/generate/generate.go` command are pretty good. [Here is an exemplary test file for a detector which covers all 5 test cases](https://github.com/trufflesecurity/trufflehog/blob/6f9065b0aae981133a7fa3431c17a5c6213be226/pkg/detectors/browserstack/browserstack_test.go). 4. Now run the tests and check to make sure they are passing âœ”ī¸! ```bash go test ./pkg/detectors/ -tags=detectors ``` If the tests are passing, feel free to open a PR! ## Addendum ### Verification indeterminacy There are two types of reasons that secret verification can fail: * The candidate secret is not actually a valid secret. * Something went wrong in the process unrelated to the candidate secret, such as a transient network error or an unexpected API response. In Trufflehog parlance, the first type of verification response is called _determinate_ and the second type is called _indeterminate_. Verification code should distinguish between the two by returning an error object in the result struct **only** for indeterminate failures. In general, a verifier should return an error (indicating an indeterminate failure) in all cases that haven't been explicitly identified as determinate failure states. For example, consider a hypothetical authentication endpoint that returns `200 OK` for valid credentials and `403 Forbidden` for invalid credentials. The verifier for this endpoint could make an HTTP request and use the response status code to decide what to return: * A `200` response would indicate that verification succeeded. (Or maybe any `2xx` response.) * A `403` response would indicate that verification failed **determinately** and no error object should be returned. * Any other response would indicate that verification failed **indeterminately** and an error object should be returned. ### Adding Protos in Windows 1. Install Ubuntu App in Microsoft Store https://www.microsoft.com/en-us/p/ubuntu/9nblggh4msv6. 2. Install Docker Desktop https://www.docker.com/products/docker-desktop. Enable WSL integration to Ubuntu. In Docker app, go to Settings->Resources->WSL INTEGRATION->enable Ubuntu. 3. Open Ubuntu cli and install `dos2unix`. ```bash sudo apt install dos2unix ``` 4. Identify the `trufflehog` local directory and convert `scripts/gen_proto.sh` file in Unix format. ```bash dos2unix ./scripts/gen_proto.sh ``` 5. Open [/proto/detectors.proto](/proto/detectors.proto) file and add new detectors then save it. Make sure Docker is running and run this in Ubuntu command line. ```bash make protos ``` trufflehog-3.60.0/hack/generate/000077500000000000000000000000001451332102400164405ustar00rootroot00000000000000trufflehog-3.60.0/hack/generate/generate.go000066400000000000000000000067261451332102400205740ustar00rootroot00000000000000package main import ( "fmt" "log" "os" "path/filepath" "strings" "text/template" "github.com/go-errors/errors" "golang.org/x/text/cases" "golang.org/x/text/language" "gopkg.in/alecthomas/kingpin.v2" ) var ( app = kingpin.New("generate", "Generate is used to write new features.") kind = app.Arg("kind", "Kind of thing to generate.").Required().Enum("detector") name = app.Arg("name", "Name of the Source/Detector to generate.").Required().String() nameTitle, nameLower, nameUpper string ) func main() { log.SetFlags(log.Lmsgprefix) log.SetPrefix("😲 [generate] ") kingpin.MustParse(app.Parse(os.Args[1:])) nameTitle = cases.Title(language.AmericanEnglish).String(*name) nameLower = strings.ToLower(*name) nameUpper = strings.ToUpper(*name) switch *kind { case "detector": mustWriteTemplates([]templateJob{ { TemplatePath: "pkg/detectors/alchemy/alchemy.go", WritePath: filepath.Join(folderPath(), nameLower+".go"), ReplaceString: []string{"alchemy"}, }, { TemplatePath: "pkg/detectors/alchemy/alchemy_test.go", WritePath: filepath.Join(folderPath(), nameLower+"_test.go"), ReplaceString: []string{"alchemy"}, }, }) // case "source": // mustWriteTemplates([]templateJob{ // { // TemplatePath: "pkg/sources/filesystem/filesystem.go", // WritePath: filepath.Join(folderPath(), nameLower+".go"), // ReplaceString: []string{"filesystem"}, // }, // { // TemplatePath: "pkg/sources/filesystem/filesystem_test.go", // WritePath: filepath.Join(folderPath(), nameLower+"_test.go"), // ReplaceString: []string{"filesystem"}, // }, // }) } } type templateJob struct { TemplatePath string WritePath string ReplaceString []string } func mustWriteTemplates(jobs []templateJob) { log.Printf("Generating %s %s\n", cases.Title(language.AmericanEnglish).String(*kind), nameTitle) // Make the folder. log.Printf("Creating folder %s\n", folderPath()) err := makeFolder(folderPath()) if err != nil { log.Fatal(err) } // Write the files from templates. for _, job := range jobs { tmplBytes, err := os.ReadFile(job.TemplatePath) if err != nil { log.Fatal(err) } tmplRaw := string(tmplBytes) for _, rplString := range job.ReplaceString { tmplRaw = strings.ReplaceAll(tmplRaw, strings.ToLower(rplString), "<<.NameLower>>") tmplRaw = strings.ReplaceAll(tmplRaw, cases.Title(language.AmericanEnglish).String(rplString), "<<.NameTitle>>") tmplRaw = strings.ReplaceAll(tmplRaw, strings.ToUpper(rplString), "<<.NameUpper>>") } tmpl := template.Must(template.New("main").Delims("<<", ">>").Parse(tmplRaw)) log.Printf("Writing file %s\n", job.WritePath) f, err := os.OpenFile(job.WritePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { log.Fatal(err) } err = tmpl.Execute(f, templateData{ NameTitle: nameTitle, NameLower: nameLower, NameUpper: nameUpper, }) if err != nil { log.Fatal(fmt.Errorf("failed to execute template: %w", err)) } } } type templateData struct { NameTitle string NameLower string NameUpper string } func folderPath() string { return filepath.Join("pkg/", *kind+"s", nameLower) } func makeFolder(path string) error { _, err := os.Stat(path) if os.IsNotExist(err) { err := os.MkdirAll(path, 0755) if err != nil { return errors.New(err) } return nil } return errors.Errorf("%s %s already exists", *kind, *name) } trufflehog-3.60.0/hack/generate/test.sh000077500000000000000000000005011451332102400177520ustar00rootroot00000000000000#!/usr/bin/env bash set -eu function cleanup { rm -rf pkg/detectors/test } trap cleanup EXIT export CGO_ENABLED=0 export FORCE_PASS_DIFF=true echo "████████████ Testing generate Detector" go run hack/generate/generate.go detector Test go test ./pkg/detectors/test -benchmem -bench . echo "" trufflehog-3.60.0/hack/snifftest/000077500000000000000000000000001451332102400166535ustar00rootroot00000000000000trufflehog-3.60.0/hack/snifftest/README.md000066400000000000000000000010631451332102400201320ustar00rootroot00000000000000# snifftest See the help pages with this command, or look further below to get started quickly. ``` go run snifftest/main.go ``` ## Show available secret scanners ``` go run snifftest/main.go show-scanners ``` ## Load a repo into a DB ``` go run snifftest/main.go load --db ~/sdb --repo https://github.com/Netflix/Hystrix.git ``` ## Scan All scanners ``` go run snifftest/main.go scan --db ~/sdb --scanner all --print ``` Particular scanner ``` go run snifftest/main.go scan --db ~/sdb --scanner github --print --print-chunk --fail-threshold 5 ```trufflehog-3.60.0/hack/snifftest/main.go000066400000000000000000000160201451332102400201250ustar00rootroot00000000000000package main import ( "fmt" "os" "reflect" "runtime" "strings" "sync" "sync/atomic" "time" "github.com/paulbellamy/ratecounter" "golang.org/x/sync/semaphore" "gopkg.in/alecthomas/kingpin.v2" "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/decoders" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/engine" "github.com/trufflesecurity/trufflehog/v3/pkg/log" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" "github.com/trufflesecurity/trufflehog/v3/pkg/sources/git" ) var ( // CLI flags and commands app = kingpin.New("Snifftest", "Test secret detectors against data sets.") showDetectorsCmd = app.Command("show-detectors", "Shows the available detectors.") scanCmd = app.Command("scan", "Scans data.") scanCmdDetector = scanCmd.Flag("detector", "Detector to scan with. 'all', or a specific name.").Default("all").String() scanCmdExclude = scanCmd.Flag("exclude", "Detector(s) to exclude").Strings() scanCmdRepo = scanCmd.Flag("repo", "URI to .git repo.").Required().String() scanThreshold = scanCmd.Flag("fail-threshold", "Result threshold that causes failure for a single scanner.").Int() scanPrintRes = scanCmd.Flag("print", "Print results.").Bool() scanPrintChunkRes = scanCmd.Flag("print-chunk", "Print chunks that have results.").Bool() scanVerify = scanCmd.Flag("verify", "Verify found secrets.").Bool() ) func main() { // setup logger logger, flush := log.New("trufflehog", log.WithConsoleSink(os.Stderr)) // make it the default logger for contexts context.SetDefaultLogger(logger) defer func() { _ = flush() }() logFatal := func(err error, message string, keyAndVals ...any) { logger.Error(err, message, keyAndVals...) if err != nil { os.Exit(1) return } os.Exit(0) } ctx, cancel := context.WithTimeout(context.Background(), time.Hour*2) var cancelOnce sync.Once defer cancelOnce.Do(cancel) cmd := kingpin.MustParse(app.Parse(os.Args[1:])) switch cmd { case scanCmd.FullCommand(): chunksChan := make(chan *sources.Chunk, 10000) var wgChunkers sync.WaitGroup sem := semaphore.NewWeighted(int64(runtime.NumCPU())) selectedScanners := map[string]detectors.Detector{} allScanners := getAllScanners() decoders := decoders.DefaultDecoders() input := strings.ToLower(*scanCmdDetector) if input == "all" { selectedScanners = allScanners } else { _, ok := allScanners[input] if !ok { logFatal(fmt.Errorf("invalid input"), "could not find scanner by that name") } selectedScanners[input] = allScanners[input] } if len(selectedScanners) == 0 { logFatal(fmt.Errorf("invalid input"), "no detectors selected") } for _, excluded := range *scanCmdExclude { delete(selectedScanners, excluded) } logger.Info("loaded secret detectors", "count", len(selectedScanners)+3) var wgScanners sync.WaitGroup var chunkCounter uint64 go func() { counter := ratecounter.NewRateCounter(60 * time.Second) var prev uint64 for { time.Sleep(60 * time.Second) counter.Incr(int64(chunkCounter - prev)) prev = chunkCounter logger.Info("chunk scan rate per second", "rate", counter.Rate()/60) } }() resCounter := make(map[string]*uint64) failed := false for i := 0; i < runtime.NumCPU(); i++ { wgScanners.Add(1) go func() { defer wgScanners.Done() for chunk := range chunksChan { for name, scanner := range selectedScanners { for _, dec := range decoders { decoded := dec.FromChunk(&sources.Chunk{Data: chunk.Data}) if decoded != nil { foundKeyword := false for _, kw := range scanner.Keywords() { if strings.Contains(strings.ToLower(string(decoded.Data)), strings.ToLower(kw)) { foundKeyword = true } } if !foundKeyword { continue } res, err := scanner.FromData(ctx, *scanVerify, decoded.Data) if err != nil { logFatal(err, "error scanning chunk") } if len(res) > 0 { if resCounter[name] == nil { zero := uint64(0) resCounter[name] = &zero } atomic.AddUint64(resCounter[name], uint64(len(res))) if *scanThreshold != 0 && int(*resCounter[name]) > *scanThreshold { logger.Error( fmt.Errorf("exceeded result threshold"), "snifftest failed", "scanner", name, "threshold", *scanThreshold, ) failed = true os.Exit(1) } if *scanPrintRes { for _, r := range res { logger := logger.WithValues("secret", name, "meta", chunk.SourceMetadata, "result", string(r.Raw)) if *scanPrintChunkRes { logger = logger.WithValues("chunk", string(decoded.Data)) } logger.Info("result") } } } } } } atomic.AddUint64(&chunkCounter, uint64(1)) } }() } for _, repo := range strings.Split(*scanCmdRepo, ",") { if err := sem.Acquire(ctx, 1); err != nil { logFatal(err, "timed out waiting for semaphore") } wgChunkers.Add(1) go func(r string) { defer sem.Release(1) defer wgChunkers.Done() logger.Info("cloning repo", "repo", r) path, repo, err := git.CloneRepoUsingUnauthenticated(ctx, r) if err != nil { logFatal(err, "error cloning repo", "repo", r) } logger.Info("cloned repo", "repo", r) s := git.NewGit(sourcespb.SourceType_SOURCE_TYPE_GIT, 0, 0, "snifftest", false, runtime.NumCPU(), func(file, email, commit, timestamp, repository string, line int64) *source_metadatapb.MetaData { return &source_metadatapb.MetaData{ Data: &source_metadatapb.MetaData_Git{ Git: &source_metadatapb.Git{ Commit: commit, File: file, Email: email, Repository: repository, Timestamp: timestamp, }, }, } }) logger.Info("scanning repo", "repo", r) err = s.ScanRepo(ctx, repo, path, git.NewScanOptions(), chunksChan) if err != nil { logFatal(err, "error scanning repo") } logger.Info("scanned repo", "repo", r) defer os.RemoveAll(path) }(repo) } go func() { wgChunkers.Wait() close(chunksChan) }() wgScanners.Wait() logger.Info("completed snifftest", "chunks", chunkCounter) for scanner, resultsCount := range resCounter { logger.Info(scanner, "results", *resultsCount) } if failed { os.Exit(1) } case showDetectorsCmd.FullCommand(): for s := range getAllScanners() { fmt.Println(s) } } } func getAllScanners() map[string]detectors.Detector { allScanners := map[string]detectors.Detector{} for _, s := range engine.DefaultDetectors() { secretType := reflect.Indirect(reflect.ValueOf(s)).Type().PkgPath() path := strings.Split(secretType, "/")[len(strings.Split(secretType, "/"))-1] allScanners[path] = s } return allScanners } trufflehog-3.60.0/hack/snifftest/snifftest.sh000077500000000000000000000011611451332102400212160ustar00rootroot00000000000000#!/usr/bin/env bash REPO_ARRAY=( "https://github.com/Netflix/Hystrix.git" # "https://github.com/facebook/flow.git" # "https://github.com/Netflix/vizceral.git" # "https://github.com/Netflix/metaflow.git" # "https://github.com/Netflix/dgs-framework.git" # "https://github.com/Netflix/vector.git" # "https://github.com/expressjs/express.git" ) REPOS=$(printf "%s," "${REPO_ARRAY[@]}" | cut -d "," -f 1-${#REPO_ARRAY[@]}) go run hack/snifftest/main.go scan --exclude privatekey --exclude uri --exclude github_old --repo "$REPOS" --detector all --print --fail-threshold 99trufflehog-3.60.0/main.go000066400000000000000000000675241451332102400152310ustar00rootroot00000000000000package main import ( "fmt" "net/http" _ "net/http/pprof" "os" "runtime" "strconv" "strings" "syscall" "github.com/felixge/fgprof" "github.com/go-logr/logr" "github.com/jpillora/overseer" "github.com/mattn/go-isatty" "google.golang.org/protobuf/types/known/anypb" "gopkg.in/alecthomas/kingpin.v2" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/config" "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/decoders" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/engine" "github.com/trufflesecurity/trufflehog/v3/pkg/handlers" "github.com/trufflesecurity/trufflehog/v3/pkg/log" "github.com/trufflesecurity/trufflehog/v3/pkg/output" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" "github.com/trufflesecurity/trufflehog/v3/pkg/sources/git" "github.com/trufflesecurity/trufflehog/v3/pkg/tui" "github.com/trufflesecurity/trufflehog/v3/pkg/updater" "github.com/trufflesecurity/trufflehog/v3/pkg/version" ) var ( cli = kingpin.New("TruffleHog", "TruffleHog is a tool for finding credentials.") cmd string debug = cli.Flag("debug", "Run in debug mode.").Bool() trace = cli.Flag("trace", "Run in trace mode.").Bool() profile = cli.Flag("profile", "Enables profiling and sets a pprof and fgprof server on :18066.").Bool() localDev = cli.Flag("local-dev", "Hidden feature to disable overseer for local dev.").Hidden().Bool() jsonOut = cli.Flag("json", "Output in JSON format.").Short('j').Bool() jsonLegacy = cli.Flag("json-legacy", "Use the pre-v3.0 JSON format. Only works with git, gitlab, and github sources.").Bool() gitHubActionsFormat = cli.Flag("github-actions", "Output in GitHub Actions format.").Bool() 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.").Bool() filterUnverified = cli.Flag("filter-unverified", "Only output first unverified result per chunk per detector if there are more than one results.").Bool() filterEntropy = cli.Flag("filter-entropy", "Filter unverified results with Shannon entropy. Start with 3.0.").Float64() configFilename = cli.Flag("config", "Path to configuration file.").ExistingFile() // rules = cli.Flag("rules", "Path to file with custom rules.").String() 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() verifiers = cli.Flag("verifier", "Set custom verification endpoints.").StringMap() archiveMaxSize = cli.Flag("archive-max-size", "Maximum size of archive to scan. (Byte units eg. 512B, 2KB, 4MB)").Bytes() archiveMaxDepth = cli.Flag("archive-max-depth", "Maximum depth of archive to scan.").Int() archiveTimeout = cli.Flag("archive-timeout", "Maximum time to spend extracting an archive.").Duration() includeDetectors = cli.Flag("include-detectors", "Comma separated list of detector types to include. Protobuf name or IDs may be used, as well as ranges.").Default("all").String() excludeDetectors = cli.Flag("exclude-detectors", "Comma separated list of detector types to exclude. Protobuf name or IDs may be used, as well as ranges. IDs defined here take precedence over the include list.").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() githubScan = cli.Command("github", "Find credentials in GitHub repositories.") githubScanEndpoint = githubScan.Flag("endpoint", "GitHub endpoint.").Default("https://api.github.com").String() githubScanRepos = githubScan.Flag("repo", `GitHub repository to scan. You can repeat this flag. Example: "https://github.com/dustin-decker/secretsandstuff"`).Strings() githubScanOrgs = githubScan.Flag("org", `GitHub organization to scan. You can repeat this flag. Example: "trufflesecurity"`).Strings() githubScanToken = githubScan.Flag("token", "GitHub token. Can be provided with environment variable GITHUB_TOKEN.").Envar("GITHUB_TOKEN").String() githubIncludeForks = githubScan.Flag("include-forks", "Include forks in scan.").Bool() githubIncludeMembers = githubScan.Flag("include-members", "Include organization member repositories in scan.").Bool() githubIncludeRepos = githubScan.Flag("include-repos", `Repositories to include in an org scan. This can also be a glob pattern. You can repeat this flag. Must use Github repo full name. Example: "trufflesecurity/trufflehog", "trufflesecurity/t*"`).Strings() githubExcludeRepos = githubScan.Flag("exclude-repos", `Repositories to exclude in an org scan. This can also be a glob pattern. You can repeat this flag. Must use Github repo full name. Example: "trufflesecurity/driftwood", "trufflesecurity/d*"`).Strings() githubScanIncludePaths = githubScan.Flag("include-paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String() githubScanExcludePaths = githubScan.Flag("exclude-paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String() githubScanIssueComments = githubScan.Flag("issue-comments", "Include issue descriptions and comments in scan.").Bool() githubScanPRComments = githubScan.Flag("pr-comments", "Include pull request descriptions and comments in scan.").Bool() githubScanGistComments = githubScan.Flag("gist-comments", "Include gist comments in scan.").Bool() gitlabScan = cli.Command("gitlab", "Find credentials in GitLab repositories.") // TODO: Add more GitLab options 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() 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() filesystemScan = cli.Command("filesystem", "Find credentials in a filesystem.") filesystemPaths = filesystemScan.Arg("path", "Path to file or directory to scan.").Strings() // DEPRECATED: --directory is deprecated in favor of arguments. filesystemDirectories = filesystemScan.Flag("directory", "Path to directory to scan. You can repeat this flag.").Strings() // TODO: Add more filesystem scan options. Currently only supports scanning a list of directories. // filesystemScanRecursive = filesystemScan.Flag("recursive", "Scan recursively.").Short('r').Bool() filesystemScanIncludePaths = filesystemScan.Flag("include-paths", "Path to file with newline separated regexes for files to include in scan.").Short('i').String() filesystemScanExcludePaths = filesystemScan.Flag("exclude-paths", "Path to file with newline separated regexes for files to exclude in scan.").Short('x').String() s3Scan = cli.Command("s3", "Find credentials in S3 buckets.") s3ScanKey = s3Scan.Flag("key", "S3 key used to authenticate. Can be provided with environment variable AWS_ACCESS_KEY_ID.").Envar("AWS_ACCESS_KEY_ID").String() s3ScanRoleArns = s3Scan.Flag("role-arn", "Specify the ARN of an IAM role to assume for scanning. You can repeat this flag.").Strings() s3ScanSecret = s3Scan.Flag("secret", "S3 secret used to authenticate. Can be provided with environment variable AWS_SECRET_ACCESS_KEY.").Envar("AWS_SECRET_ACCESS_KEY").String() s3ScanSessionToken = s3Scan.Flag("session-token", "S3 session token used to authenticate temporary credentials. Can be provided with environment variable AWS_SESSION_TOKEN.").Envar("AWS_SESSION_TOKEN").String() s3ScanCloudEnv = s3Scan.Flag("cloud-environment", "Use IAM credentials in cloud environment.").Bool() s3ScanBuckets = s3Scan.Flag("bucket", "Name of S3 bucket to scan. You can repeat this flag.").Strings() s3ScanMaxObjectSize = s3Scan.Flag("max-object-size", "Maximum size of objects to scan. Objects larger than this will be skipped. (Byte units eg. 512B, 2KB, 4MB)").Default("250MB").Bytes() gcsScan = cli.Command("gcs", "Find credentials in GCS buckets.") gcsProjectID = gcsScan.Flag("project-id", "GCS project ID used to authenticate. Can NOT be used with unauth scan. Can be provided with environment variable GOOGLE_CLOUD_PROJECT.").Envar("GOOGLE_CLOUD_PROJECT").String() gcsCloudEnv = gcsScan.Flag("cloud-environment", "Use Application Default Credentials, IAM credentials to authenticate.").Bool() gcsServiceAccount = gcsScan.Flag("service-account", "Path to GCS service account JSON file.").ExistingFile() gcsWithoutAuth = gcsScan.Flag("without-auth", "Scan GCS buckets without authentication. This will only work for public buckets").Bool() gcsAPIKey = gcsScan.Flag("api-key", "GCS API key used to authenticate. Can be provided with environment variable GOOGLE_API_KEY.").Envar("GOOGLE_API_KEY").String() gcsIncludeBuckets = gcsScan.Flag("include-buckets", "Buckets to scan. Comma separated list of buckets. You can repeat this flag. Globs are supported").Short('I').Strings() gcsExcludeBuckets = gcsScan.Flag("exclude-buckets", "Buckets to exclude from scan. Comma separated list of buckets. Globs are supported").Short('X').Strings() gcsIncludeObjects = gcsScan.Flag("include-objects", "Objects to scan. Comma separated list of objects. you can repeat this flag. Globs are supported").Short('i').Strings() gcsExcludeObjects = gcsScan.Flag("exclude-objects", "Objects to exclude from scan. Comma separated list of objects. You can repeat this flag. Globs are supported").Short('x').Strings() gcsMaxObjectSize = gcsScan.Flag("max-object-size", "Maximum size of objects to scan. Objects larger than this will be skipped. (Byte units eg. 512B, 2KB, 4MB)").Default("10MB").Bytes() syslogScan = cli.Command("syslog", "Scan syslog") syslogAddress = syslogScan.Flag("address", "Address and port to listen on for syslog. Example: 127.0.0.1:514").String() 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() 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() ) func init() { for i, arg := range os.Args { if strings.HasPrefix(arg, "--") { split := strings.SplitN(arg, "=", 2) split[0] = strings.ReplaceAll(split[0], "_", "-") os.Args[i] = strings.Join(split, "=") } } cli.Version("trufflehog " + version.BuildVersion) if len(os.Args) <= 1 && isatty.IsTerminal(os.Stdout.Fd()) { args := tui.Run() if len(args) == 0 { os.Exit(0) } // Overwrite the Args slice so overseer works properly. os.Args = os.Args[:1] os.Args = append(os.Args, args...) } cmd = kingpin.MustParse(cli.Parse(os.Args[1:])) switch { case *trace: log.SetLevel(5) case *debug: log.SetLevel(2) } } func main() { // setup logger logFormat := log.WithConsoleSink if *jsonOut { logFormat = log.WithJSONSink } logger, sync := log.New("trufflehog", logFormat(os.Stderr)) // make it the default logger for contexts context.SetDefaultLogger(logger) if *localDev { run(overseer.State{}) os.Exit(0) } defer func() { _ = sync() }() logFatal := logFatalFunc(logger) updateCfg := overseer.Config{ Program: run, Debug: *debug, RestartSignal: syscall.SIGTERM, // TODO: Eventually add a PreUpgrade func for signature check w/ x509 PKCS1v15 // PreUpgrade: checkUpdateSignature(binaryPath string), } if !*noUpdate { updateCfg.Fetcher = updater.Fetcher(version.BuildVersion) } if version.BuildVersion == "dev" { updateCfg.Fetcher = nil } err := overseer.RunErr(updateCfg) if err != nil { logFatal(err, "error occurred with trufflehog updater 🐷") } } func run(state overseer.State) { ctx := context.Background() logger := ctx.Logger() logFatal := logFatalFunc(logger) logger.V(2).Info(fmt.Sprintf("trufflehog %s", version.BuildVersion)) if *githubScanToken != "" { // NOTE: this kludge is here to do an authenticated shallow commit // TODO: refactor to better pass credentials os.Setenv("GITHUB_TOKEN", *githubScanToken) } // When setting a base commit, chunks must be scanned in order. if *gitScanSinceCommit != "" { *concurrency = 1 } if *profile { go func() { router := http.NewServeMux() router.Handle("/debug/pprof/", http.DefaultServeMux) router.Handle("/debug/fgprof", fgprof.Handler()) logger.Info("starting pprof and fgprof server on :18066 /debug/pprof and /debug/fgprof") if err := http.ListenAndServe(":18066", router); err != nil { logger.Error(err, "error serving pprof and fgprof") } }() } conf := &config.Config{} if *configFilename != "" { var err error conf, err = config.Read(*configFilename) if err != nil { logFatal(err, "error parsing the provided configuration file") } } if *archiveMaxSize != 0 { handlers.SetArchiveMaxSize(int(*archiveMaxSize)) } if *archiveMaxDepth != 0 { handlers.SetArchiveMaxDepth(*archiveMaxDepth) } if *archiveTimeout != 0 { handlers.SetArchiveMaxTimeout(*archiveTimeout) } // Build include and exclude detector sets for filtering on engine initialization. // Exit if there was an error to inform the user of the misconfiguration. var includeDetectorSet, excludeDetectorSet map[config.DetectorID]struct{} var detectorsWithCustomVerifierEndpoints map[config.DetectorID][]string { includeList, err := config.ParseDetectors(*includeDetectors) if err != nil { logFatal(err, "invalid include list detector configuration") } excludeList, err := config.ParseDetectors(*excludeDetectors) if err != nil { logFatal(err, "invalid exclude list detector configuration") } detectorsWithCustomVerifierEndpoints, err = config.ParseVerifierEndpoints(*verifiers) if err != nil { logFatal(err, "invalid verifier detector configuration") } includeDetectorSet = detectorTypeToSet(includeList) excludeDetectorSet = detectorTypeToSet(excludeList) } // Verify that all the user-provided detectors support the optional // detector features. { if err, id := verifyDetectorsAreVersioner(includeDetectorSet); err != nil { logFatal(err, "invalid include list detector configuration", "detector", id) } if err, id := verifyDetectorsAreVersioner(excludeDetectorSet); err != nil { logFatal(err, "invalid exclude list detector configuration", "detector", id) } if err, id := verifyDetectorsAreVersioner(detectorsWithCustomVerifierEndpoints); err != nil { logFatal(err, "invalid verifier detector configuration", "detector", id) } // Extra check for endpoint customization. isEndpointCustomizer := engine.DefaultDetectorTypesImplementing[detectors.EndpointCustomizer]() for id := range detectorsWithCustomVerifierEndpoints { if _, ok := isEndpointCustomizer[id.ID]; !ok { logFatal( fmt.Errorf("endpoint provided but detector does not support endpoint customization"), "invalid custom verifier endpoint detector configuration", "detector", id, ) } } } includeFilter := func(d detectors.Detector) bool { _, ok := getWithDetectorID(d, includeDetectorSet) return ok } excludeFilter := func(d detectors.Detector) bool { _, ok := getWithDetectorID(d, excludeDetectorSet) return !ok } // Abuse filter to cause a side-effect. endpointCustomizer := func(d detectors.Detector) bool { urls, ok := getWithDetectorID(d, detectorsWithCustomVerifierEndpoints) if !ok { return true } id := config.GetDetectorID(d) customizer, ok := d.(detectors.EndpointCustomizer) if !ok { // NOTE: We should never reach here due to validation above. logFatal( fmt.Errorf("failed to configure a detector endpoint"), "the provided detector does not support endpoint configuration", "detector", id, ) } // TODO: Add flag to ignore the default endpoint. urls = append(urls, customizer.DefaultEndpoint()) if err := customizer.SetEndpoints(urls...); err != nil { logFatal(err, "failed configuring custom endpoint for detector", "detector", id) } logger.Info("configured detector with verification urls", "detector", id, "urls", urls, ) return true } // Set how the engine will print its results. var printer engine.Printer switch { case *jsonLegacy: printer = new(output.LegacyJSONPrinter) case *jsonOut: printer = new(output.JSONPrinter) case *gitHubActionsFormat: printer = new(output.GitHubActionsPrinter) default: printer = new(output.PlainPrinter) } if !*jsonLegacy && !*jsonOut { fmt.Fprintf(os.Stderr, "🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷\n\n") } e, err := engine.Start(ctx, engine.WithConcurrency(uint8(*concurrency)), engine.WithDecoders(decoders.DefaultDecoders()...), engine.WithDetectors(!*noVerification, engine.DefaultDetectors()...), engine.WithDetectors(!*noVerification, conf.Detectors...), engine.WithFilterDetectors(includeFilter), engine.WithFilterDetectors(excludeFilter), engine.WithFilterDetectors(endpointCustomizer), engine.WithFilterUnverified(*filterUnverified), engine.WithOnlyVerified(*onlyVerified), engine.WithPrintAvgDetectorTime(*printAvgDetectorTime), engine.WithPrinter(printer), engine.WithFilterEntropy(*filterEntropy), ) if err != nil { logFatal(err, "error initializing engine") } var repoPath string var remote bool switch cmd { case gitScan.FullCommand(): filter, err := common.FilterFromFiles(*gitScanIncludePaths, *gitScanExcludePaths) if err != nil { logFatal(err, "could not create filter") } repoPath, remote, err = git.PrepareRepoSinceCommit(ctx, *gitScanURI, *gitScanSinceCommit) if err != nil || repoPath == "" { logFatal(err, "error preparing git repo for scanning") } if remote { defer os.RemoveAll(repoPath) } excludedGlobs := []string{} if *gitScanExcludeGlobs != "" { excludedGlobs = strings.Split(*gitScanExcludeGlobs, ",") } cfg := sources.GitConfig{ RepoPath: repoPath, HeadRef: *gitScanBranch, BaseRef: *gitScanSinceCommit, MaxDepth: *gitScanMaxDepth, Bare: *gitScanBare, Filter: filter, ExcludeGlobs: excludedGlobs, } if err = e.ScanGit(ctx, cfg); err != nil { logFatal(err, "Failed to scan Git.") } case githubScan.FullCommand(): filter, err := common.FilterFromFiles(*githubScanIncludePaths, *githubScanExcludePaths) if err != nil { logFatal(err, "could not create filter") } if len(*githubScanOrgs) == 0 && len(*githubScanRepos) == 0 { logFatal(fmt.Errorf("invalid config"), "You must specify at least one organization or repository.") } cfg := sources.GithubConfig{ Endpoint: *githubScanEndpoint, Token: *githubScanToken, IncludeForks: *githubIncludeForks, IncludeMembers: *githubIncludeMembers, Concurrency: *concurrency, ExcludeRepos: *githubExcludeRepos, IncludeRepos: *githubIncludeRepos, Repos: *githubScanRepos, Orgs: *githubScanOrgs, IncludeIssueComments: *githubScanIssueComments, IncludePullRequestComments: *githubScanPRComments, IncludeGistComments: *githubScanGistComments, Filter: filter, } if err := e.ScanGitHub(ctx, cfg); err != nil { logFatal(err, "Failed to scan Github.") } case gitlabScan.FullCommand(): filter, err := common.FilterFromFiles(*gitlabScanIncludePaths, *gitlabScanExcludePaths) if err != nil { logFatal(err, "could not create filter") } cfg := sources.GitlabConfig{ Endpoint: *gitlabScanEndpoint, Token: *gitlabScanToken, Repos: *gitlabScanRepos, Filter: filter, } if err := e.ScanGitLab(ctx, cfg); err != nil { logFatal(err, "Failed to scan GitLab.") } case filesystemScan.FullCommand(): filter, err := common.FilterFromFiles(*filesystemScanIncludePaths, *filesystemScanExcludePaths) if err != nil { logFatal(err, "could not create filter") } if len(*filesystemDirectories) > 0 { ctx.Logger().Info("--directory flag is deprecated, please pass directories as arguments") } paths := make([]string, 0, len(*filesystemPaths)+len(*filesystemDirectories)) paths = append(paths, *filesystemPaths...) paths = append(paths, *filesystemDirectories...) cfg := sources.FilesystemConfig{ Paths: paths, Filter: filter, } if err = e.ScanFileSystem(ctx, cfg); err != nil { logFatal(err, "Failed to scan filesystem") } case s3Scan.FullCommand(): cfg := sources.S3Config{ Key: *s3ScanKey, Secret: *s3ScanSecret, SessionToken: *s3ScanSessionToken, Buckets: *s3ScanBuckets, Roles: *s3ScanRoleArns, CloudCred: *s3ScanCloudEnv, MaxObjectSize: int64(*s3ScanMaxObjectSize), } if err := e.ScanS3(ctx, cfg); err != nil { logFatal(err, "Failed to scan S3.") } case syslogScan.FullCommand(): cfg := sources.SyslogConfig{ Address: *syslogAddress, Format: *syslogFormat, Protocol: *syslogProtocol, CertPath: *syslogTLSCert, KeyPath: *syslogTLSKey, Concurrency: *concurrency, } if err := e.ScanSyslog(ctx, cfg); err != nil { logFatal(err, "Failed to scan syslog.") } case circleCiScan.FullCommand(): if err := e.ScanCircleCI(ctx, *circleCiScanToken); err != nil { logFatal(err, "Failed to scan CircleCI.") } case gcsScan.FullCommand(): cfg := sources.GCSConfig{ ProjectID: *gcsProjectID, CloudCred: *gcsCloudEnv, ServiceAccount: *gcsServiceAccount, WithoutAuth: *gcsWithoutAuth, ApiKey: *gcsAPIKey, IncludeBuckets: commaSeparatedToSlice(*gcsIncludeBuckets), ExcludeBuckets: commaSeparatedToSlice(*gcsExcludeBuckets), IncludeObjects: commaSeparatedToSlice(*gcsIncludeObjects), ExcludeObjects: commaSeparatedToSlice(*gcsExcludeObjects), Concurrency: *concurrency, MaxObjectSize: int64(*gcsMaxObjectSize), } if err := e.ScanGCS(ctx, cfg); err != nil { logFatal(err, "Failed to scan GCS.") } case dockerScan.FullCommand(): dockerConn := sourcespb.Docker{ Images: *dockerScanImages, Credential: &sourcespb.Docker_DockerKeychain{ DockerKeychain: true, }, } anyConn, err := anypb.New(&dockerConn) if err != nil { logFatal(err, "Failed to marshal Docker connection") } if err := e.ScanDocker(ctx, anyConn); err != nil { logFatal(err, "Failed to scan Docker.") } } // Wait for all workers to finish. if err = e.Finish(ctx); err != nil { logFatal(err, "engine failed to finish execution") } metrics := e.GetMetrics() // Print results. logger.Info("finished scanning", "chunks", metrics.ChunksScanned, "bytes", metrics.BytesScanned, "verified_secrets", metrics.VerifiedSecretsFound, "unverified_secrets", metrics.UnverifiedSecretsFound, "scan_duration", metrics.ScanDuration.String(), ) if *printAvgDetectorTime { printAverageDetectorTime(e) } if e.HasFoundResults() && *fail { logger.V(2).Info("exiting with code 183 because results were found") os.Exit(183) } } // logFatalFunc returns a log.Fatal style function. Calling the returned // function will terminate the program without cleanup. func logFatalFunc(logger logr.Logger) func(error, string, ...any) { return func(err error, message string, keyAndVals ...any) { logger.Error(err, message, keyAndVals...) if err != nil { os.Exit(1) return } os.Exit(0) } } func commaSeparatedToSlice(s []string) []string { var result []string for _, items := range s { for _, item := range strings.Split(items, ",") { item = strings.TrimSpace(item) if item == "" { continue } result = append(result, item) } } return result } func printAverageDetectorTime(e *engine.Engine) { fmt.Fprintln(os.Stderr, "Average detector time is the measurement of average time spent on each detector when results are returned.") for detectorName, duration := range e.GetDetectorsMetrics() { fmt.Fprintf(os.Stderr, "%s: %s\n", detectorName, duration) } } // detectorTypeToSet is a helper function to convert a slice of detector IDs into a set. func detectorTypeToSet(detectors []config.DetectorID) map[config.DetectorID]struct{} { output := make(map[config.DetectorID]struct{}, len(detectors)) for _, d := range detectors { output[d] = struct{}{} } return output } // getWithDetectorID is a helper function to get a value from a map using a // detector's ID. This function behaves like a normal map lookup, with an extra // step of checking for the non-specific version of a detector. func getWithDetectorID[T any](d detectors.Detector, data map[config.DetectorID]T) (T, bool) { key := config.GetDetectorID(d) // Check if the specific ID is provided. if t, ok := data[key]; ok || key.Version == 0 { return t, ok } // Check if the generic type is provided without a version. // This means "all" versions of a type. key.Version = 0 t, ok := data[key] return t, ok } // verifyDetectorsAreVersioner checks all keys in a provided map to verify the // provided type is actually a Versioner. func verifyDetectorsAreVersioner[T any](data map[config.DetectorID]T) (error, config.DetectorID) { isVersioner := engine.DefaultDetectorTypesImplementing[detectors.Versioner]() for id := range data { if id.Version == 0 { // Version not provided. continue } if _, ok := isVersioner[id.ID]; ok { // Version provided for a Versioner detector. continue } // Version provided on a non-Versioner detector. return fmt.Errorf("version provided but detector does not have a version"), id } return nil, config.DetectorID{} } trufflehog-3.60.0/pkg/000077500000000000000000000000001451332102400145215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/cache/000077500000000000000000000000001451332102400155645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/cache/cache.go000066400000000000000000000014631451332102400171620ustar00rootroot00000000000000// Package cache provides an interface which can be implemented by different cache types. package cache // Cache is used to store key/value pairs. type Cache interface { // Set stores the given key/value pair. Set(string, string) // Get returns the value for the given key and a boolean indicating if the key was found. Get(string) (string, bool) // Exists returns true if the given key exists in the cache. Exists(string) bool // Delete the given key from the cache. Delete(string) // Clear all key/value pairs from the cache. Clear() // Count the number of key/value pairs in the cache. Count() int // Keys returns all keys in the cache. Keys() []string // Values returns all values in the cache. Values() []string // Contents returns all keys in the cache encoded as a string. Contents() string } trufflehog-3.60.0/pkg/cache/memory/000077500000000000000000000000001451332102400170745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/cache/memory/memory.go000066400000000000000000000045001451332102400207320ustar00rootroot00000000000000package memory import ( "strings" "time" "github.com/patrickmn/go-cache" "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) const ( expirationInterval = 12 * time.Hour purgeInterval = 13 * time.Hour defaultExpiration = cache.DefaultExpiration ) // Cache is a wrapper around the go-cache library. type Cache struct { c *cache.Cache } // New constructs a new in-memory cache. func New() *Cache { c := cache.New(expirationInterval, purgeInterval) return &Cache{c: c} } // NewWithData constructs a new in-memory cache with existing data. func NewWithData(ctx context.Context, data []string) *Cache { ctx.Logger().V(3).Info("Loading cache", "num-items", len(data)) items := make(map[string]cache.Item, len(data)) for _, d := range data { items[d] = cache.Item{Object: d, Expiration: int64(defaultExpiration)} } c := cache.NewFrom(expirationInterval, purgeInterval, items) return &Cache{c: c} } // Set adds a key-value pair to the cache. func (c *Cache) Set(key, value string) { c.c.Set(key, value, defaultExpiration) } // Get returns the value for the given key. func (c *Cache) Get(key string) (string, bool) { res, ok := c.c.Get(key) if !ok { return "", ok } return res.(string), ok } // Exists returns true if the given key exists in the cache. func (c *Cache) Exists(key string) bool { _, ok := c.c.Get(key) return ok } // Delete removes the key-value pair from the cache. func (c *Cache) Delete(key string) { c.c.Delete(key) } // Clear removes all key-value pairs from the cache. func (c *Cache) Clear() { c.c.Flush() } // Count returns the number of key-value pairs in the cache. func (c *Cache) Count() int { return c.c.ItemCount() } // Keys returns all keys in the cache. func (c *Cache) Keys() []string { items := c.c.Items() res := make([]string, 0, len(items)) for k := range items { res = append(res, k) } return res } // Values returns all values in the cache. func (c *Cache) Values() []string { items := c.c.Items() res := make([]string, 0, len(items)) for _, v := range items { res = append(res, v.Object.(string)) } return res } // Contents returns all key-value pairs in the cache encodes as a string. func (c *Cache) Contents() string { items := c.c.Items() res := make([]string, 0, len(items)) for k := range items { res = append(res, k) } return strings.Join(res, ",") } trufflehog-3.60.0/pkg/cache/memory/memory_test.go000066400000000000000000000057231451332102400220010ustar00rootroot00000000000000package memory import ( "fmt" "sort" "strings" "testing" "github.com/google/go-cmp/cmp" logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) func TestCache(t *testing.T) { c := New() // Test set and get. c.Set("key1", "key1") v, ok := c.Get("key1") if !ok || v != "key1" { t.Fatalf("Unexpected value for key1: %v, %v", v, ok) } // Test exists. if !c.Exists("key1") { t.Fatalf("Expected key1 to exist") } // Test the count. if c.Count() != 1 { t.Fatalf("Unexpected count: %d", c.Count()) } // Test delete. c.Delete("key1") v, ok = c.Get("key1") if ok || v != "" { t.Fatalf("Unexpected value for key1 after delete: %v, %v", v, ok) } // Test clear. c.Set("key10", "key10") c.Clear() v, ok = c.Get("key10") if ok || v != "" { t.Fatalf("Unexpected value for key10 after clear: %v, %v", v, ok) } // Test getting only the keys. keys := []string{"key1", "key2", "key3"} values := []string{"value1", "value2", "value3"} for i, k := range keys { c.Set(k, values[i]) } k := c.Keys() sort.Strings(keys) sort.Strings(k) if !cmp.Equal(keys, k) { t.Fatalf("Unexpected keys: %v", k) } // Test getting only the values. vals := c.Values() sort.Strings(vals) sort.Strings(values) if !cmp.Equal(values, vals) { t.Fatalf("Unexpected values: %v", vals) } // Test contents. items := c.Contents() sort.Strings(keys) res := strings.Split(items, ",") sort.Strings(res) if len(keys) != len(res) { t.Fatalf("Unexpected length of items: %d", len(res)) } if !cmp.Equal(keys, res) { t.Fatalf("Unexpected items: %v", res) } } func TestCache_NewWithData(t *testing.T) { c := NewWithData(logContext.Background(), []string{"key1", "key2", "key3"}) // Test the count. if c.Count() != 3 { t.Fatalf("Unexpected count: %d", c.Count()) } // Test contents. keys := []string{"key1", "key2", "key3"} items := c.Contents() sort.Strings(keys) res := strings.Split(items, ",") sort.Strings(res) if len(keys) != len(res) { t.Fatalf("Unexpected length of items: %d", len(res)) } if !cmp.Equal(keys, res) { t.Fatalf("Unexpected items: %v", res) } } func setupBenchmarks(b *testing.B) *Cache { b.Helper() c := New() for i := 0; i < 500_000; i++ { key := fmt.Sprintf("key%d", i) c.Set(key, key) } return c } func BenchmarkSet(b *testing.B) { c := New() for i := 0; i < b.N; i++ { key := fmt.Sprintf("key%d", i) c.Set(key, key) } } func BenchmarkGet(b *testing.B) { c := setupBenchmarks(b) b.ResetTimer() for i := 0; i < b.N; i++ { key := fmt.Sprintf("key%d", i) c.Get(key) } } func BenchmarkDelete(b *testing.B) { c := setupBenchmarks(b) b.ResetTimer() for i := 0; i < b.N; i++ { key := fmt.Sprintf("key%d", i) c.Delete(key) } } func BenchmarkCount(b *testing.B) { c := setupBenchmarks(b) b.ResetTimer() for i := 0; i < b.N; i++ { c.Count() } } func BenchmarkContents(b *testing.B) { c := setupBenchmarks(b) b.ResetTimer() var s string for i := 0; i < b.N; i++ { s = c.Contents() } _ = s } trufflehog-3.60.0/pkg/common/000077500000000000000000000000001451332102400160115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/common/context.go000066400000000000000000000011751451332102400200300ustar00rootroot00000000000000package common import "context" func IsDone(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } // CancellableWrite blocks on writing the item to the channel but can be // cancelled by the context. If both the context is cancelled and the channel // write would succeed, either operation will be performed randomly. func CancellableWrite[T any](ctx context.Context, ch chan<- T, item T) error { select { case <-ctx.Done(): // priority to context cancellation return ctx.Err() default: select { case <-ctx.Done(): return ctx.Err() case ch <- item: return nil } } } trufflehog-3.60.0/pkg/common/depaware.go000066400000000000000000000001071451332102400201260ustar00rootroot00000000000000package common import ( _ "github.com/tailscale/depaware/depaware" ) trufflehog-3.60.0/pkg/common/filter.go000066400000000000000000000055631451332102400176360ustar00rootroot00000000000000package common import ( "bufio" "fmt" "os" "regexp" "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) type Filter struct { include *FilterRuleSet exclude *FilterRuleSet } type FilterRuleSet []regexp.Regexp // FilterEmpty returns a Filter that always passes. func FilterEmpty() *Filter { filter, err := FilterFromFiles("", "") if err != nil { context.Background().Logger().Error(err, "could not create empty filter") os.Exit(1) } return filter } // FilterFromFiles creates a Filter using the rules in the provided include and exclude files. func FilterFromFiles(includeFilterPath, excludeFilterPath string) (*Filter, error) { includeRules, err := FilterRulesFromFile(includeFilterPath) if err != nil { return nil, fmt.Errorf("could not create include rules: %s", err) } excludeRules, err := FilterRulesFromFile(excludeFilterPath) if err != nil { return nil, fmt.Errorf("could not create exclude rules: %s", err) } // If no includeFilterPath is provided, every pattern should pass the include rules. if includeFilterPath == "" { includeRules = &FilterRuleSet{*regexp.MustCompile("")} } filter := &Filter{ include: includeRules, exclude: excludeRules, } return filter, nil } // FilterRulesFromFile loads the list of regular expression filter rules in `source` and creates a FilterRuleSet. func FilterRulesFromFile(source string) (*FilterRuleSet, error) { rules := FilterRuleSet{} if source == "" { return &rules, nil } commentPattern := regexp.MustCompile(`^\s*#`) emptyLinePattern := regexp.MustCompile(`^\s*$`) file, err := os.Open(source) logger := context.Background().Logger().WithValues("file", source) if err != nil { logger.Error(err, "unable to open filter file", "file", source) os.Exit(1) } defer func(file *os.File) { err := file.Close() if err != nil { logger.Error(err, "unable to close filter file") os.Exit(1) } }(file) scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if commentPattern.MatchString(line) { continue } if emptyLinePattern.MatchString(line) { continue } pattern, err := regexp.Compile(line) if err != nil { return nil, fmt.Errorf("can not compile regular expression: %s", line) } rules = append(rules, *pattern) } return &rules, nil } // Pass returns true if the include FilterRuleSet matches the pattern and the exclude FilterRuleSet does not match. func (filter *Filter) Pass(object string) bool { if filter == nil { return true } excluded := filter.exclude.Matches(object) included := filter.include.Matches(object) return !excluded && included } // Matches will return true if any of the regular expressions in the FilterRuleSet match the pattern. func (rules *FilterRuleSet) Matches(object string) bool { if rules == nil { return false } for _, rule := range *rules { if rule.MatchString(object) { return true } } return false } trufflehog-3.60.0/pkg/common/filter_test.go000066400000000000000000000110311451332102400206600ustar00rootroot00000000000000package common import ( "os" "regexp" "testing" ) func TestFilterBasic(t *testing.T) { type filterTest struct { filter Filter pattern string pass bool } tests := map[string]filterTest{ "IncludePassed": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("test")}, }, pattern: "teststring", pass: true, }, "IncludeFiltered": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("nomatch")}, }, pattern: "teststring", pass: false, }, "ExcludePassed": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("")}, exclude: &FilterRuleSet{*regexp.MustCompile("nomatch")}, }, pattern: "teststring", pass: true, }, "ExcludeFiltered": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("")}, exclude: &FilterRuleSet{*regexp.MustCompile("test")}, }, pattern: "teststring", pass: false, }, "IncludeExcludeDifferentPass": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("test")}, exclude: &FilterRuleSet{*regexp.MustCompile("nomatch")}, }, pattern: "teststring", pass: true, }, "IncludeExcludeDifferentFiltered": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("nomatch")}, exclude: &FilterRuleSet{*regexp.MustCompile("test")}, }, pattern: "teststring", pass: false, }, "IncludeExcludeSameFiltered": { filter: Filter{ include: &FilterRuleSet{*regexp.MustCompile("test")}, exclude: &FilterRuleSet{*regexp.MustCompile("test")}, }, pattern: "teststring", pass: false, }, } for name, test := range tests { if test.filter.Pass(test.pattern) != test.pass { t.Errorf("%s: unexpected filter result. pattern: %q, pass: %t", name, test.pattern, !test.pass) } } } func TestFilterFromFile(t *testing.T) { type filterTest struct { includeFile bool excludeFile bool includeFileContents string excludeFileContents string pattern string pass bool } tests := map[string]filterTest{ "includeFileOnlyPass": { includeFile: true, excludeFile: false, includeFileContents: "test", pattern: "test", pass: true, }, "includeFileOnlyFiltered": { includeFile: true, excludeFile: false, includeFileContents: "nomatch", pattern: "test", pass: false, }, "includeFileEmptyFiltered": { includeFile: true, excludeFile: false, includeFileContents: "", pattern: "test", pass: false, }, "excludeFileOnlyPass": { includeFile: false, excludeFile: true, excludeFileContents: "nomatch", pattern: "test", pass: true, }, "excludeFileOnlyFiltered": { includeFile: false, excludeFile: true, excludeFileContents: "test", pattern: "test", pass: false, }, "BothFilesEmptyExcludeFiltered": { includeFile: true, excludeFile: true, excludeFileContents: "", includeFileContents: "", pattern: "test", pass: false, }, "EmptyLinesAreIgnored": { includeFile: false, excludeFile: true, excludeFileContents: " \ntest.txt", pattern: "hello world.txt", pass: true, }, } for name, test := range tests { var includeTestFile, excludeTestFile string if test.includeFile { includeTestFile = "/tmp/trufflehog_test_ifilter.txt" if err := testFilterWriteFile(includeTestFile, []byte(test.includeFileContents)); err != nil { t.Fatalf("failed to create include rules file: %s", err) } defer os.Remove(includeTestFile) } if test.excludeFile { excludeTestFile = "/tmp/trufflehog_test_xfilter.txt" if err := testFilterWriteFile(excludeTestFile, []byte(test.excludeFileContents)); err != nil { t.Fatalf("failed to create include rules file: %s", err) } defer os.Remove(excludeTestFile) } filter, err := FilterFromFiles(includeTestFile, excludeTestFile) if err != nil { t.Errorf("failed to create filter from files: %s", err) } if filter.Pass(test.pattern) != test.pass { t.Errorf("%s: unexpected filter result. pattern: %q, pass: %t", name, test.pattern, !test.pass) } } } func testFilterWriteFile(filename string, content []byte) error { f, err := os.Create(filename) if err != nil { return err } _, err = f.Write(content) if err != nil { return err } return f.Close() } trufflehog-3.60.0/pkg/common/http.go000066400000000000000000000142541451332102400173250ustar00rootroot00000000000000package common import ( "crypto/tls" "crypto/x509" "io" "net" "net/http" "strings" "time" "github.com/hashicorp/go-retryablehttp" ) var caCerts = []string{ // CN = ISRG Root X1 // TODO: Expires Monday, June 4, 2035 at 4:04:38 AM Pacific ` -----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= -----END CERTIFICATE----- `, // CN = ISRG Root X2 // TODO: Expires September 17, 2040 at 9:00:00 AM Pacific Daylight Time ` -----BEGIN CERTIFICATE----- MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW +1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 /q4AaOeMSQ+2b1tbFfLn -----END CERTIFICATE----- `, } func PinnedCertPool() *x509.CertPool { trustedCerts := x509.NewCertPool() for _, cert := range caCerts { trustedCerts.AppendCertsFromPEM([]byte(strings.TrimSpace(cert))) } return trustedCerts } type FakeTransport struct { CreateResponse func(req *http.Request) (*http.Response, error) } func (t FakeTransport) RoundTrip(req *http.Request) (*http.Response, error) { return t.CreateResponse(req) } type CustomTransport struct { T http.RoundTripper } func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Add("User-Agent", "TruffleHog") return t.T.RoundTrip(req) } func NewCustomTransport(T http.RoundTripper) *CustomTransport { if T == nil { T = http.DefaultTransport } return &CustomTransport{T} } func ConstantResponseHttpClient(statusCode int, body string) *http.Client { return &http.Client{ Timeout: DefaultResponseTimeout, Transport: FakeTransport{ CreateResponse: func(req *http.Request) (*http.Response, error) { return &http.Response{ Request: req, Body: io.NopCloser(strings.NewReader(body)), StatusCode: statusCode, }, nil }, }, } } func PinnedRetryableHttpClient() *http.Client { httpClient := retryablehttp.NewClient() httpClient.Logger = nil httpClient.HTTPClient.Transport = NewCustomTransport(&http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: PinnedCertPool(), }, Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, }) return httpClient.StandardClient() } func RetryableHttpClient() *http.Client { httpClient := retryablehttp.NewClient() httpClient.RetryMax = 3 httpClient.Logger = nil httpClient.HTTPClient.Timeout = 3 * time.Second httpClient.HTTPClient.Transport = NewCustomTransport(nil) return httpClient.StandardClient() } func RetryableHttpClientTimeout(timeOutSeconds int64) *http.Client { httpClient := retryablehttp.NewClient() httpClient.RetryMax = 3 httpClient.Logger = nil httpClient.HTTPClient.Timeout = time.Duration(timeOutSeconds) * time.Second httpClient.HTTPClient.Transport = NewCustomTransport(nil) return httpClient.StandardClient() } const DefaultResponseTimeout = 5 * time.Second var saneTransport = &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 2 * time.Second, KeepAlive: 5 * time.Second, }).DialContext, MaxIdleConns: 5, IdleConnTimeout: 5 * time.Second, TLSHandshakeTimeout: 3 * time.Second, ExpectContinueTimeout: 1 * time.Second, } func SaneHttpClient() *http.Client { httpClient := &http.Client{} httpClient.Timeout = DefaultResponseTimeout httpClient.Transport = NewCustomTransport(saneTransport) return httpClient } // SaneHttpClientTimeOut adds a custom timeout for some scanners func SaneHttpClientTimeOut(timeout time.Duration) *http.Client { httpClient := &http.Client{} httpClient.Timeout = timeout httpClient.Transport = NewCustomTransport(nil) return httpClient } trufflehog-3.60.0/pkg/common/metrics.go000066400000000000000000000003071451332102400200060ustar00rootroot00000000000000package common const ( // MetricsNamespace is the namespace for all metrics. MetricsNamespace = "trufflehog" // MetricsSubsystem is the subsystem for all metrics. MetricsSubsystem = "scanner" ) trufflehog-3.60.0/pkg/common/patterns.go000066400000000000000000000054331451332102400202050ustar00rootroot00000000000000package common import ( "fmt" "regexp" "strconv" "strings" ) const EmailPattern = `\b(?:[a-z0-9!#$%&'*+/=?^_\x60{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_\x60{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])\b` const SubDomainPattern = `\b([A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?)\b` const UUIDPattern = `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b` const UUIDPatternUpperCase = `\b([0-9A-Z]{8}-[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{12})\b` const RegexPattern = "0-9a-z" const AlphaNumPattern = "0-9a-zA-Z" const HexPattern = "0-9a-f" type RegexState struct { compiledRegex *regexp.Regexp } // Custom Regex functions func BuildRegex(pattern string, specialChar string, length int) string { return fmt.Sprintf(`\b([%s%s]{%s})\b`, pattern, specialChar, strconv.Itoa(length)) } func BuildRegexJWT(firstRange, secondRange, thirdRange string) string { if RangeValidation(firstRange) || RangeValidation(secondRange) || RangeValidation(thirdRange) { panic("Min value should not be greater than or equal to max") } return fmt.Sprintf(`\b(ey[%s]{%s}.ey[%s-\/_]{%s}.[%s-\/_]{%s})\b`, AlphaNumPattern, firstRange, AlphaNumPattern, secondRange, AlphaNumPattern, thirdRange) } func RangeValidation(rangeInput string) bool { range_split := strings.Split(rangeInput, ",") range_min, _ := strconv.ParseInt(strings.TrimSpace(range_split[0]), 10, 0) range_max, _ := strconv.ParseInt(strings.TrimSpace(range_split[1]), 10, 0) return range_min >= range_max } func ToUpperCase(input string) string { return strings.ToUpper(input) } func (r RegexState) Matches(data []byte) []string { matches := r.compiledRegex.FindAllStringSubmatch(string(data), -1) res := make([]string, 0, len(matches)) // trim off spaces and different quote types ('"). for i := range matches { res = append(res, strings.Trim(matches[i][1], `"' )`)) } return res } // UsernameRegexCheck constructs an username usernameRegex pattern from a given pattern of excluded characters. func UsernameRegexCheck(pattern string) RegexState { raw := fmt.Sprintf(`(?im)(?:user|usr)\S{0,40}?[:=\s]{1,3}[ '"=]{0,1}([^:%+v]{4,40})\b`, pattern) return RegexState{regexp.MustCompile(raw)} } // PasswordRegexCheck constructs an username usernameRegex pattern from a given pattern of excluded characters. func PasswordRegexCheck(pattern string) RegexState { raw := fmt.Sprintf(`(?im)(?:pass|password)\S{0,40}?[:=\s]{1,3}[ '"=]{0,1}([^:%+v]{4,40})`, pattern) return RegexState{regexp.MustCompile(raw)} } trufflehog-3.60.0/pkg/common/patterns_test.go000066400000000000000000000035561451332102400212500ustar00rootroot00000000000000package common import ( "github.com/stretchr/testify/assert" "regexp" "testing" ) const ( usernamePattern = `?()/\+=\s\n` passwordPattern = `^<>;.*&|ÂŖ\n\s` usernameRegex = `(?im)(?:user|usr)\S{0,40}?[:=\s]{1,3}[ '"=]{0,1}([^:?()/\+=\s\n]{4,40})\b` passwordRegex = `(?im)(?:pass|password)\S{0,40}?[:=\s]{1,3}[ '"=]{0,1}([^:^<>;.*&|ÂŖ\n\s]{4,40})` ) func TestUsernameRegexCheck(t *testing.T) { usernameRegexPat := UsernameRegexCheck(usernamePattern) expectedRegexPattern := regexp.MustCompile(usernameRegex) if usernameRegexPat.compiledRegex.String() != expectedRegexPattern.String() { t.Errorf("\n got %v \n want %v", usernameRegexPat.compiledRegex, expectedRegexPattern) } testString := `username = "johnsmith123" username='johnsmith123' username:="johnsmith123" username = johnsmith123 username=johnsmith123` expectedStr := []string{"johnsmith123", "johnsmith123", "johnsmith123", "johnsmith123", "johnsmith123"} usernameRegexMatches := usernameRegexPat.Matches([]byte(testString)) assert.Exactly(t, usernameRegexMatches, expectedStr) } func TestPasswordRegexCheck(t *testing.T) { passwordRegexPat := PasswordRegexCheck(passwordPattern) expectedRegexPattern := regexp.MustCompile(passwordRegex) assert.Equal(t, passwordRegexPat.compiledRegex, expectedRegexPattern) testString := `password = "johnsmith123$!" password='johnsmith123$!' password:="johnsmith123$!" password = johnsmith123$! password=johnsmith123$! PasswordAuthenticator(username, "johnsmith123$!")` expectedStr := []string{"johnsmith123$!", "johnsmith123$!", "johnsmith123$!", "johnsmith123$!", "johnsmith123$!", "johnsmith123$!"} passwordRegexMatches := passwordRegexPat.Matches([]byte(testString)) assert.Exactly(t, passwordRegexMatches, expectedStr) } trufflehog-3.60.0/pkg/common/recover.go000066400000000000000000000022301451332102400200020ustar00rootroot00000000000000package common import ( "fmt" "os" "runtime/debug" "time" "github.com/getsentry/sentry-go" "github.com/trufflesecurity/trufflehog/v3/pkg/context" ) // Recover handles panics and reports to Sentry. func Recover(ctx context.Context) { 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, ) if !sentry.Flush(time.Second * 5) { ctx.Logger().Info("sentry flush failed") } } } // RecoverWithExit handles panics and reports to Sentry before exiting. func RecoverWithExit(ctx context.Context) { 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"), "recovered from panic before exiting", "stack-trace", panicStack, "recover", err, ) if !sentry.Flush(time.Second * 5) { ctx.Logger().Info("sentry flush failed") } os.Exit(1) } } trufflehog-3.60.0/pkg/common/secrets.go000066400000000000000000000032631451332102400200140ustar00rootroot00000000000000package common import ( "context" "fmt" "os" "time" secretmanager "cloud.google.com/go/secretmanager/apiv1" "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" "github.com/joho/godotenv" "github.com/pkg/errors" ) type Secret struct{ kv map[string]string } func (s *Secret) MustGetField(name string) string { val, ok := s.kv[name] if !ok { panic(errors.Errorf("field %s not found", name)) } return val } func GetSecretFromEnv(filename string) (secret *Secret, err error) { data, err := godotenv.Read(filename) if err != nil { return nil, err } return &Secret{kv: data}, nil } func GetTestSecret(ctx context.Context) (secret *Secret, err error) { filename := os.Getenv("TEST_SECRET_FILE") if len(filename) > 0 { return GetSecretFromEnv(filename) } return GetSecret(ctx, "trufflehog-testing", "test") } func GetSecret(ctx context.Context, gcpProject, name string) (secret *Secret, err error) { ctx, cancel := context.WithTimeout(ctx, time.Second*10) defer cancel() filename := os.Getenv("TEST_SECRET_FILE") if len(filename) > 0 { return GetSecretFromEnv(filename) } parent := fmt.Sprintf("projects/%s/secrets/%s/versions/latest", gcpProject, name) client, err := secretmanager.NewClient(ctx) if err != nil { return nil, errors.Errorf("failed to create secretmanager client: %v", err) } defer client.Close() req := &secretmanagerpb.AccessSecretVersionRequest{ Name: parent, } result, err := client.AccessSecretVersion(ctx, req) if err != nil { return nil, errors.Errorf("failed to access secret version: %v", err) } data, err := godotenv.Unmarshal(string(result.Payload.Data)) if err != nil { return nil, err } return &Secret{kv: data}, nil } trufflehog-3.60.0/pkg/common/utils.go000066400000000000000000000024511451332102400175020ustar00rootroot00000000000000package common import ( "bufio" "bytes" "crypto/rand" "io" "math/big" "strings" ) func AddStringSliceItem(item string, slice *[]string) { for _, i := range *slice { if i == item { return } } *slice = append(*slice, item) } func RemoveStringSliceItem(item string, slice *[]string) { for i, listItem := range *slice { if item == listItem { (*slice)[i] = (*slice)[len(*slice)-1] *slice = (*slice)[:len(*slice)-1] } } } func MinInt(a, b int) int { if a < b { return a } return b } func BytesEqual(a, b []byte, numBytes int) bool { limit := MinInt(numBytes, MinInt(len(a), len(b))-1) return bytes.Equal(a[:limit], b[:limit]) } func ResponseContainsSubstring(reader io.ReadCloser, target string) (bool, error) { scanner := bufio.NewScanner(reader) for scanner.Scan() { if strings.Contains(scanner.Text(), target) { return true, nil } } if err := scanner.Err(); err != nil { return false, err } return false, nil } var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") // RandomID returns a random string of the given length. func RandomID(length int) string { b := make([]rune, length) for i := range b { randInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) b[i] = letters[randInt.Int64()] } return string(b) } trufflehog-3.60.0/pkg/common/utils_test.go000066400000000000000000000054021451332102400205400ustar00rootroot00000000000000package common import ( "io" "reflect" "strings" "testing" ) func TestAddItem(t *testing.T) { type Case struct { Slice []string Modifier []string Expected []string } tests := map[string]Case{ "newItem": { Slice: []string{"a", "b", "c"}, Modifier: []string{"d"}, Expected: []string{"a", "b", "c", "d"}, }, "newDuplicate": { Slice: []string{"a", "b", "c"}, Modifier: []string{"c"}, Expected: []string{"a", "b", "c"}, }, } for name, test := range tests { for _, item := range test.Modifier { AddStringSliceItem(item, &test.Slice) } if !reflect.DeepEqual(test.Slice, test.Expected) { t.Errorf("%s: expected:%v, got:%v", name, test.Expected, test.Slice) } } } func TestRemoveItem(t *testing.T) { type Case struct { Slice []string Modifier []string Expected []string } tests := map[string]Case{ "existingItemEnd": { Slice: []string{"a", "b", "c"}, Modifier: []string{"c"}, Expected: []string{"a", "b"}, }, "existingItemMiddle": { Slice: []string{"a", "b", "c"}, Modifier: []string{"b"}, Expected: []string{"a", "c"}, }, "existingItemBeginning": { Slice: []string{"a", "b", "c"}, Modifier: []string{"a"}, Expected: []string{"c", "b"}, }, "nonExistingItem": { Slice: []string{"a", "b", "c"}, Modifier: []string{"d"}, Expected: []string{"a", "b", "c"}, }, } for name, test := range tests { for _, item := range test.Modifier { RemoveStringSliceItem(item, &test.Slice) } if !reflect.DeepEqual(test.Slice, test.Expected) { t.Errorf("%s: expected:%v, got:%v", name, test.Expected, test.Slice) } } } // Test ParseResponseForKeywords with a reader that contains the keyword and a reader that doesn't. func TestParseResponseForKeywords(t *testing.T) { testCases := []struct { name string input string keyword string expected bool }{ { name: "Should find keyword", input: "ey: abc", keyword: "ey", expected: true, }, { name: "Should not find keyword", input: "fake response", keyword: "ey", expected: false, }, { name: "Empty string", input: "", keyword: "ey", expected: false, }, { name: "Keyword at end", input: "abc ey", keyword: "ey", expected: true, }, { name: "Keyword at start", input: "ey abc", keyword: "ey", expected: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { testReader := strings.NewReader(tc.input) testReadCloser := io.NopCloser(testReader) found, err := ResponseContainsSubstring(testReadCloser, tc.keyword) if err != nil { t.Errorf("Error: %v", err) } if found != tc.expected { t.Errorf("Expected %v, got %v", tc.expected, found) } }) } } trufflehog-3.60.0/pkg/common/vars.go000066400000000000000000000007141451332102400173150ustar00rootroot00000000000000package common import ( "path/filepath" "strings" ) var ( KB, MB, GB, TB, PB = 1e3, 1e6, 1e9, 1e12, 1e15 IgnoredExtensions = []string{"mp4", "avi", "mpeg", "mpg", "mov", "wmv", "m4p", "swf", "mp2", "flv", "vob", "webm", "hdv", "3gp", "ogg", "mp3", "wav", "flac", "webp"} ) func SkipFile(filename string) bool { for _, ext := range IgnoredExtensions { if strings.TrimPrefix(filepath.Ext(filename), ".") == ext { return true } } return false } trufflehog-3.60.0/pkg/common/vars_test.go000066400000000000000000000014271451332102400203560ustar00rootroot00000000000000package common import "testing" func TestSkipFile(t *testing.T) { type testCase struct { file string want bool } // Add a test case for each ignored extension. testCases := make([]testCase, 0, len(IgnoredExtensions)+1) for _, ext := range IgnoredExtensions { testCases = append(testCases, testCase{ file: "test." + ext, want: true, }) } // Add a test case for a file that should not be skipped. testCases = append(testCases, testCase{file: "test.txt", want: false}) for _, tt := range testCases { t.Run(tt.file, func(t *testing.T) { if got := SkipFile(tt.file); got != tt.want { t.Errorf("SkipFile(%v) got %v, want %v", tt.file, got, tt.want) } }) } } func BenchmarkSkipFile(b *testing.B) { for i := 0; i < b.N; i++ { SkipFile("test.mp4") } } trufflehog-3.60.0/pkg/config/000077500000000000000000000000001451332102400157665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/config/config.go000066400000000000000000000022701451332102400175630ustar00rootroot00000000000000package config import ( "os" "github.com/trufflesecurity/trufflehog/v3/pkg/custom_detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/protoyaml" ) // Config holds user supplied configuration. type Config struct { Detectors []detectors.Detector } // Read parses a given filename into a Config. func Read(filename string) (*Config, error) { input, err := os.ReadFile(filename) if err != nil { return nil, err } return NewYAML(input) } // NewYAML parses the given YAML data into a Config. func NewYAML(input []byte) (*Config, error) { // Parse the raw YAML into a structure. var messages custom_detectorspb.CustomDetectors if err := protoyaml.UnmarshalStrict(input, &messages); err != nil { return nil, err } // Convert the structured YAML into detectors. var detectors []detectors.Detector for _, detectorConfig := range messages.Detectors { detector, err := custom_detectors.NewWebhookCustomRegex(detectorConfig) if err != nil { return nil, err } detectors = append(detectors, detector) } return &Config{ Detectors: detectors, }, nil } trufflehog-3.60.0/pkg/config/detectors.go000066400000000000000000000154341451332102400203200ustar00rootroot00000000000000package config import ( "fmt" "net/url" "sort" "strconv" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" dpb "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) var ( specialGroups = map[string][]DetectorID{ "all": allDetectors(), } detectorTypeValue = make(map[string]dpb.DetectorType, len(dpb.DetectorType_value)) validDetectors = make(map[dpb.DetectorType]struct{}, len(dpb.DetectorType_value)) maxDetectorType dpb.DetectorType ) // Setup package local global variables. func init() { for k, v := range dpb.DetectorType_value { dt := dpb.DetectorType(v) detectorTypeValue[strings.ToLower(k)] = dt validDetectors[dt] = struct{}{} if dt > maxDetectorType { maxDetectorType = dt } } } // DetectorID identifies a detector type and version. This struct is used as a // way for users to identify detectors, whether unique or not. A DetectorID // with Version = 0 indicates all possible versions of a detector. type DetectorID struct { ID dpb.DetectorType Version int } // GetDetectorID extracts the DetectorID from a Detector. func GetDetectorID(d detectors.Detector) DetectorID { var version int if v, ok := d.(detectors.Versioner); ok { version = v.Version() } return DetectorID{ ID: d.Type(), Version: version, } } // ParseDetectors parses user supplied string into a list of detectors types. // "all" will return the list of all available detectors. The input is comma // separated and may use the case-insensitive detector name defined in the // protobuf, or the protobuf enum number. A range may be used as well in the // form "start-end". Order is preserved and duplicates are ignored. func ParseDetectors(input string) ([]DetectorID, error) { var output []DetectorID seenDetector := map[DetectorID]struct{}{} for _, item := range strings.Split(input, ",") { item = strings.TrimSpace(item) if item == "" { continue } detectors, ok := specialGroups[strings.ToLower(item)] if !ok { var err error detectors, err = asRange(item) if err != nil { return nil, err } } for _, d := range detectors { if _, ok := seenDetector[d]; ok { continue } seenDetector[d] = struct{}{} output = append(output, d) } } return output, nil } // ParseDetector parses a user supplied string into a single DetectorID. Input // is case-insensitive and either the detector name or ID may be used. func ParseDetector(input string) (DetectorID, error) { return asDetectorID(strings.TrimSpace(input)) } // ParseVerifierEndpoints parses a map of user supplied verifier URLs. The // input keys are detector IDs and the values are a comma separated list of // URLs. The URLs are validated as HTTPS endpoints. func ParseVerifierEndpoints(verifierURLs map[string]string) (map[DetectorID][]string, error) { verifiers := make(map[DetectorID][]string, len(verifierURLs)) for detectorID, urls := range verifierURLs { key, err := ParseDetector(detectorID) if err != nil { return nil, fmt.Errorf("invalid detector ID for verifier: %w", err) } verifierURLs := strings.Split(urls, ",") for i, rawEndpoint := range verifierURLs { rawEndpoint := strings.TrimSpace(rawEndpoint) verifierURLs[i] = rawEndpoint if endpoint, err := url.Parse(rawEndpoint); err != nil { return nil, fmt.Errorf("invalid verifier url %q: %w", rawEndpoint, err) } else if endpoint.Scheme != "https" { return nil, fmt.Errorf("verifier url must be https: %q", rawEndpoint) } } verifiers[key] = append(verifiers[key], verifierURLs...) } return verifiers, nil } func (id DetectorID) String() string { name := dpb.DetectorType_name[int32(id.ID)] if name == "" { name = "" } if id.Version == 0 { return name } return fmt.Sprintf("%s.v%d", name, id.Version) } // allDetectors reutrns an ordered slice of all detector types. func allDetectors() []DetectorID { all := make([]DetectorID, 0, len(dpb.DetectorType_name)) for id := range dpb.DetectorType_name { all = append(all, DetectorID{ID: dpb.DetectorType(id)}) } sort.Slice(all, func(i, j int) bool { return all[i].ID < all[j].ID }) return all } // asRange converts a single input into a slice of detector types. If the input // is not in range format, a slice of length 1 is returned. Unbounded ranges // are allowed. func asRange(input string) ([]DetectorID, error) { // Check if it's a single detector type. dt, err := asDetectorID(input) if err == nil { return []DetectorID{dt}, nil } // Check if it's a range; if not return the error from above. start, end, found := strings.Cut(input, "-") if !found { return nil, err } start, end = strings.TrimSpace(start), strings.TrimSpace(end) // Convert the range start and end to a DetectorType. dtStart, err := asDetectorID(start) if err != nil { return nil, err } dtEnd, err := asDetectorID(end) // If end is empty it's an unbounded range. if err != nil && end != "" { return nil, err } if end == "" { dtEnd.ID = maxDetectorType } // Ensure these ranges don't have versions. if dtEnd.Version != 0 || dtStart.Version != 0 { return nil, fmt.Errorf("versions within ranges are not supported: %s", input) } step := dpb.DetectorType(1) if dtStart.ID > dtEnd.ID { step = -1 } var output []DetectorID for dt := dtStart.ID; dt != dtEnd.ID; dt += step { if _, ok := validDetectors[dt]; !ok { continue } output = append(output, DetectorID{ID: dt}) } return append(output, dtEnd), nil } // asDetectorID converts the case-insensitive input into a DetectorID. Name or // ID may be used. Input is expected to be already trimmed of whitespace. func asDetectorID(input string) (DetectorID, error) { if input == "" { return DetectorID{}, fmt.Errorf("empty detector") } var detectorID DetectorID // Separate the version if there is one. if detector, version, hasVersion := strings.Cut(input, "."); hasVersion { parsedVersion, err := parseVersion(version) if err != nil { return DetectorID{}, fmt.Errorf("invalid version for input: %q error: %w", input, err) } detectorID.Version = parsedVersion // Because there was a version, the detector type input is the part before the '.' input = detector } // Check if it's a named detector. if dt, ok := detectorTypeValue[strings.ToLower(input)]; ok { detectorID.ID = dt return detectorID, nil } // Check if it's a detector ID. if i, err := strconv.ParseInt(input, 10, 32); err == nil { dt := dpb.DetectorType(i) if _, ok := validDetectors[dt]; !ok { return DetectorID{}, fmt.Errorf("invalid detector ID: %s", input) } detectorID.ID = dt return detectorID, nil } return DetectorID{}, fmt.Errorf("unrecognized detector type: %s", input) } func parseVersion(v string) (int, error) { if !strings.HasPrefix(strings.ToLower(v), "v") { return 0, fmt.Errorf("version must start with 'v'") } version := strings.TrimLeft(v, "vV") return strconv.Atoi(version) } trufflehog-3.60.0/pkg/config/detectors_test.go000066400000000000000000000040011451332102400213430ustar00rootroot00000000000000package config import ( "testing" "github.com/stretchr/testify/assert" dpb "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDetectorParsing(t *testing.T) { tests := map[string]struct { input string expected []DetectorID }{ "all": {"AlL", allDetectors()}, "trailing range": {"0-", allDetectors()}, "all after 1": {"1-", allDetectors()[1:]}, "named and valid range": {"aWs,8-9", []DetectorID{{ID: dpb.DetectorType_AWS}, {ID: dpb.DetectorType_Github}, {ID: dpb.DetectorType_Gitlab}}}, "duplicate order preserved": {"9, 8, 9", []DetectorID{{ID: 9}, {ID: 8}}}, "named range": {"github - gitlab", []DetectorID{{ID: dpb.DetectorType_Github}, {ID: dpb.DetectorType_Gitlab}}}, "range preserved": {"8-9, 7-10", []DetectorID{{ID: 8}, {ID: 9}, {ID: 7}, {ID: 10}}}, "reverse range": {"9-8", []DetectorID{{ID: 9}, {ID: 8}}}, "range preserved with all": {"10-,all", append(allDetectors()[10:], allDetectors()[:10]...)}, "empty list item": {"8, ,9", []DetectorID{{ID: 8}, {ID: 9}}}, "invalid end range": {"0-1337", nil}, "invalid name": {"foo", nil}, "negative": {"-1", nil}, "github.v1": {"github.v1", []DetectorID{{ID: dpb.DetectorType_Github, Version: 1}}}, "gitlab.v100": {"gitlab.v100", []DetectorID{{ID: dpb.DetectorType_Gitlab, Version: 100}}}, "range with versions": {"github.v2 - gitlab.v1", nil}, "invalid version no v": {"gitlab.2", nil}, "invalid version no number": {"gitlab.github", nil}, "capital V is fine": {"GiTlAb.V2", []DetectorID{{ID: dpb.DetectorType_Gitlab, Version: 2}}}, "id number with version": {"8.v2", []DetectorID{{ID: 8, Version: 2}}}, } for name, tt := range tests { t.Run(name, func(t *testing.T) { got, gotErr := ParseDetectors(tt.input) if tt.expected == nil { assert.Error(t, gotErr) return } assert.Equal(t, tt.expected, got) }) } } trufflehog-3.60.0/pkg/context/000077500000000000000000000000001451332102400162055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/context/context.go000066400000000000000000000121371451332102400202240ustar00rootroot00000000000000package context import ( "context" "os" "time" "github.com/go-logr/logr" "github.com/trufflesecurity/trufflehog/v3/pkg/log" ) var ( // defaultLogger can be set via SetDefaultLogger. // It is initialized to write to stderr. To disable, you can call // SetDefaultLogger with logr.Discard(). defaultLogger logr.Logger ) func init() { defaultLogger, _ = log.New("context", log.WithConsoleSink(os.Stderr)) } // Context wraps context.Context and includes an additional Logger() method. type Context interface { context.Context Logger() logr.Logger } // CancelFunc and CancelCauseFunc are type aliases to allow use as if they are // the same types as the standard library variants. type CancelFunc = context.CancelFunc type CancelCauseFunc = context.CancelCauseFunc // logCtx implements Context. type logCtx struct { // Embed context.Context to get all methods for free. context.Context log logr.Logger } // Logger returns a structured logger. func (l logCtx) Logger() logr.Logger { return l.log } // Background returns context.Background with a default logger. func Background() Context { return logCtx{ log: defaultLogger, Context: context.Background(), } } // TODO returns context.TODO with a default logger. func TODO() Context { return logCtx{ log: defaultLogger, Context: context.TODO(), } } // WithCancel returns context.WithCancel with the log object propagated. func WithCancel(parent Context) (Context, context.CancelFunc) { ctx, cancel := context.WithCancel(parent) lCtx := logCtx{ log: parent.Logger(), Context: ctx, } return lCtx, cancel } // WithCancelCause returns context.WithCancelCause with the log object propagated. func WithCancelCause(parent Context) (Context, context.CancelCauseFunc) { ctx, cancel := context.WithCancelCause(parent) lCtx := logCtx{ log: parent.Logger(), Context: ctx, } return lCtx, cancel } // WithDeadline returns context.WithDeadline with the log object propagated and // the deadline added to the structured log values. func WithDeadline(parent Context, d time.Time) (Context, context.CancelFunc) { ctx, cancel := context.WithDeadline(parent, d) lCtx := logCtx{ log: parent.Logger().WithValues("deadline", d), Context: ctx, } return lCtx, cancel } // WithDeadlineCause returns context.WithDeadlineCause with the log object // propagated and the deadline added to the structured log values. func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, context.CancelFunc) { ctx, cancel := context.WithDeadlineCause(parent, d, cause) lCtx := logCtx{ log: parent.Logger().WithValues("deadline", d), Context: ctx, } return lCtx, cancel } // WithTimeout returns context.WithTimeout with the log object propagated and // the timeout added to the structured log values. func WithTimeout(parent Context, timeout time.Duration) (Context, context.CancelFunc) { ctx, cancel := context.WithTimeout(parent, timeout) lCtx := logCtx{ log: parent.Logger().WithValues("timeout", timeout), Context: ctx, } return lCtx, cancel } // WithTimeoutCause returns context.WithTimeoutCause with the log object // propagated and the timeout added to the structured log values. func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, context.CancelFunc) { ctx, cancel := context.WithTimeoutCause(parent, timeout, cause) lCtx := logCtx{ log: parent.Logger().WithValues("timeout", timeout), Context: ctx, } return lCtx, cancel } // Cause returns the context.Cause of the context. func Cause(ctx context.Context) error { return context.Cause(ctx) } // WithValue returns context.WithValue with the log object propagated and // 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() if k, ok := key.(string); ok { logger = logger.WithValues(k, val) } return logCtx{ log: logger, Context: context.WithValue(parent, key, val), } } // WithValues returns context.WithValue with the log object propagated and // the values added to the structured log values (if the key is a string). func WithValues(parent Context, keyAndVals ...any) Context { ctx := parent for i := 0; i < len(keyAndVals)-1; i += 2 { ctx = WithValue(ctx, keyAndVals[i], keyAndVals[i+1]) } return ctx } // WithLogger converts a context.Context into a Context by adding a logger. func WithLogger(parent context.Context, logger logr.Logger) Context { return logCtx{ log: logger, Context: parent, } } // AddLogger converts a context.Context into a Context. If the underlying type // is already a Context, that will be returned, otherwise a default logger will // be added. func AddLogger(parent context.Context) Context { if loggerCtx, ok := parent.(Context); ok { return loggerCtx } return WithLogger(parent, defaultLogger) } // SetupDefaultLogger sets the package-level global default logger that will be // used for Background and TODO contexts. On startup, the default logger will // be configured to output logs to stderr. Use logr.Discard() to disable all // logs from Contexts. func SetDefaultLogger(l logr.Logger) { defaultLogger = l } trufflehog-3.60.0/pkg/context/context_test.go000066400000000000000000000111221451332102400212540ustar00rootroot00000000000000package context import ( "bytes" "context" "fmt" "strings" "testing" "time" "github.com/go-logr/logr" "github.com/go-logr/zapr" "github.com/stretchr/testify/assert" "github.com/trufflesecurity/trufflehog/v3/pkg/log" "go.uber.org/zap" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest" ) // testLogger is a helper function to create a logger with a closure callback. func testLogger(t *testing.T, f func(zapcore.Entry)) logr.Logger { return zapr.NewLogger(zaptest.NewLogger(t, zaptest.WrapOptions(zap.Hooks(func(e zapcore.Entry) error { f(e) return nil })))) } // infoCounterContext is a helper function to create a Context that will count // the number of Info messages logged. func infoCounterContext(t *testing.T) (Context, *int) { var infoCount int logger := testLogger(t, func(e zapcore.Entry) { if e.Level == zap.InfoLevel { infoCount++ } }) return WithLogger(context.Background(), logger), &infoCount } func TestWithCancel(t *testing.T) { parentCtx, infoCount := infoCounterContext(t) ctx, cancel := WithCancel(parentCtx) cancel() assert.Equal(t, 0, *infoCount) select { case <-ctx.Done(): ctx.Logger().Info("yay") case <-time.After(1 * time.Second): assert.Fail(t, "context should be done") } assert.Equal(t, 1, *infoCount) } func TestWithTimeout(t *testing.T) { parentCtx, infoCount := infoCounterContext(t) ctx, cancel := WithTimeout(parentCtx, 10*time.Millisecond) defer cancel() assert.Equal(t, 0, *infoCount) select { case <-ctx.Done(): ctx.Logger().Info("yay") case <-time.After(1 * time.Second): assert.Fail(t, "context should be done") } assert.Equal(t, 1, *infoCount) ctx, cancel = WithTimeout(parentCtx, 1*time.Second) defer cancel() select { case <-ctx.Done(): assert.Fail(t, "context should not be done") case <-time.After(10 * time.Millisecond): ctx.Logger().Info("yay") } assert.Equal(t, 2, *infoCount) } func TestWithLogger(t *testing.T) { var infoCount int logger := testLogger(t, func(e zapcore.Entry) { if e.Level == zap.InfoLevel { infoCount++ } }) ctx := WithLogger(context.Background(), logger) assert.Equal(t, logger, ctx.Logger()) assert.Equal(t, 0, infoCount) ctx.Logger().Info("yay") assert.Equal(t, 1, infoCount) } func TestAsContext(t *testing.T) { var gotValue any normalFuncThatTakesContext := func(ctx context.Context) { if logCtx, ok := ctx.(Context); ok { logCtx.Logger().Info("yay") } gotValue = ctx.Value("key") } parentCtx, infoCount := infoCounterContext(t) ctx := WithValue(parentCtx, "key", "value") assert.Equal(t, 0, *infoCount) normalFuncThatTakesContext(ctx) assert.Equal(t, 1, *infoCount) assert.Equal(t, "value", gotValue) } func TestWithValues(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) { ctx1 := Background() ctx1.Logger().Info("only a", "a", 0) ctx2 := WithValue(ctx1, "b", 1) ctx2.Logger().Info("only b") assert.Equal(t, 1, ctx2.Value("b")) ctx3 := WithLogger(ctx2, ctx2.Logger().WithValues("c", 2, "d", 3)) ctx3.Logger().Info("bcd") ctx2.Logger().Info("only b again") type customKey string ctx4 := WithValue(Background(), customKey("foo"), "bar") // foo:bar shouldn't be added to the logger because the key isn't a string ctx4.Logger().Info("foo") ctx5 := WithValues(ctx2, "e", 4, "f", 5, 6, "six") ctx5.Logger().Info("bef") assert.Equal(t, "six", ctx5.Value(6)) ctx6 := WithValues(ctx2, "what does this do?") ctx6.Logger().Info("silently fail I suppose") } assert.Nil(t, sync()) logs := strings.Split(strings.TrimSpace(buffer.String()), "\n") assert.Equal(t, 7, len(logs)) assert.Contains(t, logs[0], `{"a": 0}`) assert.Contains(t, logs[1], `{"b": 1}`) assert.Contains(t, logs[2], `{"b": 1, "c": 2, "d": 3}`) assert.Contains(t, logs[3], `{"b": 1}`) assert.NotContains(t, logs[4], `{"foo": "bar"}`) assert.Contains(t, logs[5], `{"b": 1, "e": 4, "f": 5}`) assert.Contains(t, logs[6], `silently fail`) assert.NotContains(t, logs[6], `what does this do?`) } func TestDefaultLogger(t *testing.T) { var panicked bool defer func() { if r := recover(); r != nil { panicked = true } assert.False(t, panicked) }() ctx := Background() ctx.Logger().Info("this shouldn't panic") } func TestRace(t *testing.T) { ctx, cancel := WithCancel(Background()) go cancel() go func() { _ = ctx.Err() }() cancel() _ = ctx.Err() } func TestCause(t *testing.T) { ctx, cancel := WithCancelCause(Background()) err := fmt.Errorf("oh no") cancel(err) assert.Equal(t, err, Cause(ctx)) } trufflehog-3.60.0/pkg/custom_detectors/000077500000000000000000000000001451332102400201075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/custom_detectors/custom_detectors.go000066400000000000000000000156001451332102400240260ustar00rootroot00000000000000package custom_detectors import ( "bytes" "context" "encoding/json" "net/http" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "golang.org/x/sync/errgroup" ) // The maximum number of matches from one chunk. This const is used when // permutating each regex match to protect the scanner from doing too much work // for poorly defined regexps. const maxTotalMatches = 100 // customRegexWebhook is a CustomRegex with webhook validation that is // guaranteed to be valid (assuming the data is not changed after // initialization). type customRegexWebhook struct { *custom_detectorspb.CustomRegex } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*customRegexWebhook)(nil) // NewWebhookCustomRegex initializes and validates a customRegexWebhook. An // unexported type is intentionally returned here to ensure the values have // been validated. func NewWebhookCustomRegex(pb *custom_detectorspb.CustomRegex) (*customRegexWebhook, error) { // TODO: Return all validation errors. if err := ValidateKeywords(pb.Keywords); err != nil { return nil, err } if err := ValidateRegex(pb.Regex); err != nil { return nil, err } for _, verify := range pb.Verify { if err := ValidateVerifyEndpoint(verify.Endpoint, verify.Unsafe); err != nil { return nil, err } if err := ValidateVerifyHeaders(verify.Headers); err != nil { return nil, err } } // TODO: Copy only necessary data out of pb. return &customRegexWebhook{pb}, nil } var httpClient = common.SaneHttpClient() func (c *customRegexWebhook) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) regexMatches := make(map[string][][]string, len(c.GetRegex())) // Find all submatches for each regex. for name, regex := range c.GetRegex() { regex, err := regexp.Compile(regex) if err != nil { // This will only happen if the regex is invalid. return nil, err } regexMatches[name] = regex.FindAllStringSubmatch(dataStr, -1) } // Permutate each individual match. // { // "foo": [["match1"]] // "bar": [["match2"], ["match3"]] // } // becomes // [ // {"foo": ["match1"], "bar": ["match2"]}, // {"foo": ["match1"], "bar": ["match3"]}, // ] matches := permutateMatches(regexMatches) g := new(errgroup.Group) // Create result object and test for verification. resultsCh := make(chan detectors.Result, maxTotalMatches) for _, match := range matches { match := match g.Go(func() error { return c.createResults(ctx, match, verify, resultsCh) }) } // Ignore any errors and collect as many of the results as we can. _ = g.Wait() close(resultsCh) for result := range resultsCh { // NOTE: I don't believe this is being set anywhere else, hence the map assignment. result.ExtraData = map[string]string{ "name": c.GetName(), } results = append(results, result) } return results, nil } func (c *customRegexWebhook) createResults(ctx context.Context, match map[string][]string, verify bool, results chan<- detectors.Result) error { if common.IsDone(ctx) { // TODO: Log we're possibly leaving out results. return ctx.Err() } var raw string for _, values := range match { // values[0] contains the entire regex match. raw += values[0] } result := detectors.Result{ DetectorType: detectorspb.DetectorType_CustomRegex, DetectorName: c.GetName(), Raw: []byte(raw), } if !verify { select { case <-ctx.Done(): return ctx.Err() case results <- result: return nil } } // Verify via webhook. jsonBody, err := json.Marshal(map[string]map[string][]string{ c.GetName(): match, }) if err != nil { // This should never happen, but if it does, return nil to not // disrupt other verification. return nil } // Try each config until we successfully verify. for _, verifyConfig := range c.GetVerify() { if common.IsDone(ctx) { // TODO: Log we're possibly leaving out results. return ctx.Err() } req, err := http.NewRequestWithContext(ctx, "POST", verifyConfig.GetEndpoint(), bytes.NewReader(jsonBody)) if err != nil { continue } for _, header := range verifyConfig.GetHeaders() { key, value, found := strings.Cut(header, ":") if !found { // Should be unreachable due to validation. continue } req.Header.Add(key, strings.TrimLeft(value, "\t\n\v\f\r ")) } res, err := httpClient.Do(req) if err != nil { continue } // TODO: Read response body. res.Body.Close() if res.StatusCode == http.StatusOK { result.Verified = true break } } select { case <-ctx.Done(): return ctx.Err() case results <- result: return nil } } func (c *customRegexWebhook) Keywords() []string { return c.GetKeywords() } // productIndices produces a permutation of indices for each length. Example: // productIndices(3, 2) -> [[0 0] [1 0] [2 0] [0 1] [1 1] [2 1]]. It returns // a slice of length no larger than maxTotalMatches. func productIndices(lengths ...int) [][]int { count := 1 for _, l := range lengths { count *= l } if count == 0 { return nil } if count > maxTotalMatches { count = maxTotalMatches } results := make([][]int, count) for i := 0; i < count; i++ { j := 1 result := make([]int, 0, len(lengths)) for _, l := range lengths { result = append(result, (i/j)%l) j *= l } results[i] = result } return results } // permutateMatches converts the list of all regex matches into all possible // permutations selecting one from each named entry in the map. For example: // {"foo": [matchA, matchB], "bar": [matchC]} becomes // // [{"foo": matchA, "bar": matchC}, {"foo": matchB, "bar": matchC}] func permutateMatches(regexMatches map[string][][]string) []map[string][]string { // Get a consistent order for names and their matching lengths. // The lengths are used in calculating the permutation so order matters. names := make([]string, 0, len(regexMatches)) lengths := make([]int, 0, len(regexMatches)) for key, value := range regexMatches { names = append(names, key) lengths = append(lengths, len(value)) } // Permutate all the indices for each match. For example, if "foo" has // [matchA, matchB] and "bar" has [matchC], we will get indices [0 0] [1 0]. permutationIndices := productIndices(lengths...) // Build {"foo": matchA, "bar": matchC} and {"foo": matchB, "bar": matchC} // from the indices. var matches []map[string][]string for _, permutation := range permutationIndices { candidate := make(map[string][]string, len(permutationIndices)) for i, name := range names { candidate[name] = regexMatches[name][permutation[i]] } matches = append(matches, candidate) } return matches } func (c *customRegexWebhook) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CustomRegex } trufflehog-3.60.0/pkg/custom_detectors/custom_detectors_test.go000066400000000000000000000137361451332102400250750ustar00rootroot00000000000000package custom_detectors import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/custom_detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/protoyaml" ) func TestCustomRegexTemplateParsing(t *testing.T) { testCustomRegexTemplateYaml := `name: Internal bi tool keywords: - secret_v1_ - pat_v2_ regex: id_pat_example: ([a-zA-Z0-9]{32}) secret_pat_example: ([a-zA-Z0-9]{32}) verify: - endpoint: http://localhost:8000/{id_pat_example} unsafe: true headers: - 'Authorization: Bearer {secret_pat_example.0}' successRanges: - 200-250 - '288'` var got custom_detectorspb.CustomRegex assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testCustomRegexTemplateYaml), &got)) assert.Equal(t, "Internal bi tool", got.Name) assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords) assert.Equal(t, map[string]string{ "id_pat_example": "([a-zA-Z0-9]{32})", "secret_pat_example": "([a-zA-Z0-9]{32})", }, got.Regex) assert.Equal(t, 1, len(got.Verify)) assert.Equal(t, "http://localhost:8000/{id_pat_example}", got.Verify[0].Endpoint) assert.Equal(t, true, got.Verify[0].Unsafe) assert.Equal(t, []string{"Authorization: Bearer {secret_pat_example.0}"}, got.Verify[0].Headers) assert.Equal(t, []string{"200-250", "288"}, got.Verify[0].SuccessRanges) } func TestCustomRegexWebhookParsing(t *testing.T) { testCustomRegexWebhookYaml := `name: Internal bi tool keywords: - secret_v1_ - pat_v2_ regex: id_pat_example: ([a-zA-Z0-9]{32}) secret_pat_example: ([a-zA-Z0-9]{32}) verify: - endpoint: http://localhost:8000/ unsafe: true headers: - 'Authorization: Bearer token'` var got custom_detectorspb.CustomRegex assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testCustomRegexWebhookYaml), &got)) assert.Equal(t, "Internal bi tool", got.Name) assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords) assert.Equal(t, map[string]string{ "id_pat_example": "([a-zA-Z0-9]{32})", "secret_pat_example": "([a-zA-Z0-9]{32})", }, got.Regex) assert.Equal(t, 1, len(got.Verify)) assert.Equal(t, "http://localhost:8000/", got.Verify[0].Endpoint) assert.Equal(t, true, got.Verify[0].Unsafe) assert.Equal(t, []string{"Authorization: Bearer token"}, got.Verify[0].Headers) } // TestCustomDetectorsParsing tests the full `detectors` configuration. func TestCustomDetectorsParsing(t *testing.T) { // TODO: Support both template and webhook. testYamlConfig := `detectors: - name: Internal bi tool keywords: - secret_v1_ - pat_v2_ regex: id_pat_example: ([a-zA-Z0-9]{32}) secret_pat_example: ([a-zA-Z0-9]{32}) verify: - endpoint: http://localhost:8000/ unsafe: true headers: - 'Authorization: Bearer token'` var messages custom_detectorspb.CustomDetectors assert.NoError(t, protoyaml.UnmarshalStrict([]byte(testYamlConfig), &messages)) assert.Equal(t, 1, len(messages.Detectors)) got := messages.Detectors[0] assert.Equal(t, "Internal bi tool", got.Name) assert.Equal(t, []string{"secret_v1_", "pat_v2_"}, got.Keywords) assert.Equal(t, map[string]string{ "id_pat_example": "([a-zA-Z0-9]{32})", "secret_pat_example": "([a-zA-Z0-9]{32})", }, got.Regex) assert.Equal(t, 1, len(got.Verify)) assert.Equal(t, "http://localhost:8000/", got.Verify[0].Endpoint) assert.Equal(t, true, got.Verify[0].Unsafe) assert.Equal(t, []string{"Authorization: Bearer token"}, got.Verify[0].Headers) } func TestFromData_InvalidRegEx(t *testing.T) { c := &customRegexWebhook{ &custom_detectorspb.CustomRegex{ Name: "Internal bi tool", Keywords: []string{"secret_v1_", "pat_v2_"}, Regex: map[string]string{ "test": "!!?(?:?)[a-zA-Z0-9]{32}", // invalid regex }, }, } _, err := c.FromData(context.Background(), false, []byte("test")) assert.Error(t, err) } func TestProductIndices(t *testing.T) { tests := []struct { name string input []int want [][]int }{ { name: "zero", input: []int{3, 0}, want: nil, }, { name: "one input", input: []int{3}, want: [][]int{{0}, {1}, {2}}, }, { name: "two inputs", input: []int{3, 2}, want: [][]int{ {0, 0}, {1, 0}, {2, 0}, {0, 1}, {1, 1}, {2, 1}, }, }, { name: "three inputs", input: []int{3, 2, 3}, want: [][]int{ {0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {0, 1, 0}, {1, 1, 0}, {2, 1, 0}, {0, 0, 1}, {1, 0, 1}, {2, 0, 1}, {0, 1, 1}, {1, 1, 1}, {2, 1, 1}, {0, 0, 2}, {1, 0, 2}, {2, 0, 2}, {0, 1, 2}, {1, 1, 2}, {2, 1, 2}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := productIndices(tt.input...) assert.Equal(t, tt.want, got) }) } } func TestProductIndicesMax(t *testing.T) { got := productIndices(2, 3, 4, 5, 6) assert.GreaterOrEqual(t, 2*3*4*5*6, maxTotalMatches) assert.Equal(t, maxTotalMatches, len(got)) } func TestPermutateMatches(t *testing.T) { tests := []struct { name string input map[string][][]string want []map[string][]string }{ { name: "two matches", input: map[string][][]string{"foo": {{"matchA"}, {"matchB"}}, "bar": {{"matchC"}}}, want: []map[string][]string{ {"foo": {"matchA"}, "bar": {"matchC"}}, {"foo": {"matchB"}, "bar": {"matchC"}}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := permutateMatches(tt.input) assert.Equal(t, tt.want, got) }) } } func TestDetector(t *testing.T) { detector, err := NewWebhookCustomRegex(&custom_detectorspb.CustomRegex{ Name: "test", // "password" is normally flagged as a false positive, but CustomRegex // should allow the user to decide and report it as a result. Keywords: []string{"password"}, Regex: map[string]string{"regex": "password=.*"}, }) assert.NoError(t, err) results, err := detector.FromData(context.Background(), false, []byte(`password="123456"`)) assert.NoError(t, err) assert.Equal(t, 1, len(results)) assert.Equal(t, results[0].Raw, []byte(`password="123456"`)) } func BenchmarkProductIndices(b *testing.B) { for i := 0; i < b.N; i++ { _ = productIndices(3, 2, 6) } } trufflehog-3.60.0/pkg/custom_detectors/regex_varstring.go000066400000000000000000000021651451332102400236530ustar00rootroot00000000000000package custom_detectors import ( "regexp" "strconv" "strings" ) // nameGroupRegex matches `{ name . group }` ignoring any whitespace. var nameGroupRegex = regexp.MustCompile(`{\s*([a-zA-Z0-9-_]+)\s*(\.\s*[0-9]*)?\s*}`) // RegexVarString is a string with embedded {name.group} variables. A name may // only contain alphanumeric, hyphen, and underscore characters. Group is // optional but if provided it must be a non-negative integer. If the group is // omitted it defaults to 0. type RegexVarString struct { original string // map from name to group variables map[string]int } func NewRegexVarString(original string) RegexVarString { variables := make(map[string]int) matches := nameGroupRegex.FindAllStringSubmatch(original, -1) for _, match := range matches { name, group := match[1], 0 // The second match will start with a period followed by any number // of whitespace. if len(match[2]) > 1 { g, err := strconv.Atoi(strings.TrimSpace(match[2][1:])) if err != nil { continue } group = g } variables[name] = group } return RegexVarString{ original: original, variables: variables, } } trufflehog-3.60.0/pkg/custom_detectors/regex_varstring_test.go000066400000000000000000000024421451332102400247100ustar00rootroot00000000000000package custom_detectors import ( "testing" "github.com/stretchr/testify/assert" ) func TestVarString(t *testing.T) { tests := []struct { name string input string wantVars map[string]int }{ { name: "empty", input: "{}", wantVars: map[string]int{}, }, { name: "no subgroup", input: "{hello}", wantVars: map[string]int{ "hello": 0, }, }, { name: "with subgroup", input: "{hello.123}", wantVars: map[string]int{ "hello": 123, }, }, { name: "subgroup with spaces", input: "{\thell0 . 123 }", wantVars: map[string]int{ "hell0": 123, }, }, { name: "multiple groups", input: "foo {bar} {bazz.buzz} {buzz.2}", wantVars: map[string]int{ "bar": 0, "buzz": 2, }, }, { name: "nested groups", input: "{foo {bar}}", wantVars: map[string]int{ "bar": 0, }, }, { name: "decimal without number", input: "{foo.}", wantVars: map[string]int{ "foo": 0, }, }, { name: "negative number", input: "{foo.-1}", wantVars: map[string]int{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := NewRegexVarString(tt.input) assert.Equal(t, tt.input, got.original) assert.Equal(t, tt.wantVars, got.variables) }) } } trufflehog-3.60.0/pkg/custom_detectors/validation.go000066400000000000000000000050011451332102400225640ustar00rootroot00000000000000package custom_detectors import ( "fmt" "regexp" "strconv" "strings" ) func ValidateKeywords(keywords []string) error { if len(keywords) == 0 { return fmt.Errorf("no keywords") } for _, keyword := range keywords { if len(keyword) == 0 { return fmt.Errorf("empty keyword") } } return nil } func ValidateRegex(regex map[string]string) error { if len(regex) == 0 { return fmt.Errorf("no regex") } for name, reg := range regex { if _, err := regexp.Compile(reg); err != nil { return fmt.Errorf("regex '%s': %w", name, err) } } return nil } func ValidateVerifyEndpoint(endpoint string, unsafe bool) error { if len(endpoint) == 0 { return fmt.Errorf("no endpoint") } if strings.HasPrefix(endpoint, "http://") && !unsafe { return fmt.Errorf("http endpoint must have unsafe=true") } return nil } func ValidateVerifyHeaders(headers []string) error { for _, header := range headers { if !strings.Contains(header, ":") { return fmt.Errorf("header %q must contain a colon", header) } } return nil } func ValidateVerifyRanges(ranges []string) error { const httpLowerRange = 100 const httpUpperRange = 599 for _, successRange := range ranges { if !strings.Contains(successRange, "-") { httpCode, err := strconv.Atoi(successRange) if err != nil { return fmt.Errorf("unable to convert http code to int %q", successRange) } if httpCode < httpLowerRange || httpCode > httpUpperRange { return fmt.Errorf("invalid http status code %q", successRange) } continue } httpRange := strings.Split(successRange, "-") if len(httpRange) != 2 { return fmt.Errorf("invalid range format %q", successRange) } lowerBound, err := strconv.Atoi(httpRange[0]) if err != nil { return fmt.Errorf("unable to convert lower bound to int %q", successRange) } upperBound, err := strconv.Atoi(httpRange[1]) if err != nil { return fmt.Errorf("unable to convert upper bound to int %q", successRange) } if lowerBound > upperBound { return fmt.Errorf("lower bound greater than upper bound on range %q", successRange) } if lowerBound < httpLowerRange || upperBound > httpUpperRange { return fmt.Errorf("invalid http status code range %q", successRange) } } return nil } func ValidateRegexVars(regex map[string]string, body ...string) error { for _, b := range body { matches := NewRegexVarString(b).variables for match := range matches { if _, ok := regex[match]; !ok { return fmt.Errorf("body %q contains an unknown variable", b) } } } return nil } trufflehog-3.60.0/pkg/custom_detectors/validation_test.go000066400000000000000000000124631451332102400236350ustar00rootroot00000000000000package custom_detectors import "testing" func TestCustomDetectorsKeywordValidation(t *testing.T) { tests := []struct { name string input []string wantErr bool }{ { name: "Test empty list of keywords", input: []string{}, wantErr: true, }, { name: "Test empty keyword", input: []string{""}, wantErr: true, }, { name: "Test valid keywords", input: []string{"hello", "world"}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateKeywords(tt.input) if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) { t.Errorf("ValidateKeywords() error = %v, wantErr %v", got, tt.wantErr) } }) } } func TestCustomDetectorsRegexValidation(t *testing.T) { tests := []struct { name string input map[string]string wantErr bool }{ { name: "Test list of keywords", input: map[string]string{ "id_pat_example": "([a-zA-Z0-9]{32})", }, wantErr: false, }, { name: "Test empty list of keywords", input: map[string]string{}, wantErr: true, }, { name: "Test invalid regex", input: map[string]string{ "test": "!!?(?:?)[a-zA-Z0-9]{32}", }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateRegex(tt.input) if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) { t.Errorf("ValidateRegex() error = %v, wantErr %v", got, tt.wantErr) } }) } } func TestCustomDetectorsVerifyEndpointValidation(t *testing.T) { tests := []struct { name string endpoint string unsafe bool wantErr bool }{ { name: "Test http endpoint with unsafe flag", endpoint: "http://localhost:8000/{id_pat_example}", unsafe: true, wantErr: false, }, { name: "Test http endpoint without unsafe flag", endpoint: "http://localhost:8000/{id_pat_example}", unsafe: false, wantErr: true, }, { name: "Test https endpoint with unsafe flag", endpoint: "https://localhost:8000/{id_pat_example}", unsafe: true, wantErr: false, }, { name: "Test https endpoint without unsafe flag", endpoint: "https://localhost:8000/{id_pat_example}", unsafe: false, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateVerifyEndpoint(tt.endpoint, tt.unsafe) if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) { t.Errorf("ValidateVerifyEndpoint() error = %v, wantErr %v", got, tt.wantErr) } }) } } func TestCustomDetectorsVerifyHeadersValidation(t *testing.T) { tests := []struct { name string headers []string wantErr bool }{ { name: "Test single header", headers: []string{"Authorization: Bearer {secret_pat_example.0}"}, wantErr: false, }, { name: "Test invalid header", headers: []string{"Hello world"}, wantErr: true, }, { name: "Test ugly header", headers: []string{"Hello:::::::world::hi:"}, wantErr: false, }, { name: "Test empty header", headers: []string{}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateVerifyHeaders(tt.headers) if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) { t.Errorf("ValidateVerifyHeaders() error = %v, wantErr %v", got, tt.wantErr) } }) } } func TestCustomDetectorsVerifyRangeValidation(t *testing.T) { tests := []struct { name string ranges []string wantErr bool }{ { name: "Test multiple mixed ranges", ranges: []string{"200", "300-350"}, wantErr: false, }, { name: "Test invalid non-number range", ranges: []string{"hi"}, wantErr: true, }, { name: "Test invalid lower to upper range", ranges: []string{"200-100"}, wantErr: true, }, { name: "Test invalid http range", ranges: []string{"400-1000"}, wantErr: true, }, { name: "Test multiple ranges with invalid inputs", ranges: []string{"322", "hello-world", "100-200"}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateVerifyRanges(tt.ranges) if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) { t.Errorf("ValidateVerifyRanges() error = %v, wantErr %v", got, tt.wantErr) } }) } } func TestCustomDetectorsVerifyRegexVarsValidation(t *testing.T) { tests := []struct { name string regex map[string]string body string wantErr bool }{ { name: "Regex defined but not used in body", regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"}, body: "hello world", wantErr: false, }, { name: "Regex defined and is used in body", regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"}, body: "hello world {id}", wantErr: false, }, { name: "Regex var in body but not defined", regex: map[string]string{"id": "[0-9]{1,10}", "id_pat_example": "([a-zA-Z0-9]{32})"}, body: "hello world {hello}", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ValidateRegexVars(tt.regex, tt.body) if (got != nil && !tt.wantErr) || (got == nil && tt.wantErr) { t.Errorf("ValidateRegexVars() error = %v, wantErr %v", got, tt.wantErr) } }) } } trufflehog-3.60.0/pkg/decoders/000077500000000000000000000000001451332102400163115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/decoders/base64.go000066400000000000000000000055561451332102400177370ustar00rootroot00000000000000package decoders import ( "bytes" "encoding/base64" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) type ( Base64 struct{} ) var ( b64Charset = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_=") b64EndChars = "+/-_=" // Given characters are mostly ASCII, we can use a simple array to map. b64CharsetMapping [128]bool ) func init() { // Build an array of all the characters in the base64 charset. for _, char := range b64Charset { b64CharsetMapping[char] = true } } func (d *Base64) FromChunk(chunk *sources.Chunk) *sources.Chunk { encodedSubstrings := getSubstringsOfCharacterSet(chunk.Data, 20, b64CharsetMapping, b64EndChars) decodedSubstrings := make(map[string][]byte) for _, str := range encodedSubstrings { dec, err := base64.StdEncoding.DecodeString(str) if err == nil { if len(dec) > 0 { decodedSubstrings[str] = dec } continue } dec, err = base64.RawURLEncoding.DecodeString(str) if err == nil && len(dec) > 0 { decodedSubstrings[str] = dec } } if len(decodedSubstrings) > 0 { var result bytes.Buffer result.Grow(len(chunk.Data)) start := 0 for _, encoded := range encodedSubstrings { if decoded, ok := decodedSubstrings[encoded]; ok { end := bytes.Index(chunk.Data[start:], []byte(encoded)) if end != -1 { result.Write(chunk.Data[start : start+end]) result.Write(decoded) start += end + len(encoded) } } } result.Write(chunk.Data[start:]) chunk.Data = result.Bytes() return chunk } return nil } func getSubstringsOfCharacterSet(data []byte, threshold int, charsetMapping [128]bool, endChars string) []string { if len(data) == 0 { return nil } count := 0 substringsCount := 0 // Determine the number of substrings that will be returned. // Pre-allocate the slice to avoid reallocations. for _, char := range data { if char < 128 && charsetMapping[char] { count++ } else { if count > threshold { substringsCount++ } count = 0 } } if count > threshold { substringsCount++ } count = 0 start := 0 substrings := make([]string, 0, substringsCount) for i, char := range data { if char < 128 && charsetMapping[char] { if count == 0 { start = i } count++ } else { if count > threshold { substrings = appendB64Substring(data, start, count, substrings, endChars) } count = 0 } } if count > threshold { substrings = appendB64Substring(data, start, count, substrings, endChars) } return substrings } func appendB64Substring(data []byte, start, count int, substrings []string, endChars string) []string { substring := bytes.TrimLeft(data[start:start+count], endChars) if idx := bytes.IndexByte(bytes.TrimRight(substring, endChars), '='); idx != -1 { substrings = append(substrings, string(substring[idx+1:])) } else { substrings = append(substrings, string(substring)) } return substrings } trufflehog-3.60.0/pkg/decoders/base64_test.go000066400000000000000000000102141451332102400207610ustar00rootroot00000000000000package decoders import ( "testing" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) func TestBase64_FromChunk(t *testing.T) { tests := []struct { chunk *sources.Chunk want *sources.Chunk name string }{ { name: "only b64 chunk", chunk: &sources.Chunk{ Data: []byte(`bG9uZ2VyLWVuY29kZWQtc2VjcmV0LXRlc3Q=`), }, want: &sources.Chunk{ Data: []byte(`longer-encoded-secret-test`), }, }, { name: "mixed content", chunk: &sources.Chunk{ Data: []byte(`token: bG9uZ2VyLWVuY29kZWQtc2VjcmV0LXRlc3Q=`), }, want: &sources.Chunk{ Data: []byte(`token: longer-encoded-secret-test`), }, }, { name: "no chunk", chunk: &sources.Chunk{ Data: []byte(``), }, want: nil, }, { name: "env var (looks like all b64 decodable but has `=` in the middle)", chunk: &sources.Chunk{ Data: []byte(`some-encoded-secret=dGVzdHNlY3JldA==`), }, want: &sources.Chunk{ Data: []byte(`some-encoded-secret=testsecret`), }, }, { name: "has longer b64 inside", chunk: &sources.Chunk{ Data: []byte(`some-encoded-secret="bG9uZ2VyLWVuY29kZWQtc2VjcmV0LXRlc3Q="`), }, want: &sources.Chunk{ Data: []byte(`some-encoded-secret="longer-encoded-secret-test"`), }, }, { name: "many possible substrings", chunk: &sources.Chunk{ Data: []byte(`Many substrings in this slack message could be base64 decoded but only dGhpcyBlbmNhcHN1bGF0ZWQgc2VjcmV0 should be decoded.`), }, want: &sources.Chunk{ Data: []byte(`Many substrings in this slack message could be base64 decoded but only this encapsulated secret should be decoded.`), }, }, { name: "b64-url-safe: only b64 chunk", chunk: &sources.Chunk{ Data: []byte(`bG9uZ2VyLWVuY29kZWQtc2VjcmV0LXRlc3Q`), }, want: &sources.Chunk{ Data: []byte(`longer-encoded-secret-test`), }, }, { name: "b64-url-safe: mixed content", chunk: &sources.Chunk{ Data: []byte(`token: bG9uZ2VyLWVuY29kZWQtc2VjcmV0LXRlc3Q`), }, want: &sources.Chunk{ Data: []byte(`token: longer-encoded-secret-test`), }, }, { name: "b64-url-safe: env var (looks like all b64 decodable but has `=` in the middle)", chunk: &sources.Chunk{ Data: []byte(`some-encoded-secret=dGVzdHNlY3JldA`), }, want: &sources.Chunk{ Data: []byte(`some-encoded-secret=testsecret`), }, }, { name: "b64-url-safe: has longer b64 inside", chunk: &sources.Chunk{ Data: []byte(`some-encoded-secret="bG9uZ2VyLWVuY29kZWQtc2VjcmV0LXRlc3Q"`), }, want: &sources.Chunk{ Data: []byte(`some-encoded-secret="longer-encoded-secret-test"`), }, }, { name: "b64-url-safe: hyphen url b64", chunk: &sources.Chunk{ Data: []byte(`dHJ1ZmZsZWhvZz4-ZmluZHMtc2VjcmV0cw`), }, want: &sources.Chunk{ Data: []byte(`trufflehog>>finds-secrets`), }, }, { name: "b64-url-safe: underscore url b64", chunk: &sources.Chunk{ Data: []byte(`YjY0dXJsc2FmZS10ZXN0LXNlY3JldC11bmRlcnNjb3Jlcz8_`), }, want: &sources.Chunk{ Data: []byte(`b64urlsafe-test-secret-underscores??`), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := &Base64{} got := d.FromChunk(tt.chunk) if tt.want != nil { if got == nil { t.Fatal("got nil, did not want nil") } if diff := pretty.Compare(string(got.Data), string(tt.want.Data)); diff != "" { t.Errorf("Base64FromChunk() %s diff: (-got +want)\n%s", tt.name, diff) } } else { if got != nil { t.Error("Expected nil chunk") } } }) } } func BenchmarkFromChunkSmall(b *testing.B) { d := Base64{} data := detectors.MustGetBenchmarkData()["small"] for n := 0; n < b.N; n++ { d.FromChunk(&sources.Chunk{Data: data}) } } func BenchmarkFromChunkMedium(b *testing.B) { d := Base64{} data := detectors.MustGetBenchmarkData()["medium"] for n := 0; n < b.N; n++ { d.FromChunk(&sources.Chunk{Data: data}) } } func BenchmarkFromChunkLarge(b *testing.B) { d := Base64{} data := detectors.MustGetBenchmarkData()["big"] for n := 0; n < b.N; n++ { d.FromChunk(&sources.Chunk{Data: data}) } } trufflehog-3.60.0/pkg/decoders/decoders.go000066400000000000000000000015561451332102400204370ustar00rootroot00000000000000package decoders import ( "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) func DefaultDecoders() []Decoder { return []Decoder{ // UTF8 must be first for duplicate detection &UTF8{}, &Base64{}, &UTF16{}, } } type Decoder interface { FromChunk(chunk *sources.Chunk) *sources.Chunk } // Fuzz is an entrypoint for go-fuzz, which is an AFL-style fuzzing tool. // This one attempts to uncover any panics during decoding. func Fuzz(data []byte) int { decoded := false for i, decoder := range DefaultDecoders() { // Skip the first decoder (plain), because it will always decode and give // priority to the input (return 1). if i == 0 { continue } chunk := decoder.FromChunk(&sources.Chunk{Data: data}) if chunk != nil { decoded = true } } if decoded { return 1 // prioritize the input } return -1 // Don't add input to the corpus. } trufflehog-3.60.0/pkg/decoders/utf16.go000066400000000000000000000017501451332102400176100ustar00rootroot00000000000000package decoders import ( "bytes" "encoding/binary" "unicode/utf8" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) type UTF16 struct{} func (d *UTF16) FromChunk(chunk *sources.Chunk) *sources.Chunk { if chunk == nil || len(chunk.Data) == 0 { return nil } if utf16Data, err := utf16ToUTF8(chunk.Data); err == nil { if len(utf16Data) == 0 { return nil } chunk.Data = utf16Data return chunk } return nil } // utf16ToUTF8 converts a byte slice containing UTF-16 encoded data to a UTF-8 encoded byte slice. func utf16ToUTF8(b []byte) ([]byte, error) { var bufBE, bufLE bytes.Buffer for i := 0; i < len(b)-1; i += 2 { if r := rune(binary.BigEndian.Uint16(b[i:])); b[i] == 0 && utf8.ValidRune(r) { if isValidByte(byte(r)) { bufBE.WriteRune(r) } } if r := rune(binary.LittleEndian.Uint16(b[i:])); b[i+1] == 0 && utf8.ValidRune(r) { if isValidByte(byte(r)) { bufLE.WriteRune(r) } } } return append(bufLE.Bytes(), bufBE.Bytes()...), nil } trufflehog-3.60.0/pkg/decoders/utf16_test.dll000066400000000000000000000120001451332102400210030ustar00rootroot00000000000000MZ˙˙¸@€ē´ Í!¸LÍ!This program cannot be run in DOS mode. $PELī•ā" 0 ū( @@ €`…Š(O@0` ä'T  H.text  `.rsrc0@ @@.reloc `@BŨ(H€ d"( *>( }*2rp( *"( *BSJB v4.0.30319l,#~˜€#Strings4#USL#GUID\#BlobW ú3ĪE—‡ =˛ĩ~ōōËōčōeō›ōTĢãhãoãyã !ãBQ5-5ÛA&ę)P †Y †i ‘,v †L  )19AIQYayi‰8'ũ. 2.;.Z.#c.+Ą.3Ž.;š.CÆ.KĄ.SĄC[ŅC Ņc[Ņc Ņccփ[Ņ€  ) 
$sample1System.ConsoleSystem.RuntimeWriteLineEmbeddedAttributeCompilerGeneratedAttributeAttributeUsageAttributeDebuggableAttributeAssemblyTitleAttributeTargetFrameworkAttributeAssemblyFileVersionAttributeAssemblyInformationalVersionAttributeAssemblyConfigurationAttributeRefSafetyRulesAttributeCompilationRelaxationsAttributeAssemblyProductAttributeAssemblyCompanyAttributeRuntimeCompatibilityAttributeSystem.Runtime.Versioningsample1.dllProgramSystemVersionSystem.Reflection.ctorSystem.DiagnosticsSystem.Runtime.CompilerServicesDebuggingModesargsMicrosoft.CodeAnalysisAttributeTargetsObject/ [default] aws_access_key_id = AKIAXYZDQCEN4B6JSJQI aws_secret_access_key = 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 output = json region = us-east-2 J1FNCŠ@Ŗŗ’úežZ     9°?_Õ :TWrapNonExceptionThrows=.NETCoreApp,Version=v7.0TFrameworkDisplayName.NET 7.0 sample1 Debug 1.0.0.0 1.0.0&T AllowMultipleT Inherited •žŗļMPJ8(8 '‚(‚ RSDS6ÄČOŲjGAۑ0éÜS/private/tmp/sample1/obj/Debug/net7.0/sample1.pdbSHA2566ÄČOŲj—OAۑ0éÜS•žŗļY~ÍŗfĒÜŪ ~šÅŅ(ë( Ũ(_CorExeMainmscoree.dll˙% @ €P€8€€h€<@ŦŦ4VS_VERSION_INFOŊīū?DVarFileInfo$Translation° StringFileInfoč000004b00CompanyNamesample18FileDescriptionsample10FileVersion1.0.0.08 InternalNamesample1.dll(LegalCopyright @ OriginalFilenamesample1.dll0ProductNamesample10ProductVersion1.0.08Assembly Version1.0.0.0LCßīģŋ 9trufflehog-3.60.0/pkg/decoders/utf16_test.go000066400000000000000000000044361451332102400206530ustar00rootroot00000000000000package decoders import ( "bytes" "os" "testing" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) func TestUTF16Decoder(t *testing.T) { testCases := []struct { name string input []byte expected []byte expectNil bool }{ { name: "Valid UTF-16LE input", input: []byte{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", input: []byte{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!"), expected: nil, expectNil: true, }, { name: "Invalid UTF-16 input (odd length)", input: []byte{72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 0}, expected: []byte("Hello Worl"), expectNil: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { chunk := &sources.Chunk{Data: tc.input} decoder := &UTF16{} decodedChunk := decoder.FromChunk(chunk) if tc.expectNil { if decodedChunk != nil { t.Errorf("Expected nil, got chunk with data: %v", decodedChunk.Data) } return } if decodedChunk == nil { t.Errorf("Expected chunk with data, got nil") return } if !bytes.Equal(decodedChunk.Data, tc.expected) { t.Errorf("Expected decoded data: %s, got: %s", tc.expected, decodedChunk.Data) } }) } } func TestDLL(t *testing.T) { data, err := os.ReadFile("utf16_test.dll") if err != nil { t.Errorf("Failed to read test data: %v", err) return } chunk := &sources.Chunk{Data: data} decoder := &UTF16{} decodedChunk := decoder.FromChunk(chunk) if decodedChunk == nil { t.Errorf("Expected chunk with data, got nil") return } if !bytes.Contains(decodedChunk.Data, []byte("aws_secret_access_key")) { t.Errorf("Expected chunk to have aws_secret_access_key") return } } 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++ { _, _ = utf16ToUTF8(data) } } trufflehog-3.60.0/pkg/decoders/utf8.go000066400000000000000000000021001451332102400175170ustar00rootroot00000000000000package decoders import ( "bytes" "unicode/utf8" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) type UTF8 struct{} func (d *UTF8) FromChunk(chunk *sources.Chunk) *sources.Chunk { if chunk == nil || len(chunk.Data) == 0 { return nil } if !utf8.Valid(chunk.Data) { chunk.Data = extractSubstrings(chunk.Data) return chunk } return chunk } // extractSubstrings performs similarly to the strings binutil, // extacting contigous portions of printable characters that we care // about from some bytes func extractSubstrings(b []byte) []byte { field := make([]byte, len(b)) fieldLen := 0 buf := &bytes.Buffer{} for i, c := range b { if isValidByte(c) { field[fieldLen] = c fieldLen++ } else { if fieldLen > 5 { buf.Write(field[:fieldLen]) } fieldLen = 0 } if i == len(b)-1 && fieldLen > 5 { buf.Write(field[:fieldLen]) } } return buf.Bytes() } func isValidByte(c byte) bool { // https://www.rapidtables.com/code/text/ascii-table.html // split on anything that is not ascii space through tilde return c > 31 && c < 127 } trufflehog-3.60.0/pkg/decoders/utf8_test.go000066400000000000000000000035161451332102400205720ustar00rootroot00000000000000package decoders import ( "testing" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) func TestUTF8_FromChunk(t *testing.T) { type args struct { chunk *sources.Chunk } tests := []struct { name string d *UTF8 args args want *sources.Chunk wantErr bool }{ { name: "successful UTF8 decode", d: &UTF8{}, args: args{ chunk: &sources.Chunk{Data: []byte("plain 'ol chunk that should decode successfully")}, }, want: &sources.Chunk{Data: []byte("plain 'ol chunk that should decode successfully")}, wantErr: false, }, { name: "successful binary decode", d: &UTF8{}, args: args{ chunk: &sources.Chunk{Data: []byte("\xf0\x28\x8c\x28 not-entirely utf8 chunk that should decode successfully")}, }, want: &sources.Chunk{Data: []byte("( not-entirely utf8 chunk that should decode successfully")}, wantErr: false, }, { name: "unsuccessful decode", d: &UTF8{}, args: args{ chunk: nil, }, want: nil, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := &UTF8{} got := d.FromChunk(tt.args.chunk) if got != nil && tt.want != nil { if diff := pretty.Compare(string(got.Data), string(tt.want.Data)); diff != "" { t.Errorf("%s: Plain.FromChunk() diff: (-got +want)\n%s", tt.name, diff) } } else { if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("%s: Plain.FromChunk() diff: (-got +want)\n%s", tt.name, diff) } } }) } } var testBytes = []byte(`some words with random spaces and newlines with arbitrary length of hey the lines themselves. and short words that go away.`) func Benchmark_extractSubstrings(b *testing.B) { for i := 0; i < b.N; i++ { extractSubstrings(testBytes) } } trufflehog-3.60.0/pkg/detectors/000077500000000000000000000000001451332102400165155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/abbysale/000077500000000000000000000000001451332102400202775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/abbysale/abbysale.go000066400000000000000000000041221451332102400224070ustar00rootroot00000000000000package abbysale import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"abbysale"}) + `\b([a-z0-9A-Z]{40})\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{"abbysale"} } // FromData will find and optionally verify Abbysale 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Abbysale, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.abyssale.com/ready", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Abbysale } trufflehog-3.60.0/pkg/detectors/abbysale/abbysale_test.go000066400000000000000000000055001451332102400234470ustar00rootroot00000000000000//go:build detectors // +build detectors package abbysale import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAbbysale_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ABBYSALE_TOKEN") inactiveSecret := testSecrets.MustGetField("ABBYSALE_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 abbysale secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Abbysale, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a abbysale 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_Abbysale, 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("Abbysale.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Abbysale.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) } } }) } } trufflehog-3.60.0/pkg/detectors/abstract/000077500000000000000000000000001451332102400203205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/abstract/abstract.go000066400000000000000000000042371451332102400224600ustar00rootroot00000000000000package abstract import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"abstract"}) + `\b([0-9a-z]{32})\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{"abstract"} } // FromData will find and optionally verify Abstract 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Abstract, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://exchange-rates.abstractapi.com/v1/live/?api_key=%s&base=USD", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Abstract } trufflehog-3.60.0/pkg/detectors/abstract/abstract_test.go000066400000000000000000000054721451332102400235210ustar00rootroot00000000000000//go:build detectors // +build detectors package abstract import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAbstract_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("ABSTRACT") inactiveSecret := testSecrets.MustGetField("ABSTRACT_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 abstract secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Abstract, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a abstract 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_Abstract, 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("Abstract.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Abstract.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) } } }) } } trufflehog-3.60.0/pkg/detectors/abuseipdb/000077500000000000000000000000001451332102400204535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/abuseipdb/abuseipdb.go000066400000000000000000000047071451332102400227500ustar00rootroot00000000000000package abuseipdb import ( "context" "io" "net/http" // "log" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"abuseipdb"}) + `\b([a-z0-9]{80})\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{"abuseipdb"} } // FromData will find and optionally verify AbuseIPDB 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AbuseIPDB, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.abuseipdb.com/api/v2/check?ipAddress=118.25.6.39", nil) if err != nil { continue } req.Header.Add("Key", resMatch) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `ipAddress`) // errCode := strings.Contains(bodyString, `AbuseIPDB APIv2 Server.`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AbuseIPDB } trufflehog-3.60.0/pkg/detectors/abuseipdb/abuseipdb_test.go000066400000000000000000000055041451332102400240030ustar00rootroot00000000000000//go:build detectors // +build detectors package abuseipdb import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAbuseIPDB_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("ABUSEIPDB") inactiveSecret := testSecrets.MustGetField("ABUSEIPDB_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 abuseipdb secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AbuseIPDB, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a abuseipdb 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_AbuseIPDB, 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("AbuseIPDB.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AbuseIPDB.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) } } }) } } trufflehog-3.60.0/pkg/detectors/accuweather/000077500000000000000000000000001451332102400210105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/accuweather/accuweather.go000066400000000000000000000042131451332102400236320ustar00rootroot00000000000000package accuweather import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"accuweather"}) + `([a-z0-9A-Z\%]{35})\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{"accuweather"} } // FromData will find and optionally verify Accuweather 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Accuweather, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://dataservice.accuweather.com/locations/v1/cities/autocomplete?apikey="+resMatch+"&q=----&language=en-us", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Accuweather } trufflehog-3.60.0/pkg/detectors/accuweather/accuweather_test.go000066400000000000000000000055301451332102400246740ustar00rootroot00000000000000//go:build detectors // +build detectors package accuweather import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAccuweather_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("ACCUWEATHER") inactiveSecret := testSecrets.MustGetField("ACCUWEATHER_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 accuweather secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Accuweather, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a accuweather 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_Accuweather, 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("Accuweather.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Accuweather.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) } } }) } } trufflehog-3.60.0/pkg/detectors/adafruitio/000077500000000000000000000000001451332102400206445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/adafruitio/adafruitio.go000066400000000000000000000040531451332102400233240ustar00rootroot00000000000000package adafruitio import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(aio\_[a-zA-Z0-9]{28})\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{"aio_"} } // FromData will find and optionally verify AdafruitIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AdafruitIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://io.adafruit.com/api/v2/ladybugtest/feeds/?x-aio-key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AdafruitIO } trufflehog-3.60.0/pkg/detectors/adafruitio/adafruitio_test.go000066400000000000000000000055161451332102400243700ustar00rootroot00000000000000//go:build detectors // +build detectors package adafruitio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "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, 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) { s := Scanner{} got, err := 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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); 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) } } }) } } trufflehog-3.60.0/pkg/detectors/adobeio/000077500000000000000000000000001451332102400201175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/adobeio/adobeio.go000066400000000000000000000050451451332102400220540ustar00rootroot00000000000000package adobeio import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"adobe"}) + `\b([a-z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"adobe"}) + `\b([a-zA-Z0-9.]{12})\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{"adobe"} } // FromData will find and optionally verify AdobeIO 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AdobeIO, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://stock.adobe.io/Rest/Media/1/Search/Files?locale=en_US%2526search_parameters%255Bwords%255D=kittens", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) req.Header.Add("x-product", resIdMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AdobeIO } trufflehog-3.60.0/pkg/detectors/adobeio/adobeio_test.go000066400000000000000000000056071451332102400231170ustar00rootroot00000000000000//go:build detectors // +build detectors package adobeio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAdobeIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ADOBEIO_TOKEN") id := testSecrets.MustGetField("ADOBEIO_PRODUCT") inactiveSecret := testSecrets.MustGetField("ADOBEIO_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 adobeio secret %s within adobeio %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AdobeIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a adobeio secret %s within adobeio %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AdobeIO, 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("AdobeIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AdobeIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/adzuna/000077500000000000000000000000001451332102400177775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/adzuna/adzuna.go000066400000000000000000000047131451332102400216150ustar00rootroot00000000000000package adzuna import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"adzuna"}) + `\b([a-z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"adzuna"}) + `\b([a-z0-9]{8})\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{"adzuna"} } // FromData will find and optionally verify Adzuna 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Adzuna, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.adzuna.com/v1/api/jobs/gb/search/1?app_id=%s&app_key=%s", resIdMatch, resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Adzuna } trufflehog-3.60.0/pkg/detectors/adzuna/adzuna_test.go000066400000000000000000000055571451332102400226630ustar00rootroot00000000000000//go:build detectors // +build detectors package adzuna import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAdzuna_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("ADZUNA") id := testSecrets.MustGetField("ADZUNA_ID") inactiveSecret := testSecrets.MustGetField("ADZUNA_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 adzuna secret %s within adzuna %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Adzuna, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a adzuna secret %s within adzuna %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Adzuna, 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("Adzuna.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Adzuna.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) } } }) } } trufflehog-3.60.0/pkg/detectors/aeroworkflow/000077500000000000000000000000001451332102400212365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aeroworkflow/aeroworkflow.go000066400000000000000000000050301451332102400243040ustar00rootroot00000000000000package aeroworkflow import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aeroworkflow"}) + `\b([a-zA-Z0-9^!]{20})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aeroworkflow"}) + `\b([0-9]{1,})\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{"aeroworkflow"} } // FromData will find and optionally verify Aeroworkflow 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Aeroworkflow, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequest("GET", "https://api.aeroworkflow.com/api/"+resIdMatch+"/v1/AeroAppointments", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Aeroworkflow } trufflehog-3.60.0/pkg/detectors/aeroworkflow/aeroworkflow_test.go000066400000000000000000000056101451332102400253470ustar00rootroot00000000000000//go:build detectors // +build detectors package aeroworkflow import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAeroworkflow_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AEROWORKFLOW") id := testSecrets.MustGetField("AEROWORKFLOW_ID") inactiveSecret := testSecrets.MustGetField("AEROWORKFLOW_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 aeroworkflow secret %s within aeroworkflow %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Aeroworkflow, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aeroworkflow secret %s within aeroworkflow %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Aeroworkflow, 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("Aeroworkflow.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Aeroworkflow.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/agora/000077500000000000000000000000001451332102400176065ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/agora/agora.go000066400000000000000000000046661451332102400212420ustar00rootroot00000000000000package agora import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"agora"}) + `\b([a-z0-9]{32})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"agora"}) + `\b([a-z0-9]{32})\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{"agora"} } // FromData will find and optionally verify Agora 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secret := range secretMatches { if len(secret) != 2 { continue } resSecret := strings.TrimSpace(secret[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Agora, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.agora.io/dev/v1/projects", nil) if err != nil { continue } req.SetBasicAuth(resSecret, resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Agora } trufflehog-3.60.0/pkg/detectors/agora/agora_test.go000066400000000000000000000055601451332102400222730ustar00rootroot00000000000000//go:build detectors // +build detectors package agora import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAgora_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) } id := testSecrets.MustGetField("AGORA") secret := testSecrets.MustGetField("AGORA_SECRET") inactiveSecret := testSecrets.MustGetField("AGORA_SECRET_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 agora secret %s within agora id %s but verified", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Agora, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a agora secret %s within agora id %s but not valid ", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Agora, 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("Agora.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Agora.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) } } }) } } trufflehog-3.60.0/pkg/detectors/aha/000077500000000000000000000000001451332102400172465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aha/aha.go000066400000000000000000000042331451332102400203300ustar00rootroot00000000000000package aha import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aha"}) + `\b([0-9a-f]{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{"aha"} } // 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Aha, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://company.aha.io/api/v1/me", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.aha+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Aha } trufflehog-3.60.0/pkg/detectors/aha/aha_test.go000066400000000000000000000054111451332102400213660ustar00rootroot00000000000000//go:build detectors // +build detectors package aha import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAha_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AHA") inactiveSecret := testSecrets.MustGetField("AHA_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 aha secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Aha, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aha 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_Aha, 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("Aha.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Aha.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) } } }) } } trufflehog-3.60.0/pkg/detectors/airbrakeprojectkey/000077500000000000000000000000001451332102400223755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/airbrakeprojectkey/airbrakeprojectkey.go000066400000000000000000000053641451332102400266140ustar00rootroot00000000000000package airbrakeprojectkey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"airbrake"}) + `\b([a-zA-Z-0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"airbrake"}) + `\b([0-9]{6})\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{"airbrake"} } // FromData will find and optionally verify AirbrakeProjectKey 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AirbrakeProjectKey, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/airbrake/", } if verify { payload := strings.NewReader(`{"environment":"production","username":"john","email":"john@smith.com","repository":"https://github.com/airbrake/airbrake","revision":"38748467ea579e7ae64f7815452307c9d05e05c5","version":"v2.0"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.airbrake.io/api/v4/projects/"+resIdMatch+"/deploys?key="+resMatch, payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } if !s1.Verified { if detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AirbrakeProjectKey } trufflehog-3.60.0/pkg/detectors/airbrakeprojectkey/airbrakeprojectkey_test.go000066400000000000000000000057661451332102400276610ustar00rootroot00000000000000//go:build detectors // +build detectors package airbrakeprojectkey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAirbrakeProjectKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AIRBRAKEPROJECTKEY_TOKEN") id := testSecrets.MustGetField("AIRBRAKEPROJECTKEY_ID") inactiveSecret := testSecrets.MustGetField("AIRBRAKEPROJECTKEY_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 airbrake secret %s within airbrake %s but verified", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AirbrakeProjectKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a airbrake secret %s within airbrake %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AirbrakeProjectKey, 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("AirbrakeProjectKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AirbrakeProjectKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/airbrakeuserkey/000077500000000000000000000000001451332102400217055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/airbrakeuserkey/airbrakeuserkey.go000066400000000000000000000043161451332102400254300ustar00rootroot00000000000000package airbrakeuserkey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"airbrake"}) + `\b([a-zA-Z-0-9]{40})\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{"airbrake"} } // FromData will find and optionally verify AirbrakeUserKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AirbrakeUserKey, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/airbrake/", } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.airbrake.io/api/v4/projects?key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AirbrakeUserKey } trufflehog-3.60.0/pkg/detectors/airbrakeuserkey/airbrakeuserkey_test.go000066400000000000000000000056061451332102400264720ustar00rootroot00000000000000//go:build detectors // +build detectors package airbrakeuserkey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAirbrakeUserKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AIRBRAKEUSERKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("AIRBRAKEUSERKEY_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 airbrakeuserkey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AirbrakeUserKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a airbrakeuserkey 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_AirbrakeUserKey, 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("AirbrakeUserKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AirbrakeUserKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/airship/000077500000000000000000000000001451332102400201545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/airship/airship.go000066400000000000000000000043111451332102400221410ustar00rootroot00000000000000package airship import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"airship"}) + `\b([0-9Aa-zA-Z]{91})\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{"airship"} } // FromData will find and optionally verify Airship 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Airship, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://go.urbanairship.com/api/schedules", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.urbanairship+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Airship } trufflehog-3.60.0/pkg/detectors/airship/airship_test.go000066400000000000000000000054601451332102400232060ustar00rootroot00000000000000//go:build detectors // +build detectors package airship import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAirship_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("AIRSHIP") inactiveSecret := testSecrets.MustGetField("AIRSHIP_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 airship secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Airship, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a airship 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_Airship, 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("Airship.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Airship.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) } } }) } } trufflehog-3.60.0/pkg/detectors/airtableapikey/000077500000000000000000000000001451332102400215035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/airtableapikey/airtableapikey.go000066400000000000000000000060141451332102400250210ustar00rootroot00000000000000package airtableapikey import ( "context" "encoding/json" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() appPat = regexp.MustCompile(`(app[a-zA-Z0-9_-]{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 := appPat.FindAllStringSubmatch(dataStr, -1) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) personalKeyMatches := personalPat.FindAllStringSubmatch(dataStr, -1) if len(keyMatches) == 0 { keyMatches = personalKeyMatches } for _, keyMatch := range keyMatches { if len(keyMatch) != 2 { continue } keyRes := strings.TrimSpace(keyMatch[1]) for _, appMatch := range appMatches { if len(appMatch) != 2 { continue } appRes := strings.TrimSpace(appMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AirtableApiKey, Redacted: appRes, Raw: []byte(keyRes), RawV2: []byte(keyRes + appRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.airtable.com/v0/"+appRes+"/Projects", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", keyRes)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 403 { var resp response if err = json.NewDecoder(res.Body).Decode(&resp); err == nil { // 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. s1.Verified = true } } } else { if detectors.IsKnownFalsePositive(keyRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AirtableApiKey } trufflehog-3.60.0/pkg/detectors/airtableapikey/airtableapikey_test.go000066400000000000000000000061371451332102400260660ustar00rootroot00000000000000//go:build detectors // +build detectors package airtableapikey import ( "context" "fmt" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "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/pb/detectorspb" ) func TestAirtableApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AIRTABLEAPIKEY_KEY") inactiveSecret := testSecrets.MustGetField("AIRTABLEAPIKEY_KEY_INACTIVE") app := testSecrets.MustGetField("AIRTABLEAPIKEY_APP") 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 airtableapikey secret %s within airtable https://api.airtable.com/v0/%s/Projects", secret, app)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AirtableApiKey, Redacted: app, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a airtableapikey secret %s within airtable %s", inactiveSecret, app)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AirtableApiKey, Redacted: app, 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("AirtableApiKey.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]) } got[i].Raw = nil } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Airtable.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) } } }) } } trufflehog-3.60.0/pkg/detectors/airvisual/000077500000000000000000000000001451332102400205145ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/airvisual/airvisual.go000066400000000000000000000043521451332102400230460ustar00rootroot00000000000000package airvisual import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"airvisual"}) + `\b([a-z0-9-]{36})\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{"airvisual"} } // FromData will find and optionally verify AirVisual 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AirVisual, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.airvisual.com/v2/countries?key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.airvisual+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AirVisual } trufflehog-3.60.0/pkg/detectors/airvisual/airvisual_test.go000066400000000000000000000055041451332102400241050ustar00rootroot00000000000000//go:build detectors // +build detectors package airvisual import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAirVisual_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("AIRVISUAL") inactiveSecret := testSecrets.MustGetField("AIRVISUAL_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 airvisual secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AirVisual, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a airvisual 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_AirVisual, 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("AirVisual.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AirVisual.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) } } }) } } trufflehog-3.60.0/pkg/detectors/aiven/000077500000000000000000000000001451332102400176175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aiven/aiven.go000066400000000000000000000041471451332102400212560ustar00rootroot00000000000000package aiven import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aiven"}) + `([a-zA-Z0-9/+=]{372})`) ) // 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{"aiven"} } // FromData will find and optionally verify Aiven 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Aiven, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.aiven.io/v1/project", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("aivenv1 %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Aiven } trufflehog-3.60.0/pkg/detectors/aiven/aiven_test.go000066400000000000000000000054341451332102400223150ustar00rootroot00000000000000//go:build detectors // +build detectors package aiven import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAiven_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AIVEN") inactiveSecret := testSecrets.MustGetField("AIVEN_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 aiven secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Aiven, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aiven 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_Aiven, 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("Aiven.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Aiven.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) } } }) } } trufflehog-3.60.0/pkg/detectors/alchemy/000077500000000000000000000000001451332102400201375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/alchemy/alchemy.go000066400000000000000000000047041451332102400221150ustar00rootroot00000000000000package alchemy import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // 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. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alchemy"}) + `\b([0-9a-zA-Z]{23}_[0-9a-zA-Z]{8})\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{"alchemy"} } // FromData will find and optionally verify Alchemy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Alchemy, Raw: []byte(resMatch), } if verify { client := s.client if client == nil { client = defaultClient } req, err := http.NewRequestWithContext(ctx, "GET", "https://eth-mainnet.g.alchemy.com/v2/"+resMatch+"/getNFTs/?owner=vitalik.eth", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Alchemy } trufflehog-3.60.0/pkg/detectors/alchemy/alchemy_test.go000066400000000000000000000103651451332102400231540ustar00rootroot00000000000000//go:build detectors // +build detectors package alchemy import ( "context" "fmt" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAlchemy_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ALCHEMY") inactiveSecret := testSecrets.MustGetField("ALCHEMY_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alchemy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alchemy, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alchemy 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_Alchemy, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alchemy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alchemy, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alchemy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alchemy, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("Alchemy.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Alchemy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/alconost/000077500000000000000000000000001451332102400203375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/alconost/alconost.go000066400000000000000000000044501451332102400225130ustar00rootroot00000000000000package alconost import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alconost"}) + `\b([0-9Aa-z]{32})\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{"alconost"} } // FromData will find and optionally verify Alconost 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Alconost, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://nitro.alconost.com/api/v1/account", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Alconost } trufflehog-3.60.0/pkg/detectors/alconost/alconost_test.go000066400000000000000000000054721451332102400235570ustar00rootroot00000000000000//go:build detectors // +build detectors package alconost import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAlconost_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("ALCONOST") inactiveSecret := testSecrets.MustGetField("ALCONOST_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 alconost secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alconost, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alconost 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_Alconost, 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("Alconost.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Alconost.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) } } }) } } trufflehog-3.60.0/pkg/detectors/alegra/000077500000000000000000000000001451332102400177505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/alegra/alegra.go000066400000000000000000000047301451332102400215360ustar00rootroot00000000000000package alegra import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alegra"}) + `\b([a-z0-9-]{20})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alegra"}) + `\b([a-zA-Z0-9.-@]{25,30})\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{"alegra"} } // FromData will find and optionally verify Alegra 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Alegra, Raw: []byte(tokenPatMatch), RawV2: []byte(tokenPatMatch + userPatMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.alegra.com/api/v1/users", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Alegra } trufflehog-3.60.0/pkg/detectors/alegra/alegra_test.go000066400000000000000000000055621451332102400226010ustar00rootroot00000000000000//go:build detectors // +build detectors package alegra import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAlegra_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("ALEGRA") id := testSecrets.MustGetField("ACCOUNT_USER") inactiveSecret := testSecrets.MustGetField("ALEGRA_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 alegra secret %s within alegra %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alegra, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alegra secret %s within alegra %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alegra, 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("Alegra.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Alegra.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) } } }) } } trufflehog-3.60.0/pkg/detectors/aletheiaapi/000077500000000000000000000000001451332102400207635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aletheiaapi/aletheiaapi.go000066400000000000000000000042761451332102400235710ustar00rootroot00000000000000package aletheiaapi import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aletheiaapi"}) + `\b([A-Z0-9]{32})\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{"aletheiaapi"} } // FromData will find and optionally verify AletheiaApi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AletheiaApi, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://api.aletheiaapi.com/StockData?symbol=msft&summary=true", nil) if err != nil { continue } req.Header.Add("Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AletheiaApi } trufflehog-3.60.0/pkg/detectors/aletheiaapi/aletheiaapi_test.go000066400000000000000000000055301451332102400246220ustar00rootroot00000000000000//go:build detectors // +build detectors package aletheiaapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAletheiaApi_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("ALETHEIAAPI") inactiveSecret := testSecrets.MustGetField("ALETHEIAAPI_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 aletheiaapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AletheiaApi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aletheiaapi 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_AletheiaApi, 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("AletheiaApi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AletheiaApi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/algoliaadminkey/000077500000000000000000000000001451332102400216475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/algoliaadminkey/algoliaadminkey.go000066400000000000000000000050421451332102400253310ustar00rootroot00000000000000package algoliaadminkey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"algolia"}) + `\b([a-zA-Z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"algolia"}) + `\b([A-Z0-9]{10})\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{"algolia"} } // 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) { dataStr := string(data) matches := keyPat.FindAllStringSubmatch(dataStr, -1) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AlgoliaAdminKey, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://"+resIdMatch+"-dsn.algolia.net/1/keys", nil) if err != nil { continue } req.Header.Add("X-Algolia-Application-Id", resIdMatch) req.Header.Add("X-Algolia-API-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AlgoliaAdminKey } trufflehog-3.60.0/pkg/detectors/algoliaadminkey/algoliaadminkey_test.go000066400000000000000000000057151451332102400263770ustar00rootroot00000000000000//go:build detectors // +build detectors package algoliaadminkey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAlgoliaAdminKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ALGOLIAADMINKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("ALGOLIAADMINKEY_INACTIVE") id := testSecrets.MustGetField("ALGOLIAADMINKEY_APPID") 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 algolia secret %s within algolia %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AlgoliaAdminKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a algolia secret %s within algolia %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AlgoliaAdminKey, 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("AlgoliaAdminKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AlgoliaAdminKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/alibaba/000077500000000000000000000000001451332102400200705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/alibaba/alibaba.go000066400000000000000000000075731451332102400220060ustar00rootroot00000000000000package alibaba import ( "context" "crypto/hmac" "crypto/rand" "crypto/sha1" "encoding/base64" "net/http" "net/url" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b([a-zA-Z0-9]{30})\b`) idPat = regexp.MustCompile(`\b(LTAI[a-zA-Z0-9]{17,21})[\"';\s]*`) ) // 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{"LTAI"} } func randString(n int) string { const alphanum = "0123456789abcdefghijklmnopqrstuvwxyz" var bytes = make([]byte, n) _, _ = rand.Read(bytes) for i, b := range bytes { bytes[i] = alphanum[b%byte(len(alphanum))] } return string(bytes) } func GetSignature(input, key string) string { key_for_sign := []byte(key) h := hmac.New(sha1.New, key_for_sign) h.Write([]byte(input)) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } func buildStringToSign(method, input string) string { filter := strings.Replace(input, "+", "%20", -1) filter = strings.Replace(filter, "%7E", "~", -1) filter = strings.Replace(filter, "*", "%2A", -1) filter = method + "&%2F&" + url.QueryEscape(filter) return filter } // FromData will find and optionally verify Alibaba 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Alibaba, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "http://ecs.aliyuncs.com/?", nil) if err != nil { continue } dateISO := time.Now().UTC().Format("2006-01-02T15:04:05Z07:00") params := req.URL.Query() params.Add("AccessKeyId", resIdMatch) params.Add("Action", "DescribeRegions") params.Add("Format", "JSON") params.Add("SignatureMethod", "HMAC-SHA1") params.Add("SignatureNonce", randString(16)) params.Add("SignatureVersion", "1.0") params.Add("Timestamp", dateISO) params.Add("Version", "2014-05-26") stringToSign := buildStringToSign(req.Method, params.Encode()) signature := GetSignature(stringToSign, resMatch+"&") // Get Signature HMAC SHA1 params.Add("Signature", signature) req.URL.RawQuery = params.Encode() req.Header.Add("Content-Type", "text/xml;charset=utf-8") req.Header.Add("Content-Length", strconv.Itoa(len(params.Encode()))) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Alibaba } trufflehog-3.60.0/pkg/detectors/alibaba/alibaba_test.go000066400000000000000000000056411451332102400230370ustar00rootroot00000000000000//go:build detectors // +build detectors package alibaba import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAlibaba_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ALIBABA_SECRET") inactiveSecret := testSecrets.MustGetField("ALIBABA_SECRET_INACTIVE") id := testSecrets.MustGetField("ALIBABA_ID") 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 alibaba secret %s within alibaba %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alibaba, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alibaba secret %s within alibaba %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Alibaba, 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("Alibaba.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]) } got[i].Raw = nil got[i].RawV2 = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Alibaba.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) } } }) } } trufflehog-3.60.0/pkg/detectors/alienvault/000077500000000000000000000000001451332102400206615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/alienvault/alienvault.go000066400000000000000000000041531451332102400233570ustar00rootroot00000000000000package alienvault import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alienvault"}) + `\b([a-z0-9]{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{"alienvault"} } // FromData will find and optionally verify AlienVault 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AlienVault, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://otx.alienvault.com/api/v1/users/me", nil) if err != nil { continue } req.Header.Add("X-OTX-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AlienVault } trufflehog-3.60.0/pkg/detectors/alienvault/alienvault_test.go000066400000000000000000000055161451332102400244220ustar00rootroot00000000000000//go:build detectors // +build detectors package alienvault import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAlienVault_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("ALIENVAULT") inactiveSecret := testSecrets.MustGetField("ALIENVAULT_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 alienvault secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AlienVault, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a alienvault 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_AlienVault, 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("AlienVault.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AlienVault.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) } } }) } } trufflehog-3.60.0/pkg/detectors/allsports/000077500000000000000000000000001451332102400205405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/allsports/allsports.go000066400000000000000000000040661451332102400231200ustar00rootroot00000000000000package allsports import ( "context" "io" "net/http" // "log" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"allsports"}) + `\b([0-9a-z]{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{"allsports"} } // FromData will find and optionally verify Allsports 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Allsports, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://apiv2.allsportsapi.com/football/?met=Countries&APIkey="+resMatch, nil) if err != nil { continue } 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, "success") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Allsports } trufflehog-3.60.0/pkg/detectors/allsports/allsports_test.go000066400000000000000000000055041451332102400241550ustar00rootroot00000000000000//go:build detectors // +build detectors package allsports import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAllsports_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("ALLSPORTS") inactiveSecret := testSecrets.MustGetField("ALLSPORTS_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 allsports secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Allsports, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a allsports 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_Allsports, 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("Allsports.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Allsports.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) } } }) } } trufflehog-3.60.0/pkg/detectors/amadeus/000077500000000000000000000000001451332102400201345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/amadeus/amadeus.go000066400000000000000000000054531451332102400221110ustar00rootroot00000000000000package amadeus import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"amadeus"}) + `\b([0-9A-Za-z]{32})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"amadeus"}) + `\b([0-9A-Za-z]{16})\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{"amadeus"} } // FromData will find and optionally verify Amadeus 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecret := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Amadeus, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { payload := strings.NewReader("grant_type=client_credentials&client_id=" + resMatch + "&client_secret=" + resSecret) req, err := http.NewRequestWithContext(ctx, "POST", "https://test.api.amadeus.com/v1/security/oauth2/token", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 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 (res.StatusCode >= 200 && res.StatusCode < 300) && strings.Contains(body, "access_token") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Amadeus } trufflehog-3.60.0/pkg/detectors/amadeus/amadeus_test.go000066400000000000000000000056061451332102400231500ustar00rootroot00000000000000//go:build detectors // +build detectors package amadeus import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAmadeus_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) } id := testSecrets.MustGetField("AMADEUS") secret := testSecrets.MustGetField("AMADEUS_SECRET") inactiveSecret := testSecrets.MustGetField("AMADEUS_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 amadeus secret %s within amadeus id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Amadeus, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a amadeus secret %s within amadeus id %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Amadeus, 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("Amadeus.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Amadeus.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ambee/000077500000000000000000000000001451332102400175665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ambee/ambee.go000066400000000000000000000042161451332102400211710ustar00rootroot00000000000000package ambee import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ambee"}) + `\b([0-9a-f]{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{"ambee"} } // FromData will find and optionally verify Ambee 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Ambee, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ambeedata.com/latest/by-lat-lng?lat=12&lng=77", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ambee } trufflehog-3.60.0/pkg/detectors/ambee/ambee_test.go000066400000000000000000000054341451332102400222330ustar00rootroot00000000000000//go:build detectors // +build detectors package ambee import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAmbee_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("AMBEE") inactiveSecret := testSecrets.MustGetField("AMBEE_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 ambee secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Ambee, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ambee 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_Ambee, 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("Ambee.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ambee.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) } } }) } } trufflehog-3.60.0/pkg/detectors/amplitudeapikey/000077500000000000000000000000001451332102400217045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/amplitudeapikey/amplitudeapikey.go000066400000000000000000000050171451332102400254250ustar00rootroot00000000000000package amplitudeapikey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"amplitude"}) + `\b([0-9a-f]{32})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"amplitude"}) + `\b([0-9a-f]{32})\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{"amplitude"} } // FromData will find and optionally verify AmplitudeApiKey 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AmplitudeApiKey, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecretMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://amplitude.com/api/2/taxonomy/category", nil) if err != nil { continue } req.SetBasicAuth(resMatch, resSecretMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AmplitudeApiKey } trufflehog-3.60.0/pkg/detectors/amplitudeapikey/amplitudeapikey_test.go000066400000000000000000000060571451332102400264710ustar00rootroot00000000000000//go:build detectors // +build detectors package amplitudeapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAmplitudeApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } key := testSecrets.MustGetField("AMPLITUDEAPI_KEY") secret := testSecrets.MustGetField("AMPLITUDEAPI_SECRET") inactiveKey := testSecrets.MustGetField("AMPLITUDEAPI_KEY_INACTIVE") inactiveSecret := testSecrets.MustGetField("AMPLITUDEAPI_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 an amplitude key %s with amplitude secret %s within", key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AmplitudeApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find an amplitude key %s with amplitude secret %s within but not valid", inactiveKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AmplitudeApiKey, 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("AmplitudeApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AmplitudeApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/anthropic/000077500000000000000000000000001451332102400205045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/anthropic/anthropic.go000066400000000000000000000064151451332102400230300ustar00rootroot00000000000000package anthropic import ( "bytes" "context" "encoding/json" "fmt" "net/http" "regexp" "strings" "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 } // 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. keyPat = regexp.MustCompile(`\b(sk-ant-api03-[\w\-]{93}AA)\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{"sk-ant-api03"} } type response struct { Error struct { Type string `json:"type"` Message string `json:"message"` } `json:"error"` } // FromData will find and optionally verify Anthropic 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Anthropic, Raw: []byte(resMatch), } if verify { client := s.client if client == nil { client = defaultClient } body := map[string]interface{}{ "prompt": "test", } bodyBytes, err := json.Marshal(body) if err != nil { continue } req, err := http.NewRequestWithContext(ctx, "POST", "https://api.anthropic.com/v1/complete", bytes.NewReader(bodyBytes)) if err != nil { continue } req.Header.Set("x-api-key", resMatch) req.Header.Set("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode == http.StatusBadRequest { var resp response if err = json.NewDecoder(res.Body).Decode(&resp); err != nil { s1.VerificationError = fmt.Errorf("unexpected HTTP response body: %w", err) continue } if resp.Error.Message == "max_tokens_to_sample: field required" { // The secret is verified // Anthropic returns 400 on a request containing a valid API key, // when not containing a valid model field s1.Verified = true } } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) // Anthropic returns 401 on all requests not containing a valid x-api-key header } else { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Anthropic } trufflehog-3.60.0/pkg/detectors/anthropic/anthropic_test.go000066400000000000000000000104211451332102400240570ustar00rootroot00000000000000//go:build detectors // +build detectors package anthropic import ( "context" "fmt" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAnthropic_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ANTHROPIC") inactiveSecret := testSecrets.MustGetField("ANTHROPIC_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a anthropic secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Anthropic, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a anthropic 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_Anthropic, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a anthropic secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Anthropic, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a anthropic secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Anthropic, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("Anthropic.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Anthropic.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) } } }) } } trufflehog-3.60.0/pkg/detectors/anypoint/000077500000000000000000000000001451332102400203565ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/anypoint/anypoint.go000066400000000000000000000052271451332102400225540ustar00rootroot00000000000000package anypoint import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\b`) orgPat = regexp.MustCompile(detectors.PrefixRegex([]string{"org"}) + `\b([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"anypoint"} } // 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) matches := keyPat.FindAllStringSubmatch(dataStr, -1) orgMatches := orgPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, orgMatch := range orgMatches { if len(orgMatch) != 2 { continue } orgRes := strings.TrimSpace(orgMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Anypoint, Raw: []byte(resMatch), RawV2: []byte(resMatch + orgRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://anypoint.mulesoft.com/apiplatform/repository/v2/organizations/%s/apis/by-name?apiName=%s", orgRes, ""), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Anypoint } trufflehog-3.60.0/pkg/detectors/anypoint/anypoint_test.go000066400000000000000000000057111451332102400236110ustar00rootroot00000000000000//go:build detectors // +build detectors package anypoint import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAnypoint_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ANYPOINT_TOKEN") inactiveSecret := testSecrets.MustGetField("ANYPOINT_INACTIVE") organizationId := testSecrets.MustGetField("ANYPOINT_ORGANIZATIONID") 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 organization %s", secret, organizationId)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Anypoint, 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 organization %s but not valid", inactiveSecret, organizationId)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Anypoint, 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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Anypoint.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apacta/000077500000000000000000000000001451332102400177465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apacta/apacta.go000066400000000000000000000041201451332102400215230ustar00rootroot00000000000000package apacta import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apacta"}) + `\b([a-z0-9-]{36})\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{"apacta"} } // FromData will find and optionally verify Apacta 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Apacta, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://app.apacta.com/api/v1/time_entries?api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Apacta } trufflehog-3.60.0/pkg/detectors/apacta/apacta_test.go000066400000000000000000000054461451332102400225760ustar00rootroot00000000000000//go:build detectors // +build detectors package apacta import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApacta_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APACTA") inactiveSecret := testSecrets.MustGetField("APACTA_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 apacta secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apacta, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apacta 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_Apacta, 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("Apacta.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apacta.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) } } }) } } trufflehog-3.60.0/pkg/detectors/api2cart/000077500000000000000000000000001451332102400202225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/api2cart/api2cart.go000066400000000000000000000046771451332102400222740ustar00rootroot00000000000000package api2cart import ( "context" "encoding/json" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"api2cart"}) + `\b([0-9a-f]{32})\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{"api2cart"} } // FromData will find and optionally verify Api2Cart 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Api2Cart, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.api2cart.com/v1.1/account.cart.list.json?api_key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Accept", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() body, errBody := io.ReadAll(res.Body) var result Response if errBody == nil { if err := json.Unmarshal(body, &result); err != nil { continue } if res.StatusCode >= 200 && res.StatusCode < 300 && result.ReturnCode == 0 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } type Response struct { ReturnCode int `json:"return_code"` } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Api2Cart } trufflehog-3.60.0/pkg/detectors/api2cart/api2cart_test.go000066400000000000000000000054721451332102400233250ustar00rootroot00000000000000//go:build detectors // +build detectors package api2cart import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApi2Cart_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("API2CART") inactiveSecret := testSecrets.MustGetField("API2CART_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 api2cart secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Api2Cart, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a api2cart 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_Api2Cart, 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("Api2Cart.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Api2Cart.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apideck/000077500000000000000000000000001451332102400201155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apideck/apideck.go000066400000000000000000000047651451332102400220600ustar00rootroot00000000000000package apideck import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(sk_live_[a-z0-9A-Z-]{93})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apideck"}) + `\b([a-z0-9A-Z]{40})\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{"apideck"} } // FromData will find and optionally verify ApiDeck 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idMatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ApiDeck, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://unify.apideck.com/vault/consumers", nil) if err != nil { continue } req.Header.Add("x-apideck-app-id", resIdMatch) req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ApiDeck } trufflehog-3.60.0/pkg/detectors/apideck/apideck_test.go000066400000000000000000000056111451332102400231060ustar00rootroot00000000000000//go:build detectors // +build detectors package apideck import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApiDeck_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APIDECK_TOKEN") inactiveSecret := testSecrets.MustGetField("APIDECK_INACTIVE") id := testSecrets.MustGetField("APIDECK_APPID") 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 apideck secret %s within apideckid %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ApiDeck, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apideck secret %s within apideckid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ApiDeck, 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("ApiDeck.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ApiDeck.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apiflash/000077500000000000000000000000001451332102400203045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apiflash/apiflash.go000066400000000000000000000047431451332102400224320ustar00rootroot00000000000000package apiflash import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apiflash"}) + `\b([a-z0-9]{32})\b`) urlPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apiflash"}) + `\b([a-zA-Z0-9\S]{21,30})\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{"apiflash"} } // FromData will find and optionally verify Apiflash 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, urlMatch := range urlMatches { if len(urlMatch) != 2 { continue } resUrlMatch := strings.TrimSpace(urlMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Apiflash, Raw: []byte(resMatch), RawV2: []byte(resMatch + resUrlMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.apiflash.com/v1/urltoimage?url=%s&access_key=%s", resUrlMatch, resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Apiflash } trufflehog-3.60.0/pkg/detectors/apiflash/apiflash_test.go000066400000000000000000000056101451332102400234630ustar00rootroot00000000000000//go:build detectors // +build detectors package apiflash import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApiflash_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("APIFLASH") url := testSecrets.MustGetField("API_URL") inactiveSecret := testSecrets.MustGetField("APIFLASH_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 apiflash secret %s within apiflash %s", secret, url)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apiflash, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apiflash secret %s within apiflash %s but not valid", inactiveSecret, url)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apiflash, 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("Apiflash.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apiflash.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apifonica/000077500000000000000000000000001451332102400204465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apifonica/apifonica.go000066400000000000000000000051131451332102400227260ustar00rootroot00000000000000package apifonica import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apifonica"}) + `\b([0-9a-z]{11}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"apifonica"} } // FromData will find and optionally verify Apifonica 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) tokenMatches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, tokenMatch := range tokenMatches { if len(tokenMatch) != 2 { continue } resToken := strings.TrimSpace(tokenMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ApiFonica, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:%s", resMatch, resToken) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.apifonica.com/v2/accounts", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ApiFonica } trufflehog-3.60.0/pkg/detectors/apifonica/apifonica_test.go000066400000000000000000000056621451332102400237760ustar00rootroot00000000000000//go:build detectors // +build detectors package apifonica import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApifonica_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APIFONICA_SECRET") token := testSecrets.MustGetField("APIFONICA_ID") inactiveSecret := testSecrets.MustGetField("APIFONICA_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 apifonica secret %s within apifonica token %s", secret, token)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ApiFonica, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apifonica secret %s within apifonica token %s but not valid", inactiveSecret, token)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ApiFonica, 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("Apifonica.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apifonica.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apify/000077500000000000000000000000001451332102400176255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apify/apify.go000066400000000000000000000040601451332102400212640ustar00rootroot00000000000000package apify import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(apify\_api\_[a-zA-Z-0-9]{36})\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{"apify"} } // FromData will find and optionally verify Apify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Apify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.apify.com/v2/acts?token="+resMatch+"&my=true&offset=10&limit=99&desc=true", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Apify } trufflehog-3.60.0/pkg/detectors/apify/apify_test.go000066400000000000000000000054341451332102400223310ustar00rootroot00000000000000//go:build detectors // +build detectors package apify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApify_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("APIFY") inactiveSecret := testSecrets.MustGetField("APIFY_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 apify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apify 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_Apify, 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("Apify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apilayer/000077500000000000000000000000001451332102400203235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apilayer/apilayer.go000066400000000000000000000041441451332102400224630ustar00rootroot00000000000000package apilayer import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apilayer"}) + `\b([a-zA-Z0-9]{32})\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{"apilayer"} } // FromData will find and optionally verify Apilayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Apilayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.apilayer.com/number_verification/countries", nil) if err != nil { continue } req.Header.Add("apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Apilayer } trufflehog-3.60.0/pkg/detectors/apilayer/apilayer_test.go000066400000000000000000000054201451332102400235200ustar00rootroot00000000000000package apilayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApilayer_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APILAYER") inactiveSecret := testSecrets.MustGetField("APILAYER_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 apilayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apilayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apilayer 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_Apilayer, 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("Apilayer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apilayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apimatic/000077500000000000000000000000001451332102400203045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apimatic/apimatic.go000066400000000000000000000051311451332102400224220ustar00rootroot00000000000000package apimatic import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apimatic"}) + `\b([a-zA-Z0-9]{3,20}@[a-zA-Z0-9]{2,12}.[a-zA-Z0-9]{2,5})\b`) passPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apimatic"}) + `\b([a-z0-9-\S]{8,32})\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{"apimatic"} } // FromData will find and optionally verify APIMatic 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) passMatches := passPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } userPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range passMatches { if len(idMatch) != 2 { continue } passPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_APIMatic, Raw: []byte(userPatMatch), RawV2: []byte(userPatMatch + passPatMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://www.apimatic.io/api/code-generations", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, passPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(passPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_APIMatic } trufflehog-3.60.0/pkg/detectors/apimatic/apimatic_test.go000066400000000000000000000056331451332102400234700ustar00rootroot00000000000000//go:build detectors // +build detectors package apimatic import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAPIMatic_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APIMATIC") pass := testSecrets.MustGetField("APIMATIC_PASS") inactiveSecret := testSecrets.MustGetField("APIMATIC_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 apimatic secret %s within apimatic pass %s", secret, pass)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_APIMatic, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apimatic secret %s within apimatic pass %s but not valid", inactiveSecret, pass)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_APIMatic, 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("APIMatic.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("APIMatic.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apiscience/000077500000000000000000000000001451332102400206205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apiscience/apiscience.go000066400000000000000000000042151451332102400232540ustar00rootroot00000000000000package apiscience import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apiscience"}) + `\b([a-bA-Z0-9\S]{22})\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{"apiscience"} } // FromData will find and optionally verify ApiScience 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ApiScience, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.apiscience.com/v1/monitors", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ApiScience } trufflehog-3.60.0/pkg/detectors/apiscience/apiscience_test.go000066400000000000000000000055161451332102400243200ustar00rootroot00000000000000//go:build detectors // +build detectors package apiscience import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApiScience_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APISCIENCE") inactiveSecret := testSecrets.MustGetField("APISCIENCE_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 apiscience secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ApiScience, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apiscience 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_ApiScience, 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("ApiScience.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ApiScience.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apitemplate/000077500000000000000000000000001451332102400210225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apitemplate/apitemplate.go000066400000000000000000000042501451332102400236570ustar00rootroot00000000000000package apitemplate import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apitemplate"}) + `\b([0-9a-zA-Z]{39})\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{"apitemplate"} } // FromData will find and optionally verify APITemplate 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_APITemplate, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.apitemplate.io/v1/list-templates", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_APITemplate } trufflehog-3.60.0/pkg/detectors/apitemplate/apitemplate_test.go000066400000000000000000000055301451332102400247200ustar00rootroot00000000000000//go:build detectors // +build detectors package apitemplate import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAPITemplate_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APITEMPLATE") inactiveSecret := testSecrets.MustGetField("APITEMPLATE_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 apitemplate secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_APITemplate, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apitemplate 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_APITemplate, 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("APITemplate.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("APITemplate.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apollo/000077500000000000000000000000001451332102400200035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apollo/apollo.go000066400000000000000000000041171451332102400216230ustar00rootroot00000000000000package apollo import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apollo"}) + `\b([a-zA-Z0-9]{22})\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{"apollo"} } // FromData will find and optionally verify Apollo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Apollo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.apollo.io/v1/account_stages?api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Apollo } trufflehog-3.60.0/pkg/detectors/apollo/apollo_test.go000066400000000000000000000054461451332102400226700ustar00rootroot00000000000000//go:build detectors // +build detectors package apollo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApollo_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("APOLLO") inactiveSecret := testSecrets.MustGetField("APOLLO_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 apollo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apollo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apollo 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_Apollo, 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("Apollo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apollo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/appcues/000077500000000000000000000000001451332102400201555ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/appcues/appcues.go000066400000000000000000000054561451332102400221560ustar00rootroot00000000000000package appcues import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"appcues"}) + `\b([a-z0-9-]{36})\b`) userPat = regexp.MustCompile(detectors.PrefixRegex([]string{"appcues"}) + `\b([a-z0-9-]{39})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"appcues"}) + `\b([0-9]{5})\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{"appcues"} } // FromData will find and optionally verify Appcues 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) userMatches := userPat.FindAllStringSubmatch(dataStr, -1) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, userMatch := range userMatches { if len(userMatch) != 2 { continue } resUserMatch := strings.TrimSpace(userMatch[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Appcues, Raw: []byte(resMatch), 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 } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Appcues } trufflehog-3.60.0/pkg/detectors/appcues/appcues_test.go000066400000000000000000000057271451332102400232160ustar00rootroot00000000000000//go:build detectors // +build detectors package appcues import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAppcues_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("APPCUES") user := testSecrets.MustGetField("APPCUES_USER") id := testSecrets.MustGetField("APPCUES_ID") inactiveSecret := testSecrets.MustGetField("APPCUES_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 appcues secret %s within appcues user %s and appcues id %s", secret, user, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Appcues, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a appcues secret %s within appcues user %s and appcues id %s but not valid", inactiveSecret, user, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Appcues, 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("Appcues.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Appcues.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) } } }) } } trufflehog-3.60.0/pkg/detectors/appfollow/000077500000000000000000000000001451332102400205205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/appfollow/appfollow.go000066400000000000000000000043561451332102400230620ustar00rootroot00000000000000package appfollow import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"appfollow"}) + `\b([0-9A-Za-z]{20})\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{"appfollow"} } // FromData will find and optionally verify Appfollow 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Appfollow, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.appfollow.io/test", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Appfollow } trufflehog-3.60.0/pkg/detectors/appfollow/appfollow_test.go000066400000000000000000000055041451332102400241150ustar00rootroot00000000000000//go:build detectors // +build detectors package appfollow import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAppfollow_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APPFOLLOW") inactiveSecret := testSecrets.MustGetField("APPFOLLOW_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 appfollow secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Appfollow, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a appfollow 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_Appfollow, 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("Appfollow.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Appfollow.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) } } }) } } trufflehog-3.60.0/pkg/detectors/appointedd/000077500000000000000000000000001451332102400206445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/appointedd/appointedd.go000066400000000000000000000041021451332102400233170ustar00rootroot00000000000000package appointedd import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"appointedd"}) + `\b([a-zA-Z0-9=+]{88})`) ) // 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{"appointedd"} } // FromData will find and optionally verify appointedd 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Appointedd, 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 } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Appointedd } trufflehog-3.60.0/pkg/detectors/appointedd/appointedd_test.go000066400000000000000000000054441451332102400243700ustar00rootroot00000000000000package appointedd import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAppointedd_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APPOINTEDD") inactiveSecret := testSecrets.MustGetField("APPOINTEDD_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 appointedd secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Appointedd, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a appointedd 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_Appointedd, 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("Appointedd.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Appointedd.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) } } }) } } trufflehog-3.60.0/pkg/detectors/appsynergy/000077500000000000000000000000001451332102400207165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/appsynergy/appsynergy.go000066400000000000000000000043501451332102400234500ustar00rootroot00000000000000package appsynergy import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"appsynergy"}) + `\b([a-z0-9]{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{"appsynergy"} } // FromData will find and optionally verify AppSynergy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AppSynergy, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"html":"

Hello World

","filename":"HelloWorld.pdf"}`) req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("https://www.appsynergy.com/api?action=HTML2PDF&apiKey=%s", resMatch), payload) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AppSynergy } trufflehog-3.60.0/pkg/detectors/appsynergy/appsynergy_test.go000066400000000000000000000055161451332102400245140ustar00rootroot00000000000000//go:build detectors // +build detectors package appsynergy import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAppSynergy_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APPSYNERGY") inactiveSecret := testSecrets.MustGetField("APPSYNERGY_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 appsynergy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AppSynergy, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a appsynergy 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_AppSynergy, 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("AppSynergy.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AppSynergy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/apptivo/000077500000000000000000000000001451332102400201775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/apptivo/apptivo.go000066400000000000000000000053641451332102400222200ustar00rootroot00000000000000package apptivo import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apptivo"}) + `\b([a-z0-9-]{36})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"apptivo"}) + `\b([a-zA-Z0-9-]{32})\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{"apptivo"} } // FromData will find and optionally verify Apptivo 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Apptivo, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.apptivo.com/app/dao/v6/leads?a=getConfigData&apiKey=%s&accessKey=%s", resMatch, resIdMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `displayName`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Apptivo } trufflehog-3.60.0/pkg/detectors/apptivo/apptivo_test.go000066400000000000000000000055751451332102400232630ustar00rootroot00000000000000//go:build detectors // +build detectors package apptivo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestApptivo_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("APPTIVO") id := testSecrets.MustGetField("APPTIVO_KEY") inactiveSecret := testSecrets.MustGetField("APPTIVO_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 apptivo secret %s within apptivo %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apptivo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a apptivo secret %s within but not valid apptivo %s", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Apptivo, 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("Apptivo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Apptivo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/artifactory/000077500000000000000000000000001451332102400210445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/artifactory/artifactory.go000066400000000000000000000047011451332102400237240ustar00rootroot00000000000000package artifactory import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b([a-zA-Z0-9]{73})`) URLPat = regexp.MustCompile(`\b([A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])\.jfrog\.io)`) ) // 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{"artifactory"} } // FromData will find and optionally verify Artifactory 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) URLmatches := URLPat.FindAllStringSubmatch(dataStr, -1) matches := keyPat.FindAllStringSubmatch(dataStr, -1) resURLMatch := "" for _, URLmatch := range URLmatches { if len(URLmatch) != 2 { continue } resURLMatch = strings.TrimSpace(URLmatch[1]) } for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ArtifactoryAccessToken, Raw: []byte(resMatch), RawV2: []byte(resMatch + resURLMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://"+resURLMatch+"/artifactory/api/storageinfo", nil) if err != nil { continue } req.Header.Add("X-JFrog-Art-Api", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ArtifactoryAccessToken } trufflehog-3.60.0/pkg/detectors/artifactory/artifactory_test.go000066400000000000000000000057121451332102400247660ustar00rootroot00000000000000//go:build detectors // +build detectors package artifactory import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestArtifactory_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ARTIFACTORY_TOKEN") inactiveSecret := testSecrets.MustGetField("ARTIFACTORY_INACTIVE") appURL := testSecrets.MustGetField("ARTIFACTORY_URL") 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 artifactory secret %s and domain %s", secret, appURL)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ArtifactoryAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a artifactory secret %s but not valid on endpoint %s", inactiveSecret, appURL)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ArtifactoryAccessToken, 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("Artifactory.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Artifactory.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) } } }) } } trufflehog-3.60.0/pkg/detectors/artsy/000077500000000000000000000000001451332102400176575ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/artsy/artsy.go000066400000000000000000000046761451332102400213650ustar00rootroot00000000000000package artsy import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"artsy"}) + `\b([0-9a-zA-Z]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"artsy"}) + `\b([0-9a-zA-Z]{20})\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{"artsy"} } // FromData will find and optionally verify Artsy 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Artsy, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "POST", "https://api.artsy.net/api/tokens/xapp_token?client_id="+resIdMatch+"&client_secret="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Artsy } trufflehog-3.60.0/pkg/detectors/artsy/artsy_test.go000066400000000000000000000055541451332102400224200ustar00rootroot00000000000000//go:build detectors // +build detectors package artsy import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestArtsy_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("ARTSY") inactiveSecret := testSecrets.MustGetField("ARTSY_INACTIVE") id := testSecrets.MustGetField("ARTSY_CLIENTID") 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 artsy secret %s within artsyid %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Artsy, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a artsy secret %s within artsyid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Artsy, 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("Artsy.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Artsy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/asanaoauth/000077500000000000000000000000001451332102400206415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/asanaoauth/asanaoauth.go000066400000000000000000000042011451332102400233110ustar00rootroot00000000000000package asanaoauth import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"asana"}) + `\b([a-z\/:0-9]{51})\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{"asana"} } // FromData will find and optionally verify AsanaOauth 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AsanaOauth, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.asana.com/api/1.0/users/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AsanaOauth } trufflehog-3.60.0/pkg/detectors/asanaoauth/asanaoauth_test.go000066400000000000000000000055121451332102400243560ustar00rootroot00000000000000//go:build detectors // +build detectors package asanaoauth import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAsanaOauth_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ASANAOAUTH_TOKEN") inactiveSecret := testSecrets.MustGetField("ASANAOAUTH_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 asana secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AsanaOauth, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a asana 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_AsanaOauth, 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("AsanaOauth.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AsanaOauth.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) } } }) } } trufflehog-3.60.0/pkg/detectors/asanapersonalaccesstoken/000077500000000000000000000000001451332102400235675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/asanapersonalaccesstoken/asanapersonalaccesstoken.go000066400000000000000000000037201451332102400311720ustar00rootroot00000000000000package asanapersonalaccesstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"asana"}) + `\b([0-9]{1,}\/[0-9]{16,}:[A-Za-z0-9]{32,})\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{"asana"} } // FromData will find and optionally verify AsanaPersonalAccessToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AsanaPersonalAccessToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.asana.com/api/1.0/users/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AsanaPersonalAccessToken } trufflehog-3.60.0/pkg/detectors/asanapersonalaccesstoken/asanapersonalaccesstoken_test.go000066400000000000000000000055311451332102400322330ustar00rootroot00000000000000//go:build detectors // +build detectors package asanapersonalaccesstoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAsanaPersonalAccessToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ASANA_PAT") inactiveSecret := testSecrets.MustGetField("ASANA_PAT_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 asana secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AsanaPersonalAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a asana secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AsanaPersonalAccessToken, 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("AsanaPersonalAccessToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AsanaPersonalAccessToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/assemblyai/000077500000000000000000000000001451332102400206465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/assemblyai/assemblyai.go000066400000000000000000000042371451332102400233340ustar00rootroot00000000000000package assemblyai import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"assemblyai"}) + `\b([0-9a-z]{32})\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{"assemblyai"} } // FromData will find and optionally verify Assemblyai 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AssemblyAI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.assemblyai.com/v2/transcript", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AssemblyAI } trufflehog-3.60.0/pkg/detectors/assemblyai/assemblyai_test.go000066400000000000000000000055161451332102400243740ustar00rootroot00000000000000//go:build detectors // +build detectors package assemblyai import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAssemblyai_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("ASSEMBLYAI") inactiveSecret := testSecrets.MustGetField("ASSEMBLYAI_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 assemblyai secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AssemblyAI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a assemblyai 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_AssemblyAI, 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("Assemblyai.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Assemblyai.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) } } }) } } trufflehog-3.60.0/pkg/detectors/atera/000077500000000000000000000000001451332102400176115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/atera/atera.go000066400000000000000000000041631451332102400212400ustar00rootroot00000000000000package atera import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"atera"}) + `\b([[0-9a-z]{32})\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{"atera"} } // FromData will find and optionally verify Atera 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Atera, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.atera.com/api/v3/alerts", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Atera } trufflehog-3.60.0/pkg/detectors/atera/atera_test.go000066400000000000000000000054361451332102400223030ustar00rootroot00000000000000//go:build detectors // +build detectors package atera import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAtera_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ATERA") inactiveSecret := testSecrets.MustGetField("ATERA_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 an atera secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Atera, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find an atera 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_Atera, 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("Atera.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Atera.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) } } }) } } trufflehog-3.60.0/pkg/detectors/audd/000077500000000000000000000000001451332102400174325ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/audd/audd.go000066400000000000000000000045741451332102400207100ustar00rootroot00000000000000package audd import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"audd"}) + `\b([a-z0-9-]{32})\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{"audd"} } // FromData will find and optionally verify Audd 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Audd, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.audd.io/setCallbackUrl/?api_token=%s&url=https://yourwebsite.com/callbacks_handler/", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"status":"success"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Audd } trufflehog-3.60.0/pkg/detectors/audd/audd_test.go000066400000000000000000000054221451332102400217400ustar00rootroot00000000000000//go:build detectors // +build detectors package audd import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAudd_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("AUDD") inactiveSecret := testSecrets.MustGetField("AUDD_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 audd secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Audd, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a audd 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_Audd, 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("Audd.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Audd.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) } } }) } } trufflehog-3.60.0/pkg/detectors/auth0managementapitoken/000077500000000000000000000000001451332102400233265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/auth0managementapitoken/auth0managementapitoken.go000066400000000000000000000056631451332102400305000ustar00rootroot00000000000000package auth0managementapitoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // long jwt token but note this is default 8640000 seconds = 24 hours but could be set to maximum 2592000 seconds = 720 hours = 30 days // at https://manage.auth0.com/dashboard/us/dev-63memjo3/apis/management/explorer managementApiTokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"auth0"}) + `\b(ey[a-zA-Z0-9._-]+)\b`) domainPat = regexp.MustCompile(`([a-zA-Z0-9\-]{2,16}\.[a-zA-Z0-9_-]{2,3}\.auth0\.com)`) // could be part of url ) // 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{"auth0"} } // FromData will find and optionally verify Auth0ManagementApiToken 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) managementApiTokenMatches := managementApiTokenPat.FindAllStringSubmatch(dataStr, -1) domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) for _, managementApiTokenMatch := range managementApiTokenMatches { if len(managementApiTokenMatch) != 2 { continue } managementApiTokenRes := strings.TrimSpace(managementApiTokenMatch[1]) if len(managementApiTokenRes) < 2000 || len(managementApiTokenRes) > 5000 { continue } for _, domainMatch := range domainMatches { if len(domainMatch) != 2 { continue } domainRes := strings.TrimSpace(domainMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Auth0ManagementApiToken, Redacted: domainRes, Raw: []byte(managementApiTokenRes), RawV2: []byte(managementApiTokenRes + domainRes), } if verify { /* curl -H "Authorization: Bearer $token" https://domain/api/v2/users */ req, err := http.NewRequestWithContext(ctx, "GET", "https://"+domainRes+"/api/v2/users", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", managementApiTokenRes)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(managementApiTokenRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Auth0ManagementApiToken } trufflehog-3.60.0/pkg/detectors/auth0managementapitoken/auth0managementapitoken_test.go000066400000000000000000000063001451332102400315240ustar00rootroot00000000000000//go:build detectors // +build detectors package auth0managementapitoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAuth0ManagementApiToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } // use 2592000 for 30 days, this is the maximum allowed managementApiToken := testSecrets.MustGetField("AUTH0_MANAGEMENT_APITOKEN") inactiveManagementApiToken := testSecrets.MustGetField("AUTH0_MANAGEMENT_APITOKEN_INACTIVE") domain := testSecrets.MustGetField("AUTH0_MANAGEMENT_DOMAIN") 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 auth0 secret %s domain %s", managementApiToken, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Auth0ManagementApiToken, Redacted: domain, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a auth0 secret %s domain https://%s/oauth/token within but not valid", inactiveManagementApiToken, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Auth0ManagementApiToken, Redacted: domain, 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("Auth0ManagementApiToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Auth0ManagementApiToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/auth0oauth/000077500000000000000000000000001451332102400205775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/auth0oauth/auth0oauth.go000066400000000000000000000073231451332102400232150ustar00rootroot00000000000000package auth0oauth import ( "context" "io" "net/http" "net/url" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() clientIdPat = regexp.MustCompile(detectors.PrefixRegex([]string{"auth0"}) + `\b([a-zA-Z0-9_-]{32,60})\b`) clientSecretPat = regexp.MustCompile(`\b([a-zA-Z0-9_-]{64,})\b`) domainPat = regexp.MustCompile(`\b([a-zA-Z0-9][a-zA-Z0-9._-]*auth0\.com)\b`) // could be part of url ) // 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{"auth0"} } // FromData will find and optionally verify Auth0oauth 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) clientIdMatches := clientIdPat.FindAllStringSubmatch(dataStr, -1) clientSecretMatches := clientSecretPat.FindAllStringSubmatch(dataStr, -1) domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) for _, clientIdMatch := range clientIdMatches { if len(clientIdMatch) != 2 { continue } clientIdRes := strings.TrimSpace(clientIdMatch[1]) for _, clientSecretMatch := range clientSecretMatches { if len(clientSecretMatch) != 2 { continue } clientSecretRes := strings.TrimSpace(clientSecretMatch[1]) for _, domainMatch := range domainMatches { if len(domainMatch) != 2 { continue } domainRes := strings.TrimSpace(domainMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Auth0oauth, Redacted: clientIdRes, Raw: []byte(clientSecretRes), RawV2: []byte(clientIdRes + clientSecretRes), } if verify { /* curl --request POST \ --url 'https://YOUR_DOMAIN/oauth/token' \ --header 'content-type: application/x-www-form-urlencoded' \ --data 'grant_type=authorization_code&client_id=W44JmL3qD6LxHeEJyKe9lMuhcwvPOaOq&client_secret=YOUR_CLIENT_SECRET&code=AUTHORIZATION_CODE&redirect_uri=undefined' */ data := url.Values{} data.Set("grant_type", "authorization_code") data.Set("client_id", clientIdRes) data.Set("client_secret", clientSecretRes) data.Set("code", "AUTHORIZATION_CODE") data.Set("redirect_uri", "undefined") req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://"+domainRes+"/oauth/token", strings.NewReader(data.Encode())) // URL-encoded payload if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 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 client_id and client_secret is valid -> 403 {"error":"invalid_grant","error_description":"Invalid authorization code"} // if invalid -> 401 {"error":"access_denied","error_description":"Unauthorized"} // ingenious! if !strings.Contains(body, "access_denied") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(clientIdRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Auth0oauth } trufflehog-3.60.0/pkg/detectors/auth0oauth/auth0oauth_test.go000066400000000000000000000063061451332102400242540ustar00rootroot00000000000000//go:build detectors // +build detectors package auth0oauth import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAuth0oauth_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } domain := testSecrets.MustGetField("AUTH0_DOMAIN") clientId := testSecrets.MustGetField("AUTH0_CLIENT_ID") // there is AUTH0_CLIENT_ID2 and AUTH0_CLIENT_SECRET2 pair as well clientSecret := testSecrets.MustGetField("AUTH0_CLIENT_SECRET") inactiveClientSecret := testSecrets.MustGetField("AUTH0_CLIENT_SECRET_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 auth0 client id %s client secret %s domain %s", clientId, clientSecret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Auth0oauth, Redacted: clientId, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a auth0 client id %s client secret %s domain https://%s/oauth/token within but not valid", clientId, inactiveClientSecret, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Auth0oauth, Redacted: clientId, 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("Auth0oauth.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Auth0oauth.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) } } }) } } trufflehog-3.60.0/pkg/detectors/autodesk/000077500000000000000000000000001451332102400203345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/autodesk/autodesk.go000066400000000000000000000052461451332102400225110ustar00rootroot00000000000000package autodesk import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"autodesk"}) + `\b([0-9A-Za-z]{32})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"autodesk"}) + `\b([0-9A-Za-z]{16})\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{"autodesk"} } // FromData will find and optionally verify Autodesk 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecret := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Autodesk, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { payload := strings.NewReader(fmt.Sprintf(`grant_type=client_credentials&client_id=%s&client_secret=%s`, resMatch, resSecret)) req, err := http.NewRequestWithContext(ctx, "POST", "https://developer.api.autodesk.com/authentication/v1/authenticate", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Autodesk } trufflehog-3.60.0/pkg/detectors/autodesk/autodesk_test.go000066400000000000000000000056221451332102400235460ustar00rootroot00000000000000//go:build detectors // +build detectors package autodesk import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAutodesk_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } id := testSecrets.MustGetField("AUTODESK_ID") secret := testSecrets.MustGetField("AUTODESK_SECRET") inactiveID := testSecrets.MustGetField("AUTODESK_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 autodesk secret %s within autodesk id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Autodesk, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a autodesk secret %s within autodesk id %s but not valid", secret, inactiveID)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Autodesk, 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("Autodesk.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Autodesk.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) } } }) } } trufflehog-3.60.0/pkg/detectors/autoklose/000077500000000000000000000000001451332102400205235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/autoklose/autoklose.go000066400000000000000000000044371451332102400230700ustar00rootroot00000000000000package autoklose import ( "context" "encoding/json" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"autoklose"}) + `\b([a-zA-Z0-9-]{32})\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{"autoklose"} } // FromData will find and optionally verify Autoklose 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Autoklose, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.autoklose.com/api/campaigns/?api_token=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if json.Valid(bodyBytes) { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Autoklose } trufflehog-3.60.0/pkg/detectors/autoklose/autoklose_test.go000066400000000000000000000054631451332102400241270ustar00rootroot00000000000000//go:build detectors // +build detectors package autoklose import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAutoklose_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("AUTOKLOSE") inactiveSecret := testSecrets.MustGetField("AUTOKLOSE_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 autoklose secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Autoklose, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a autoklose 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_Autoklose, 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("Autoklose.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Autoklose.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) } } }) } } trufflehog-3.60.0/pkg/detectors/autopilot/000077500000000000000000000000001451332102400205355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/autopilot/autopilot.go000066400000000000000000000041441451332102400231070ustar00rootroot00000000000000package autopilot import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"autopilot"}) + `\b([0-9a-f]{32})\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{"autopilot"} } // FromData will find and optionally verify AutoPilot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AutoPilot, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api2.autopilothq.com/v1/account", nil) if err != nil { continue } req.Header.Add("autopilotapikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AutoPilot } trufflehog-3.60.0/pkg/detectors/autopilot/autopilot_test.go000066400000000000000000000055041451332102400241470ustar00rootroot00000000000000//go:build detectors // +build detectors package autopilot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAutoPilot_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("AUTOPILOT") inactiveSecret := testSecrets.MustGetField("AUTOPILOT_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 autopilot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AutoPilot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a autopilot 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_AutoPilot, 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("AutoPilot.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AutoPilot.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) } } }) } } trufflehog-3.60.0/pkg/detectors/avazapersonalaccesstoken/000077500000000000000000000000001451332102400236065ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/avazapersonalaccesstoken/avazapersonalaccesstoken.go000066400000000000000000000043621451332102400312330ustar00rootroot00000000000000package avazapersonalaccesstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // The number prefix increments for every Personal Access Token created. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"avaza"}) + `\b([0-9]+-[0-9a-f]{40})\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{"avaza"} } // FromData will find and optionally verify AvazaPersonalAccessToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AvazaPersonalAccessToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", "https://api.avaza.com/api/Account", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AvazaPersonalAccessToken } trufflehog-3.60.0/pkg/detectors/avazapersonalaccesstoken/avazapersonalaccesstoken_test.go000066400000000000000000000055761451332102400323020ustar00rootroot00000000000000//go:build detectors // +build detectors package avazapersonalaccesstoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAvazaPersonalAccessToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AVAZAPERSONALACCESSTOKEN") inactiveSecret := testSecrets.MustGetField("AVAZAPERSONALACCESSTOKEN_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 avaza secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AvazaPersonalAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a avaza 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_AvazaPersonalAccessToken, 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("AvazaPersonalAccessToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AvazaPersonalAccessToken.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/aviationstack/000077500000000000000000000000001451332102400213555ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aviationstack/aviationstack.go000066400000000000000000000042751451332102400245540ustar00rootroot00000000000000package aviationstack import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aviationstack"}) + `\b([a-z0-9]{32})\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{"aviationstack"} } // FromData will find and optionally verify AviationStack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AviationStack, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.aviationstack.com/v1/flights?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AviationStack } trufflehog-3.60.0/pkg/detectors/aviationstack/aviationstack_test.go000066400000000000000000000055541451332102400256140ustar00rootroot00000000000000//go:build detectors // +build detectors package aviationstack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAviationStack_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("AVIATIONSTACK") inactiveSecret := testSecrets.MustGetField("AVIATIONSTACK_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 aviationstack secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AviationStack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aviationstack 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_AviationStack, 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("AviationStack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AviationStack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/aws/000077500000000000000000000000001451332102400173075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aws/aws.go000066400000000000000000000244321451332102400204350ustar00rootroot00000000000000package aws import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net/http" "regexp" "strings" "time" "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 { verificationClient *http.Client skipIDs map[string]struct{} } func New(opts ...func(*scanner)) *scanner { scanner := &scanner{ skipIDs: map[string]struct{}{}, } for _, opt := range opts { opt(scanner) } return scanner } func WithSkipIDs(skipIDs []string) func(*scanner) { return func(s *scanner) { ids := map[string]struct{}{} for _, id := range skipIDs { ids[id] = struct{}{} } s.skipIDs = ids } } // Ensure the scanner satisfies the interface at compile time. var _ detectors.Detector = (*scanner)(nil) var ( defaultVerificationClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // Key types are from this list https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids idPat = regexp.MustCompile(`\b((?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16})\b`) secretPat = regexp.MustCompile(`[^A-Za-z0-9+\/]{0,1}([A-Za-z0-9+\/]{40})[^A-Za-z0-9+\/]{0,1}`) // Hashes, like those for git, do technically match the secret pattern. // But they are extremely unlikely to be generated as an actual AWS secret. // So when we find them, if they're not verified, we should ignore the result. falsePositiveSecretCheck = regexp.MustCompile(`[a-f0-9]{40}`) ) // 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{ "AKIA", "ABIA", "ACCA", "ASIA", } } func GetHash(input string) string { data := []byte(input) hasher := sha256.New() hasher.Write(data) return hex.EncodeToString(hasher.Sum(nil)) } func GetHMAC(key []byte, data []byte) []byte { hasher := hmac.New(sha256.New, key) hasher.Write(data) return hasher.Sum(nil) } // FromData will find and optionally verify AWS 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIDMatch := strings.TrimSpace(idMatch[1]) if s.skipIDs != nil { if _, ok := s.skipIDs[resIDMatch]; ok { continue } } for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AWS, Raw: []byte(resIDMatch), Redacted: resIDMatch, RawV2: []byte(resIDMatch + resSecretMatch), } if verify { verified, extraData, verificationErr := s.verifyMatch(ctx, resIDMatch, resSecretMatch, true) s1.Verified = verified s1.ExtraData = extraData s1.VerificationError = verificationErr } if !s1.Verified { // Unverified results that contain common test words are probably not secrets if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } // Unverified results that look like hashes are probably not secrets if falsePositiveSecretCheck.MatchString(resSecretMatch) { continue } } results = append(results, s1) // If we've found a verified match with this ID, we don't need to look for any more. So move on to the next ID. if s1.Verified { break } } } return awsCustomCleanResults(results), nil } func (s scanner) verifyMatch(ctx context.Context, resIDMatch, resSecretMatch string, retryOn403 bool) (bool, map[string]string, error) { // REQUEST VALUES. method := "GET" service := "sts" host := "sts.amazonaws.com" region := "us-east-1" endpoint := "https://sts.amazonaws.com" now := time.Now().UTC() datestamp := now.Format("20060102") amzDate := now.Format("20060102T150405Z0700") req, err := http.NewRequestWithContext(ctx, method, endpoint, nil) if err != nil { return false, nil, err } req.Header.Set("Accept", "application/json") // TASK 1: CREATE A CANONICAL REQUEST. // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html canonicalURI := "/" canonicalHeaders := "host:" + host + "\n" signedHeaders := "host" algorithm := "AWS4-HMAC-SHA256" credentialScope := fmt.Sprintf("%s/%s/%s/aws4_request", datestamp, region, service) params := req.URL.Query() params.Add("Action", "GetCallerIdentity") params.Add("Version", "2011-06-15") params.Add("X-Amz-Algorithm", algorithm) params.Add("X-Amz-Credential", resIDMatch+"/"+credentialScope) params.Add("X-Amz-Date", amzDate) params.Add("X-Amz-Expires", "30") params.Add("X-Amz-SignedHeaders", signedHeaders) canonicalQuerystring := params.Encode() payloadHash := GetHash("") // empty payload canonicalRequest := method + "\n" + canonicalURI + "\n" + canonicalQuerystring + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + payloadHash // TASK 2: CREATE THE STRING TO SIGN. stringToSign := algorithm + "\n" + amzDate + "\n" + credentialScope + "\n" + GetHash(canonicalRequest) // TASK 3: CALCULATE THE SIGNATURE. // https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html hash := GetHMAC([]byte(fmt.Sprintf("AWS4%s", resSecretMatch)), []byte(datestamp)) hash = GetHMAC(hash, []byte(region)) hash = GetHMAC(hash, []byte(service)) hash = GetHMAC(hash, []byte("aws4_request")) signature2 := GetHMAC(hash, []byte(stringToSign)) // Get Signature HMAC SHA256 signature := hex.EncodeToString(signature2) // TASK 4: ADD SIGNING INFORMATION TO THE REQUEST. params.Add("X-Amz-Signature", signature) req.Header.Add("Content-type", "application/x-www-form-urlencoded; charset=utf-8") req.URL.RawQuery = params.Encode() client := s.verificationClient if client == nil { client = defaultVerificationClient } extraData := map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/aws/", } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { identityInfo := identityRes{} err := json.NewDecoder(res.Body).Decode(&identityInfo) if err == nil { extraData["account"] = identityInfo.GetCallerIdentityResponse.GetCallerIdentityResult.Account extraData["user_id"] = identityInfo.GetCallerIdentityResponse.GetCallerIdentityResult.UserID extraData["arn"] = identityInfo.GetCallerIdentityResponse.GetCallerIdentityResult.Arn return true, extraData, nil } else { return false, nil, err } } else if res.StatusCode == 403 { // Experimentation has indicated that if you make two GetCallerIdentity requests within five seconds that // share a key ID but are signed with different secrets the second one will be rejected with a 403 that // carries a SignatureDoesNotMatch code in its body. This happens even if the second ID-secret pair is // valid. Since this is exactly our access pattern, we need to work around it. // // Fortunately, experimentation has also revealed a workaround: simply resubmit the second request. The // response to the resubmission will be as expected. But there's a caveat: You can't have closed the body of // the response to the original second request, or read to its end, or the resubmission will also yield a // SignatureDoesNotMatch. For this reason, we have to re-request all 403s. We can't re-request only // SignatureDoesNotMatch responses, because we can only tell whether a given 403 is a SignatureDoesNotMatch // after decoding its response body, which requires reading the entire response body, which disables the // workaround. // // We are clearly deep in the guts of AWS implementation details here, so this all might change with no // notice. If you're here because something in this detector broke, you have my condolences. if retryOn403 { return s.verifyMatch(ctx, resIDMatch, resSecretMatch, false) } var body awsErrorResponseBody err = json.NewDecoder(res.Body).Decode(&body) if err == nil { // All instances of the code I've seen in the wild are PascalCased but this check is // case-insensitive out of an abundance of caution if strings.EqualFold(body.Error.Code, "InvalidClientTokenId") { return false, nil, nil } else { return false, nil, fmt.Errorf("request to %v returned status %d with an unexpected reason (%s: %s)", res.Request.URL, res.StatusCode, body.Error.Code, body.Error.Message) } } else { return false, nil, fmt.Errorf("couldn't parse the sts response body (%v)", err) } } else { return false, nil, fmt.Errorf("request to %v returned unexpected status %d", res.Request.URL, res.StatusCode) } } else { return false, nil, err } } func awsCustomCleanResults(results []detectors.Result) []detectors.Result { if len(results) == 0 { return results } // For every ID, we want at most one result, preferably verified. idResults := map[string]detectors.Result{} for _, result := range results { // Always accept the verified result as the result for the given ID. if result.Verified { idResults[result.Redacted] = result continue } // Only include an unverified result if we don't already have a result for a given ID. if _, exist := idResults[result.Redacted]; !exist { idResults[result.Redacted] = result } } out := []detectors.Result{} for _, r := range idResults { out = append(out, r) } return out } type awsError struct { Code string `json:"Code"` Message string `json:"Message"` } type awsErrorResponseBody struct { Error awsError `json:"Error"` } type identityRes struct { GetCallerIdentityResponse struct { GetCallerIdentityResult struct { Account string `json:"Account"` Arn string `json:"Arn"` UserID string `json:"UserId"` } `json:"GetCallerIdentityResult"` ResponseMetadata struct { RequestID string `json:"RequestId"` } `json:"ResponseMetadata"` } `json:"GetCallerIdentityResponse"` } func (s scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AWS } trufflehog-3.60.0/pkg/detectors/aws/aws_test.go000066400000000000000000000223621451332102400214740ustar00rootroot00000000000000//go:build detectors // +build detectors package aws import ( "context" "crypto/sha256" "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" ) var unverifiedSecretClient = common.ConstantResponseHttpClient(403, `{"Error": {"Code": "InvalidClientTokenId"} }`) func TestAWS_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AWS") id := testSecrets.MustGetField("AWS_ID") inactiveSecret := testSecrets.MustGetField("AWS_INACTIVE") inactiveID := id[:len(id)-3] + "XYZ" hasher := sha256.New() hasher.Write([]byte(inactiveSecret)) hash := string(hasher.Sum(nil)) type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s scanner args args want []detectors.Result wantErr bool wantVerificationError bool }{ { name: "found, verified", s: scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: true, Redacted: "AKIASP2TPHJSQH3FJRUX", ExtraData: map[string]string{ "account": "171436882533", "arn": "arn:aws:iam::171436882533:user/canarytokens.com@@4dxkh0pdeop3bzu9zx5wob793", "user_id": "AIDASP2TPHJSUFRSTTZX4", }, }, }, wantErr: false, }, { name: "found, unverified", s: scanner{verificationClient: unverifiedSecretClient}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: "AKIASP2TPHJSQH3FJRUX", ExtraData: nil, }, }, 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: "found two, one included for every ID found", s: scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("The verified ID is %s with a secret of %s, but the unverified ID is %s and this is the secret %s", id, secret, inactiveID, inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: "AKIASP2TPHJSQH3FJXYZ", }, { DetectorType: detectorspb.DetectorType_AWS, Verified: true, Redacted: "AKIASP2TPHJSQH3FJRUX", ExtraData: map[string]string{ "account": "171436882533", "arn": "arn:aws:iam::171436882533:user/canarytokens.com@@4dxkh0pdeop3bzu9zx5wob793", "user_id": "AIDASP2TPHJSUFRSTTZX4", }, }, }, wantErr: false, }, { name: "not found, because unverified secret was a hash", s: scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", hash, id)), // The secret would satisfy the regex but be filtered out after not passing validation. verify: true, }, want: nil, wantErr: false, }, { name: "found two, returned both because the active secret for one paired with the inactive ID, despite the hash", s: scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("The verified ID is %s with a secret of %s, but the unverified ID is %s and the secret is this hash %s", id, secret, inactiveID, hash)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: true, Redacted: "AKIASP2TPHJSQH3FJRUX", ExtraData: map[string]string{ "account": "171436882533", "arn": "arn:aws:iam::171436882533:user/canarytokens.com@@4dxkh0pdeop3bzu9zx5wob793", "user_id": "AIDASP2TPHJSUFRSTTZX4", }, }, { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: inactiveID, }, }, wantErr: false, }, { name: "found, unverified, with leading +", s: scanner{verificationClient: unverifiedSecretClient}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", "+HaNv9cTwheDKGJaws/+BMF2GgybQgBWdhcOOdfF", id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: "AKIASP2TPHJSQH3FJRUX", }, }, wantErr: false, }, { name: "skipped", s: scanner{ skipIDs: map[string]struct{}{ "AKIASP2TPHJSQH3FJRUX": {}, }, }, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s but not valid", "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", id)), // the secret would satisfy the regex but not pass validation verify: true, }, wantErr: false, }, { name: "found, would be verified if not for http timeout", s: scanner{verificationClient: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: "AKIASP2TPHJSQH3FJRUX", }, }, wantErr: false, wantVerificationError: true, }, { name: "found, unverified due to unexpected http response status", s: scanner{verificationClient: common.ConstantResponseHttpClient(500, "internal server error")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: "AKIASP2TPHJSQH3FJRUX", }, }, wantErr: false, wantVerificationError: true, }, { name: "found, unverified due to unexpected 403 response reason", s: scanner{verificationClient: common.ConstantResponseHttpClient(403, `{"Error": {"Code": "SignatureDoesNotMatch"} }`)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aws secret %s within aws %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: false, Redacted: "AKIASP2TPHJSQH3FJRUX", }, }, wantErr: false, wantVerificationError: true, }, { name: "verified secret checked directly after unverified secret with same key id", s: scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("%s\n%s\n%s", inactiveSecret, id, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AWS, Verified: true, Redacted: "AKIASP2TPHJSQH3FJRUX", ExtraData: map[string]string{ "account": "171436882533", "arn": "arn:aws:iam::171436882533:user/canarytokens.com@@4dxkh0pdeop3bzu9zx5wob793", "user_id": "AIDASP2TPHJSUFRSTTZX4", }, }, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := tt.s got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("AWS.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationError { t.Fatalf("wantVerificationError %v, verification error = %v", tt.wantVerificationError, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("AWS.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) } } }) } } trufflehog-3.60.0/pkg/detectors/axonaut/000077500000000000000000000000001451332102400201745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/axonaut/axonaut.go000066400000000000000000000041411451332102400222020ustar00rootroot00000000000000package axonaut import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"axonaut"}) + `\b([a-z0-9]{32})\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{"axonaut"} } // FromData will find and optionally verify Axonaut 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Axonaut, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://axonaut.com/api/v2/companies?type=all&sort=id", nil) if err != nil { continue } req.Header.Add("userApiKey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Axonaut } trufflehog-3.60.0/pkg/detectors/axonaut/axonaut_test.go000066400000000000000000000054601451332102400232460ustar00rootroot00000000000000//go:build detectors // +build detectors package axonaut import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAxonaut_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("AXONAUT") inactiveSecret := testSecrets.MustGetField("AXONAUT_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 axonaut secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Axonaut, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a axonaut 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_Axonaut, 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("Axonaut.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Axonaut.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) } } }) } } trufflehog-3.60.0/pkg/detectors/aylien/000077500000000000000000000000001451332102400177765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/aylien/aylien.go000066400000000000000000000050041451332102400216050ustar00rootroot00000000000000package aylien import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aylien"}) + `\b([a-z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"aylien"}) + `\b([a-z0-9]{8})\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{"aylien"} } // FromData will find and optionally verify Aylien 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Aylien, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.aylien.com/news/stories", nil) if err != nil { continue } req.Header.Add("X-AYLIEN-NewsAPI-Application-ID", resIdMatch) req.Header.Add("X-AYLIEN-NewsAPI-Application-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Aylien } trufflehog-3.60.0/pkg/detectors/aylien/aylien_test.go000066400000000000000000000055571451332102400226610ustar00rootroot00000000000000//go:build detectors // +build detectors package aylien import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAylien_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("AYLIEN") id := testSecrets.MustGetField("AYLIEN_ID") inactiveSecret := testSecrets.MustGetField("AYLIEN_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 aylien secret %s within aylien %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Aylien, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a aylien secret %s within aylien %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Aylien, 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("Aylien.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Aylien.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ayrshare/000077500000000000000000000000001451332102400203335ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ayrshare/ayrshare.go000066400000000000000000000042421451332102400225020ustar00rootroot00000000000000package ayrshare import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ayrshare"}) + `\b([A-Z]{7}-[A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7})\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{"ayrshare"} } // FromData will find and optionally verify Ayrshare 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Ayrshare, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.ayrshare.com/api/analytics/links", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ayrshare } trufflehog-3.60.0/pkg/detectors/ayrshare/ayrshare_test.go000066400000000000000000000055001451332102400235370ustar00rootroot00000000000000//go:build detectors // +build detectors package ayrshare import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAyrshare_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AYRSHARE_TOKEN") inactiveSecret := testSecrets.MustGetField("AYRSHARE_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 ayrshare secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Ayrshare, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ayrshare 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_Ayrshare, 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("Ayrshare.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ayrshare.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) } } }) } } trufflehog-3.60.0/pkg/detectors/azure/000077500000000000000000000000001451332102400176435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/azure/azure.go000066400000000000000000000054511451332102400213250ustar00rootroot00000000000000package azure import ( "context" "fmt" "regexp" "strings" "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) func mustFmtPat(id, pat string) *regexp.Regexp { combinedID := strings.ReplaceAll(id, "_", "") + "|" + id return regexp.MustCompile(fmt.Sprintf(pat, combinedID)) } var ( // TODO: Azure storage access keys and investigate other types of creds. // Azure App Oauth idPatFmt = `(?i)(%s).{0,20}([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})` clientIDPat = mustFmtPat("client_id", idPatFmt) tenantIDPat = mustFmtPat("tenant_id", idPatFmt) // TODO: support old patterns secretPatFmt = `(?i)(%s).{0,20}([a-z0-9_\.\-~]{34})` clientSecretPat = mustFmtPat("client_secret", secretPatFmt) ) // 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{"azure"} } // FromData will find and optionally verify Azure 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) clientSecretMatches := clientSecretPat.FindAllStringSubmatch(dataStr, -1) for _, clientSecret := range clientSecretMatches { tenantIDMatches := tenantIDPat.FindAllStringSubmatch(dataStr, -1) for _, tenantID := range tenantIDMatches { clientIDMatches := clientIDPat.FindAllStringSubmatch(dataStr, -1) for _, clientID := range clientIDMatches { s := detectors.Result{ DetectorType: detectorspb.DetectorType_Azure, Raw: []byte(clientSecret[2]), RawV2: []byte(clientID[2] + clientSecret[2] + tenantID[2]), Redacted: clientID[2], } // Set the RotationGuideURL in the ExtraData s.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/azure/", } if verify { cred := auth.NewClientCredentialsConfig(clientID[2], clientSecret[2], tenantID[2]) token, err := cred.ServicePrincipalToken() if err != nil { continue } err = token.Refresh() if err == nil { s.Verified = true } } if !s.Verified { if detectors.IsKnownFalsePositive(s.Redacted, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) { continue } } results = append(results, s) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Azure } trufflehog-3.60.0/pkg/detectors/azure/azure_test.go000066400000000000000000000050311451332102400223560ustar00rootroot00000000000000//go:build detectors // +build detectors package azure import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAzure_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AZURE_SECRET") secretInactive := testSecrets.MustGetField("AZURE_INACTIVE") id := testSecrets.MustGetField("AZURE_ID") tenantId := testSecrets.MustGetField("AZURE_TENANT_ID") 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(` tenant_id=%s client_id=%s client_secret=%s client_secret=%s `, tenantId, id, secretInactive, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Azure, Redacted: id, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf(` tenant_id=%s client_id=%s client_secret=%s `, tenantId, id, secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Azure, Redacted: id, 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("Azure.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Azure.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } trufflehog-3.60.0/pkg/detectors/azurestorage/000077500000000000000000000000001451332102400212305ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/azurestorage/azurestorage.go000066400000000000000000000047011451332102400242740ustar00rootroot00000000000000package azurestorage import ( "context" "crypto/hmac" "crypto/sha256" "encoding/base64" "fmt" "net/http" "regexp" "strings" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct { client *http.Client } var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = http.DefaultClient keyPat = regexp.MustCompile(`DefaultEndpointsProtocol=https;AccountName=(?P[^;]+);AccountKey=(?P[^;]+);EndpointSuffix=core\.windows\.net`) ) func (s Scanner) Keywords() []string { return []string{"DefaultEndpointsProtocol=https;AccountName="} } func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) matches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 3 { continue } accountName := strings.TrimSpace(match[1]) accountKey := strings.TrimSpace(match[2]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_AzureStorage, Raw: []byte(accountName), } if verify { client := s.client if client == nil { client = defaultClient } now := time.Now().UTC().Format(http.TimeFormat) stringToSign := "GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:" + now + "\nx-ms-version:2019-12-12\n/" + accountName + "/\ncomp:list" accountKeyBytes, _ := base64.StdEncoding.DecodeString(accountKey) h := hmac.New(sha256.New, accountKeyBytes) h.Write([]byte(stringToSign)) signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) url := "https://" + accountName + ".blob.core.windows.net/?comp=list" req, err := http.NewRequestWithContext(ctx, "GET", url, nil) req.Header.Set("x-ms-date", now) req.Header.Set("x-ms-version", "2019-12-12") req.Header.Set("Authorization", "SharedKey "+accountName+":"+signature) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 403 { } else { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_AzureStorage } trufflehog-3.60.0/pkg/detectors/azurestorage/azurestorage_test.go000066400000000000000000000104751451332102400253400ustar00rootroot00000000000000//go:build detectors // +build detectors package azurestorage 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 TestAzurestorage_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("AZURE_STORAGE") inactiveSecret := testSecrets.MustGetField("AZURE_STORAGE_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a azurestorage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AzureStorage, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a azurestorage 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_AzureStorage, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a azurestorage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AzureStorage, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a azurestorage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_AzureStorage, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("Azuretorage.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Azurestorage.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) } } }) } } trufflehog-3.60.0/pkg/detectors/badlist.txt000066400000000000000000000035261451332102400207060ustar00rootroot00000000000000value from array uint boolean config /> = 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bannerbear } trufflehog-3.60.0/pkg/detectors/bannerbear/bannerbear_test.go000066400000000000000000000055161451332102400243100ustar00rootroot00000000000000//go:build detectors // +build detectors package bannerbear import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBannerbear_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("BANNERBEAR") inactiveSecret := testSecrets.MustGetField("BANNERBEAR_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 bannerbear secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bannerbear, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bannerbear 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_Bannerbear, 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("Bannerbear.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bannerbear.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) } } }) } } trufflehog-3.60.0/pkg/detectors/baremetrics/000077500000000000000000000000001451332102400210155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/baremetrics/baremetrics.go000066400000000000000000000042221451332102400236440ustar00rootroot00000000000000package baremetrics import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"baremetrics"}) + `\b([a-zA-Z0-9_]{25})\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{"baremetrics"} } // FromData will find and optionally verify Baremetrics 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Baremetrics, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.baremetrics.com/v1/account", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Baremetrics } trufflehog-3.60.0/pkg/detectors/baremetrics/baremetrics_test.go000066400000000000000000000055301451332102400247060ustar00rootroot00000000000000//go:build detectors // +build detectors package baremetrics import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBaremetrics_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("BAREMETRICS") inactiveSecret := testSecrets.MustGetField("BAREMETRICS_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 baremetrics secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Baremetrics, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a baremetrics 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_Baremetrics, 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("Baremetrics.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Baremetrics.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) } } }) } } trufflehog-3.60.0/pkg/detectors/baseapiio/000077500000000000000000000000001451332102400204515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/baseapiio/baseapiio.go000066400000000000000000000042511451332102400227360ustar00rootroot00000000000000package baseapiio import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"baseapi", "base-api"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"baseapi", "base-api"} } // FromData will find and optionally verify BaseApiIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BaseApiIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.base-api.io/v1/forms?page=1&per_page=10", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", 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, "items") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BaseApiIO } trufflehog-3.60.0/pkg/detectors/baseapiio/baseapiio_test.go000066400000000000000000000055041451332102400237770ustar00rootroot00000000000000//go:build detectors // +build detectors package baseapiio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBaseApiIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BASEAPIIO") inactiveSecret := testSecrets.MustGetField("BASEAPIIO_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 baseapiio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BaseApiIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a baseapiio 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_BaseApiIO, 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("BaseApiIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BaseApiIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/beamer/000077500000000000000000000000001451332102400177505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/beamer/beamer.go000066400000000000000000000041171451332102400215350ustar00rootroot00000000000000package beamer import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"beamer"}) + `\b([a-zA-Z0-9_+/]{45}=)`) ) // 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{"beamer"} } // FromData will find and optionally verify Beamer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Beamer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getbeamer.com/v0/url", nil) if err != nil { continue } req.Header.Add("Beamer-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Beamer } trufflehog-3.60.0/pkg/detectors/beamer/beamer_test.go000066400000000000000000000054541451332102400226010ustar00rootroot00000000000000//go:build detectors // +build detectors package beamer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBeamer_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BEAMER_TOKEN") inactiveSecret := testSecrets.MustGetField("BEAMER_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 beamer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Beamer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a beamer 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_Beamer, 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("Beamer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Beamer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/beebole/000077500000000000000000000000001451332102400201125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/beebole/beebole.go000066400000000000000000000045411451332102400220420ustar00rootroot00000000000000package beebole import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"beebole"}) + `\b([0-9a-z]{40})\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{"beebole"} } // FromData will find and optionally verify Beebole 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Beebole, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:X", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) payload := strings.NewReader(`{"service": "custom_field.list"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://beebole-apps.com/api/v2", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Beebole } trufflehog-3.60.0/pkg/detectors/beebole/beebole_test.go000066400000000000000000000054601451332102400231020ustar00rootroot00000000000000//go:build detectors // +build detectors package beebole import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBeebole_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BEEBOLE") inactiveSecret := testSecrets.MustGetField("BEEBOLE_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 beebole secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Beebole, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a beebole 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_Beebole, 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("Beebole.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Beebole.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) } } }) } } trufflehog-3.60.0/pkg/detectors/besnappy/000077500000000000000000000000001451332102400203365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/besnappy/besnappy.go000066400000000000000000000043551451332102400225150ustar00rootroot00000000000000package besnappy import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"besnappy"}) + `\b([a-f0-9]{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{"besnappy"} } // FromData will find and optionally verify Besnappy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Besnappy, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:x", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://app.besnappy.com/api/v1/accounts", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Besnappy } trufflehog-3.60.0/pkg/detectors/besnappy/besnappy_test.go000066400000000000000000000054721451332102400235550ustar00rootroot00000000000000//go:build detectors // +build detectors package besnappy import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBesnappy_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BESNAPPY") inactiveSecret := testSecrets.MustGetField("BESNAPPY_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 besnappy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Besnappy, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a besnappy 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_Besnappy, 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("Besnappy.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Besnappy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/besttime/000077500000000000000000000000001451332102400203315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/besttime/besttime.go000066400000000000000000000040251451332102400224750ustar00rootroot00000000000000package besttime import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"besttime"}) + `\b([0-9A-Za-z_]{36})\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{"besttime"} } // FromData will find and optionally verify Besttime 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Besttime, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://besttime.app/api/v1/keys/"+resMatch, nil) if err != nil { continue } 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, `"status": "OK"`) { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Besttime } trufflehog-3.60.0/pkg/detectors/besttime/besttime_test.go000066400000000000000000000054721451332102400235430ustar00rootroot00000000000000//go:build detectors // +build detectors package besttime import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBesttime_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("BESTTIME") inactiveSecret := testSecrets.MustGetField("BESTTIME_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 besttime secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Besttime, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a besttime 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_Besttime, 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("Besttime.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Besttime.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) } } }) } } trufflehog-3.60.0/pkg/detectors/billomat/000077500000000000000000000000001451332102400203205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/billomat/billomat.go000066400000000000000000000050231451332102400224520ustar00rootroot00000000000000package billomat import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"billomat"}) + `\b([0-9a-z]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"billomat"}) + `\b([0-9a-z]{1,})\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{"billomat"} } // FromData will find and optionally verify Billomat 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resId := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Billomat, Raw: []byte(resMatch), RawV2: []byte(resMatch + resId), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.billomat.net/api/v2/clients/myself", resId), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-BillomatApiKey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Billomat } trufflehog-3.60.0/pkg/detectors/billomat/billomat_test.go000066400000000000000000000056171451332102400235220ustar00rootroot00000000000000//go:build detectors // +build detectors package billomat import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBillomat_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("BILLOMAT") id := testSecrets.MustGetField("BILLOMAT_ID") inactiveSecret := testSecrets.MustGetField("BILLOMAT_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 billomat secret %s within billomat id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Billomat, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a billomat secret %s within billomat id %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Billomat, 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("Billomat.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Billomat.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bitbar/000077500000000000000000000000001451332102400177605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bitbar/bitbar.go000066400000000000000000000044331451332102400215560ustar00rootroot00000000000000package bitbar import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitbar"}) + `\b([0-9a-z]{32})\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{"bitbar"} } // FromData will find and optionally verify Bitbar 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bitbar, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) baseToken := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.bitbar.com/api/me", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", baseToken)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bitbar } trufflehog-3.60.0/pkg/detectors/bitbar/bitbar_test.go000066400000000000000000000054461451332102400226220ustar00rootroot00000000000000//go:build detectors // +build detectors package bitbar import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBitbar_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("BITBAR") inactiveSecret := testSecrets.MustGetField("BITBAR_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 bitbar secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bitbar, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bitbar 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_Bitbar, 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("Bitbar.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bitbar.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bitcoinaverage/000077500000000000000000000000001451332102400214775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bitcoinaverage/bitcoinaverage.go000066400000000000000000000042131451332102400250100ustar00rootroot00000000000000package bitcoinaverage import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitcoinaverage"}) + `\b([a-zA-Z0-9]{43})\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{"bitcoinaverage"} } // FromData will find and optionally verify BitcoinAverage 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BitcoinAverage, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://apiv2.bitcoinaverage.com/websocket/get_ticket", nil) if err != nil { continue } req.Header.Add("x-ba-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BitcoinAverage } trufflehog-3.60.0/pkg/detectors/bitcoinaverage/bitcoinaverage_test.go000066400000000000000000000055661451332102400260630ustar00rootroot00000000000000//go:build detectors // +build detectors package bitcoinaverage import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBitcoinAverage_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("BITCOINAVERAGE") inactiveSecret := testSecrets.MustGetField("BITCOINAVERAGE_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 bitcoinaverage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BitcoinAverage, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bitcoinaverage 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_BitcoinAverage, 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("BitcoinAverage.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BitcoinAverage.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bitfinex/000077500000000000000000000000001451332102400203255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bitfinex/bitfinex.go000066400000000000000000000062501451332102400224670ustar00rootroot00000000000000package bitfinex import ( "context" "flag" "net/http" "regexp" "strings" "github.com/bitfinexcom/bitfinex-api-go/v2/rest" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // related resource https://medium.com/@Bitfinex/api-development-update-april-65fe52f84124 apiKeyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitfinex"}) + `\b([A-Za-z0-9_-]{43})\b`) apiSecretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitfinex"}) + `\b([A-Za-z0-9_-]{43})\b`) ) var ( api = flag.String("api", "https://api-pub.bitfinex.com/v2/", "v2 REST API URL") ) // 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{"bitfinex"} } // FromData will find and optionally verify Bitfinex 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) apiKeyMatches := apiKeyPat.FindAllStringSubmatch(dataStr, -1) apiSecretMatches := apiSecretPat.FindAllStringSubmatch(dataStr, -1) for _, apiKeyMatch := range apiKeyMatches { if len(apiKeyMatch) != 2 { continue } apiKeyRes := strings.TrimSpace(apiKeyMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bitfinex, Raw: []byte(apiKeyRes), } for _, apiSecretMatch := range apiSecretMatches { if len(apiSecretMatch) != 2 { continue } apiSecretRes := strings.TrimSpace(apiSecretMatch[1]) if apiKeyRes == apiSecretRes { continue } if verify { // thankfully official golang examples exist but you just need to dig their many repos https://github.com/bitfinexcom/bitfinex-api-go/blob/master/examples/v2/rest-orders/main.go key := apiKeyRes secret := apiSecretRes http.DefaultClient = client // filed https://github.com/bitfinexcom/bitfinex-api-go/issues/238 to improve this c := rest.NewClientWithURL(*api).Credentials(key, secret) isValid := true // assume valid _, err = c.Orders.AllHistory() if err != nil { if strings.HasPrefix(err.Error(), "POST https://") { // eg POST https://api-pub.bitfinex.com/v2/auth/r/orders/hist: 500 apikey: digest invalid (10100) isValid = false } else { if detectors.IsKnownFalsePositive(apiKeyRes, detectors.DefaultFalsePositives, true) { continue } } } s1.Verified = isValid // If there is a valid one, we need to stop iterating now and return the valid result if isValid { break } } } // By appending resutls in the outer loop we can reduce false positives if there are multiple // combinations of secrets and IDs found. if len(apiSecretMatches) > 0 { results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bitfinex } trufflehog-3.60.0/pkg/detectors/bitfinex/bitfinex_test.go000066400000000000000000000060201451332102400235210ustar00rootroot00000000000000//go:build detectors // +build detectors package bitfinex import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBitfinex_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } apiKey := testSecrets.MustGetField("BITFINEX_API_KEY") inactiveApiKey := testSecrets.MustGetField("BITFINEX_API_KEY_INACTIVE") apiSecret := testSecrets.MustGetField("BITFINEX_API_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 a bitfinex api key %s within bitfinex api secret %s", apiKey, apiSecret)), verify: true, }, want: []detectors.Result{ // will try to scan (apiKey, apiSecret) which will verify then (apiSecret, apiKey) which will not since both parameters have equal length { DetectorType: detectorspb.DetectorType_Bitfinex, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bitfinex api key %s within bitfinex api secret %s", inactiveApiKey, apiSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bitfinex, 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("Bitfinex.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bitfinex.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bitlyaccesstoken/000077500000000000000000000000001451332102400220635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bitlyaccesstoken/bitlyaccesstoken.go000066400000000000000000000042251451332102400257630ustar00rootroot00000000000000package bitlyaccesstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitly"}) + `\b([a-zA-Z-0-9]{40})\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{"bitly"} } // FromData will find and optionally verify BitLyAccessToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BitLyAccessToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api-ssl.bitly.com/v4/user", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BitLyAccessToken } trufflehog-3.60.0/pkg/detectors/bitlyaccesstoken/bitlyaccesstoken_test.go000066400000000000000000000056201451332102400270220ustar00rootroot00000000000000//go:build detectors // +build detectors package bitlyaccesstoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBitLyAccessToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BITLYACCESSTOKEN_TOKEN") inactiveSecret := testSecrets.MustGetField("BITLYACCESSTOKEN_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 bitlyaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BitLyAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bitlyaccesstoken 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_BitLyAccessToken, 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("BitLyAccessToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BitLyAccessToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bitmex/000077500000000000000000000000001451332102400200055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bitmex/bitmex.go000066400000000000000000000064751451332102400216400ustar00rootroot00000000000000package bitmex import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "net/http" "net/url" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitmex"}) + `([ \r\n]{1}[0-9a-zA-Z\-\_]{24}[ \r\n]{1})`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitmex"}) + `([ \r\n]{1}[0-9a-zA-Z\-\_]{48}[ \r\n]{1})`) ) // 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{"bitmex"} } // FromData will find and optionally verify Bitmex 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bitmex, Raw: []byte(resSecretMatch), RawV2: []byte(resMatch + resSecretMatch), } if verify { timestamp := strconv.FormatInt(time.Now().Unix()+5, 10) action := "GET" path := "/api/v1/user" payload := url.Values{} signature := getBitmexSignature(timestamp, resSecretMatch, action, path, payload.Encode()) req, err := http.NewRequestWithContext(ctx, action, "https://www.bitmex.com"+path, strings.NewReader(payload.Encode())) if err != nil { continue } req.Header.Add("api-expires", timestamp) req.Header.Add("api-key", resMatch) req.Header.Add("api-signature", signature) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func getBitmexSignature(timeStamp string, secret string, action string, path string, payload string) string { mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(action + path + timeStamp + payload)) macsum := mac.Sum(nil) return hex.EncodeToString(macsum) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bitmex } trufflehog-3.60.0/pkg/detectors/bitmex/bitmex_test.go000066400000000000000000000057151451332102400226730ustar00rootroot00000000000000//go:build detectors // +build detectors package bitmex import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBitmex_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) } key := testSecrets.MustGetField("BITMEX_KEY") inactiveKey := testSecrets.MustGetField("BITMEX_KEY_INACTIVE") secret := testSecrets.MustGetField("BITMEX") inactiveSecret := testSecrets.MustGetField("BITMEX_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 bitmex key %s with bitmex secret %s within", key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bitmex, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bitmex key %s with bitmex secret %s within but not valid", inactiveKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bitmex, 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("Bitmex.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bitmex.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) } } }) } } trufflehog-3.60.0/pkg/detectors/blablabus/000077500000000000000000000000001451332102400204445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/blablabus/blablabus.go000066400000000000000000000042621451332102400227260ustar00rootroot00000000000000package blablabus import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"blablabus"}) + `\b([0-9A-Za-z]{22})\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{"blablabus"} } // FromData will find and optionally verify Blablabus 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Blablabus, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.idbus.com/v3/stops", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Blablabus } trufflehog-3.60.0/pkg/detectors/blablabus/blablabus_test.go000066400000000000000000000055041451332102400237650ustar00rootroot00000000000000//go:build detectors // +build detectors package blablabus import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBlablabus_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("BLABLABUS") inactiveSecret := testSecrets.MustGetField("BLABLABUS_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 blablabus secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Blablabus, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a blablabus 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_Blablabus, 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("Blablabus.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Blablabus.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) } } }) } } trufflehog-3.60.0/pkg/detectors/blazemeter/000077500000000000000000000000001451332102400206475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/blazemeter/blazemeter.go000066400000000000000000000044001451332102400233260ustar00rootroot00000000000000package blazemeter import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"blazemeter", "runscope"}) + `\b([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"blazemeter", "runscope"} } // FromData will find and optionally verify Blazemeter 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Blazemeter, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.runscope.com/account", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Blazemeter } trufflehog-3.60.0/pkg/detectors/blazemeter/blazemeter_test.go000066400000000000000000000055161451332102400243760ustar00rootroot00000000000000//go:build detectors // +build detectors package blazemeter import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBlazemeter_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BLAZEMETER") inactiveSecret := testSecrets.MustGetField("BLAZEMETER_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 blazemeter secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Blazemeter, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a blazemeter 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_Blazemeter, 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("Blazemeter.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Blazemeter.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) } } }) } } trufflehog-3.60.0/pkg/detectors/blitapp/000077500000000000000000000000001451332102400201505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/blitapp/blitapp.go000066400000000000000000000041161451332102400221340ustar00rootroot00000000000000package blitapp import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"blitapp"}) + `\b([a-zA-Z0-9_-]{39})\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{"blitapp"} } // FromData will find and optionally verify BlitApp 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BlitApp, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://blitapp.com/api/apps/all", nil) if err != nil { continue } req.Header.Add("API-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BlitApp } trufflehog-3.60.0/pkg/detectors/blitapp/blitapp_test.go000066400000000000000000000054601451332102400231760ustar00rootroot00000000000000//go:build detectors // +build detectors package blitapp import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBlitApp_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("BLITAPP") inactiveSecret := testSecrets.MustGetField("BLITAPP_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 blitapp secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BlitApp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a blitapp 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_BlitApp, 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("BlitApp.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BlitApp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/blocknative/000077500000000000000000000000001451332102400210165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/blocknative/blocknative.go000066400000000000000000000044231451332102400236510ustar00rootroot00000000000000package blocknative import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"blocknative"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"blocknative"} } // FromData will find and optionally verify Blocknative 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BlockNative, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.blocknative.com/gasprices/blockprices", nil) if err != nil { continue } req.Header.Add("Authorization", 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, "valid") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BlockNative } trufflehog-3.60.0/pkg/detectors/blocknative/blocknative_test.go000066400000000000000000000055301451332102400247100ustar00rootroot00000000000000//go:build detectors // +build detectors package blocknative import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBlocknative_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BLOCKNATIVE") inactiveSecret := testSecrets.MustGetField("BLOCKNATIVE_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 blocknative secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BlockNative, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a blocknative 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_BlockNative, 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("Blocknative.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Blocknative.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) } } }) } } trufflehog-3.60.0/pkg/detectors/blogger/000077500000000000000000000000001451332102400201365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/blogger/blogger.go000066400000000000000000000041061451332102400221070ustar00rootroot00000000000000package blogger import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"blogger"}) + `\b([0-9A-Za-z-]{39})\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{"blogger"} } // FromData will find and optionally verify Blogger 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Blogger, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.googleapis.com/blogger/v3/blogs/2399953?key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Blogger } trufflehog-3.60.0/pkg/detectors/blogger/blogger_test.go000066400000000000000000000054601451332102400231520ustar00rootroot00000000000000//go:build detectors // +build detectors package blogger import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBlogger_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("BLOGGER") inactiveSecret := testSecrets.MustGetField("BLOGGER_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 blogger secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Blogger, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a blogger 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_Blogger, 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("Blogger.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Blogger.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bombbomb/000077500000000000000000000000001451332102400202745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bombbomb/bombbomb.go000066400000000000000000000041361451332102400224060ustar00rootroot00000000000000package bombbomb import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bombbomb"}) + `\b([a-zA-Z0-9-._]{704})\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{"bombbomb"} } // FromData will find and optionally verify BombBomb 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BombBomb, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bombbomb.com/v2/lists/", nil) if err != nil { continue } req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BombBomb } trufflehog-3.60.0/pkg/detectors/bombbomb/bombbomb_test.go000066400000000000000000000054721451332102400234510ustar00rootroot00000000000000//go:build detectors // +build detectors package bombbomb import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBombBomb_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BOMBBOMB") inactiveSecret := testSecrets.MustGetField("BOMBBOMB_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 bombbomb secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BombBomb, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bombbomb 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_BombBomb, 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("BombBomb.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BombBomb.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) } } }) } } trufflehog-3.60.0/pkg/detectors/boostnote/000077500000000000000000000000001451332102400205315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/boostnote/boostnote.go000066400000000000000000000041711451332102400230770ustar00rootroot00000000000000package boostnote import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"boostnote"}) + `\b([0-9a-f]{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{"boostnote"} } // FromData will find and optionally verify BoostNote 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BoostNote, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://boostnote.io/api/docs", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BoostNote } trufflehog-3.60.0/pkg/detectors/boostnote/boostnote_test.go000066400000000000000000000054161451332102400241410ustar00rootroot00000000000000//go:build detectors // +build detectors package boostnote import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBoostNote_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BOOSTNOTE") inactiveSecret := testSecrets.MustGetField("BOOSTNOTE_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 boostnote secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BoostNote, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a boostnote 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_BoostNote, 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("BoostNote.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BoostNote.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/borgbase/000077500000000000000000000000001451332102400203015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/borgbase/borgbase.go000066400000000000000000000050751451332102400224230ustar00rootroot00000000000000package borgbase import ( "context" "fmt" "io" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"borgbase"}) + `\b([a-zA-Z0-9/_.-]{148,152})\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{"borgbase"} } // FromData will find and optionally verify Borgbase 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Borgbase, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout payload := strings.NewReader(`{"query":"{ sshList {id, name}}"}`) req, err := http.NewRequest("POST", "https://api.borgbase.com/graphql", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"sshList":[]`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Borgbase } trufflehog-3.60.0/pkg/detectors/borgbase/borgbase_test.go000066400000000000000000000054041451332102400234560ustar00rootroot00000000000000//go:build detectors // +build detectors package borgbase import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBorgbase_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BORGBASE") inactiveSecret := testSecrets.MustGetField("BORGBASE_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 borgbase secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Borgbase, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a borgbase 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_Borgbase, 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("Borgbase.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Borgbase.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/braintreepayments/000077500000000000000000000000001451332102400222515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/braintreepayments/braintreepayments.go000066400000000000000000000054751451332102400263470ustar00rootroot00000000000000package braintreepayments import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"braintree"}) + `\b([0-9a-f]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"braintree"}) + `\b([0-9a-z]{16})\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{"braintree"} } // FromData will find and optionally verify BraintreePayments 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BraintreePayments, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"query": "query { ping }"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://payments.braintree-api.com/graphql", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Braintree-Version", "2019-01-01") req.SetBasicAuth(resIdMatch, resMatch) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"data":{`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BraintreePayments } trufflehog-3.60.0/pkg/detectors/braintreepayments/braintreepayments_test.go000066400000000000000000000056661451332102400274100ustar00rootroot00000000000000package braintreepayments import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBraintreePayments_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BRAINTREEPAYMENTS") id := testSecrets.MustGetField("BRAINTREEPAYMENTS_USER") inactiveSecret := testSecrets.MustGetField("BRAINTREEPAYMENTS_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 braintree secret %s within braintree %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BraintreePayments, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a braintree secret %s within braintree %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BraintreePayments, 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("BraintreePayments.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("BraintreePayments.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) } } }) } } trufflehog-3.60.0/pkg/detectors/brandfetch/000077500000000000000000000000001451332102400206155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/brandfetch/brandfetch.go000066400000000000000000000043501451332102400232460ustar00rootroot00000000000000package brandfetch import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"brandfetch"}) + `\b([0-9A-Za-z]{40})\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{"brandfetch"} } // FromData will find and optionally verify Brandfetch 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Brandfetch, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{ "domain": "www.example.com" }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.brandfetch.io/v1/color", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Brandfetch } trufflehog-3.60.0/pkg/detectors/brandfetch/brandfetch_test.go000066400000000000000000000055161451332102400243120ustar00rootroot00000000000000//go:build detectors // +build detectors package brandfetch import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBrandfetch_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("BRANDFETCH") inactiveSecret := testSecrets.MustGetField("BRANDFETCH_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 brandfetch secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Brandfetch, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a brandfetch 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_Brandfetch, 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("Brandfetch.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Brandfetch.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) } } }) } } trufflehog-3.60.0/pkg/detectors/browserstack/000077500000000000000000000000001451332102400212265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/browserstack/browserstack.go000066400000000000000000000066141451332102400242750ustar00rootroot00000000000000package browserstack import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) const browserStackAPIURL = "https://www.browserstack.com/automate/plan.json" var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hub-cloud.browserstack.com", "accessKey", "\"access_Key\":", "ACCESS_KEY", "key", "browserstackKey", "BS_AUTHKEY", "BROWSERSTACK_ACCESS_KEY"}) + `\b([0-9a-zA-Z]{20})\b`) userPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hub-cloud.browserstack.com", "userName", "\"username\":", "USER_NAME", "user", "browserstackUser", "BS_USERNAME", "BROWSERSTACK_USERNAME"}) + `\b([a-zA-Z\d]{3,18}[._-]?[a-zA-Z\d]{6,11})\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{"browserstack"} } // FromData will find and optionally verify BrowserStack 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) userMatches := userPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, userMatch := range userMatches { if len(userMatch) != 2 { continue } resUserMatch := strings.TrimSpace(userMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BrowserStack, Raw: []byte(resMatch), RawV2: []byte(resMatch + resUserMatch), } if verify { client := s.client if client == nil { client = defaultClient } isVerified, verificationErr := verifyBrowserStackCredentials(ctx, client, resUserMatch, resMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } } return results, nil } func verifyBrowserStackCredentials(ctx context.Context, client *http.Client, username, accessKey string) (bool, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, browserStackAPIURL, nil) if err != nil { return false, err } req.Header.Add("Content-Type", "application/json") req.SetBasicAuth(username, accessKey) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { return true, nil } else if res.StatusCode != 401 { return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } return false, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BrowserStack } trufflehog-3.60.0/pkg/detectors/browserstack/browserstack_test.go000066400000000000000000000112471451332102400253320ustar00rootroot00000000000000//go:build detectors // +build detectors package browserstack 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 TestBrowserStack_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secretUser := testSecrets.MustGetField("BROWSERSTACK_USER") secret := testSecrets.MustGetField("BROWSERSTACK") inactiveSecret := testSecrets.MustGetField("BROWSERSTACK_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a BROWSERSTACK_ACCESS_KEY %s within BROWSERSTACK_USERNAME %s", secret, secretUser)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BrowserStack, Verified: true, RawV2: []byte(fmt.Sprintf("%s%s", secret, secretUser)), }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a BROWSERSTACK_ACCESS_KEY %s within BROWSERSTACK_USERNAME %s but not valid", inactiveSecret, secretUser)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BrowserStack, Verified: false, RawV2: []byte(fmt.Sprintf("%s%s", inactiveSecret, secretUser)), }, }, wantErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a BROWSERSTACK_ACCESS_KEY %s within BROWSERSTACK_USERNAME %s", secret, secretUser)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BrowserStack, RawV2: []byte(fmt.Sprintf("%s%s", secret, secretUser)), Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a BROWSERSTACK_ACCESS_KEY %s within BROWSERSTACK_USERNAME %s", secret, secretUser)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BrowserStack, Verified: false, RawV2: []byte(fmt.Sprintf("%s%s", secret, secretUser)), }, }, wantErr: false, wantVerificationErr: true, }, { 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("BrowserStack.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("BrowserStack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/browshot/000077500000000000000000000000001451332102400203645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/browshot/browshot.go000066400000000000000000000041071451332102400225640ustar00rootroot00000000000000package browshot import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"browshot"}) + `\b([a-zA-Z-0-9]{28})\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{"browshot"} } // FromData will find and optionally verify Browshot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Browshot, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.browshot.com/api/v1/instance/list?key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Browshot } trufflehog-3.60.0/pkg/detectors/browshot/browshot_test.go000066400000000000000000000054721451332102400236310ustar00rootroot00000000000000//go:build detectors // +build detectors package browshot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBrowshot_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("BROWSHOT") inactiveSecret := testSecrets.MustGetField("BROWSHOT_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 browshot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Browshot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a browshot 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_Browshot, 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("Browshot.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Browshot.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bscscan/000077500000000000000000000000001451332102400201315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bscscan/bscscan.go000066400000000000000000000051701451332102400220770ustar00rootroot00000000000000package bscscan import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bscscan"}) + `\b([0-9A-Z]{34})\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{"bscscan"} } // FromData will find and optionally verify Bscscan 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BscScan, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bscscan.com/api?module=account&action=balance&address=0x70F657164e5b75689b64B7fd1fA275F334f28e18&apikey="+resMatch, nil) if err != nil { continue } 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, "NOTOK") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sur> if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BscScan } trufflehog-3.60.0/pkg/detectors/bscscan/bscscan_test.go000066400000000000000000000054601451332102400231400ustar00rootroot00000000000000//go:build detectors // +build detectors package bscscan import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBscscan_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BSCSCAN") inactiveSecret := testSecrets.MustGetField("BSCSCAN_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 bscscan secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BscScan, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bscscan 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_BscScan, 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("Bscscan.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bscscan.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) } } }) } } trufflehog-3.60.0/pkg/detectors/buddyns/000077500000000000000000000000001451332102400201655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/buddyns/buddyns.go000066400000000000000000000042511451332102400221660ustar00rootroot00000000000000package buddyns import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"buddyns"}) + `\b([0-9a-z]{40})\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{"buddyns"} } // FromData will find and optionally verify Buddyns 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_BuddyNS, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.buddyns.com/api/v2/zone/", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_BuddyNS } trufflehog-3.60.0/pkg/detectors/buddyns/buddyns_test.go000066400000000000000000000054601451332102400232300ustar00rootroot00000000000000//go:build detectors // +build detectors package buddyns import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBuddyns_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BUDDYNS") inactiveSecret := testSecrets.MustGetField("BUDDYNS_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 buddyns secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_BuddyNS, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a buddyns 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_BuddyNS, 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("Buddyns.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Buddyns.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bugherd/000077500000000000000000000000001451332102400201355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bugherd/bugherd.go000066400000000000000000000044451451332102400221130ustar00rootroot00000000000000package bugherd import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bugherd"}) + `\b([0-9a-z]{22})\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{"bugherd"} } // FromData will find and optionally verify Bugherd 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bugherd, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:x", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://www.bugherd.com/api_v2/projects.json", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bugherd } trufflehog-3.60.0/pkg/detectors/bugherd/bugherd_test.go000066400000000000000000000054601451332102400231500ustar00rootroot00000000000000//go:build detectors // +build detectors package bugherd import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBugherd_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BUGHERD") inactiveSecret := testSecrets.MustGetField("BUGHERD_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 bugherd secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bugherd, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bugherd 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_Bugherd, 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("Bugherd.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bugherd.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bugsnag/000077500000000000000000000000001451332102400201435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bugsnag/bugsnag.go000066400000000000000000000043451451332102400221260ustar00rootroot00000000000000package bugsnag import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bugsnag"}) + `\b([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"bugsnag"} } // FromData will find and optionally verify Bugsnag 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bugsnag, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bugsnag.com/user/organizations?admin", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bugsnag } trufflehog-3.60.0/pkg/detectors/bugsnag/bugsnag_test.go000066400000000000000000000054601451332102400231640ustar00rootroot00000000000000//go:build detectors // +build detectors package bugsnag import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBugsnag_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("BUGSNAG") inactiveSecret := testSecrets.MustGetField("BUGSNAG_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 bugsnag secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bugsnag, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bugsnag 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_Bugsnag, 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("Bugsnag.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bugsnag.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) } } }) } } trufflehog-3.60.0/pkg/detectors/buildkite/000077500000000000000000000000001451332102400204715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/buildkite/buildkite.go000066400000000000000000000043361451332102400230020ustar00rootroot00000000000000package buildkite import ( "context" "fmt" "net/http" "regexp" "strings" "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{} func (s Scanner) Version() int { return 1 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"buildkite"}) + `\b([a-z0-9]{40})\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{"buildkite"} } // FromData will find and optionally verify Buildkite 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Buildkite, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.buildkite.com/v2/access-token", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Buildkite } trufflehog-3.60.0/pkg/detectors/buildkite/buildkite_test.go000066400000000000000000000055121451332102400240360ustar00rootroot00000000000000//go:build detectors // +build detectors package buildkite import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBuildkite_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BUILDKITE_TOKEN") inactiveSecret := testSecrets.MustGetField("BUILDKITE_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 buildkite secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Buildkite, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a buildkite 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_Buildkite, 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("Buildkite.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Buildkite.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) } } }) } } trufflehog-3.60.0/pkg/detectors/buildkitev2/000077500000000000000000000000001451332102400207415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/buildkitev2/buildkite.go000066400000000000000000000042621451332102400232500ustar00rootroot00000000000000package buildkitev2 import ( "context" "fmt" "net/http" "regexp" "strings" "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{} func (s Scanner) Version() int { return 2 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(bkua_[a-z0-9]{40})\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{"bkua_"} } // FromData will find and optionally verify Buildkite 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Buildkite, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.buildkite.com/v2/access-token", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Buildkite } trufflehog-3.60.0/pkg/detectors/buildkitev2/buildkite_test.go000066400000000000000000000055201451332102400243050ustar00rootroot00000000000000//go:build detectors // +build detectors package buildkitev2 import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBuildkite_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BUILDKITEV2_TOKEN") inactiveSecret := testSecrets.MustGetField("BUILDKITEV2_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 buildkite secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Buildkite, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a buildkite 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_Buildkite, 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("Buildkite.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Buildkite.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bulbul/000077500000000000000000000000001451332102400200025ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bulbul/bulbul.go000066400000000000000000000045431451332102400216240ustar00rootroot00000000000000package bulbul import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bulbul"}) + `\b([a-z0-9]{32})\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{"bulbul"} } // FromData will find and optionally verify Bulbul 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bulbul, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://prod-api.bulbul.io/view_all_users?api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"message":"Successful",`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bulbul } trufflehog-3.60.0/pkg/detectors/bulbul/bulbul_test.go000066400000000000000000000054461451332102400226660ustar00rootroot00000000000000//go:build detectors // +build detectors package bulbul import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBulbul_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("BULBUL") inactiveSecret := testSecrets.MustGetField("BULBUL_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 bulbul secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bulbul, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bulbul 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_Bulbul, 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("Bulbul.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bulbul.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) } } }) } } trufflehog-3.60.0/pkg/detectors/bulksms/000077500000000000000000000000001451332102400201755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/bulksms/bulksms.go000066400000000000000000000051331451332102400222060ustar00rootroot00000000000000package bulksms import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bulksms"}) + `\b([a-fA-Z0-9*]{29})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bulksms"}) + `\b([A-F0-9-]{37})\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{"bulksms"} } // FromData will find and optionally verify Bulksms 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idMatches { if len(match) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Bulksms, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { data := fmt.Sprintf("%s:%s", resIdMatch, resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bulksms.com/v1/messages", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Bulksms } trufflehog-3.60.0/pkg/detectors/bulksms/bulksms_test.go000066400000000000000000000056101451332102400232450ustar00rootroot00000000000000//go:build detectors // +build detectors package bulksms import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestBulksms_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BULKSMS") inactiveSecret := testSecrets.MustGetField("BULKSMS_INACTIVE") token := testSecrets.MustGetField("BULKSMS_TOKEN") 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 bulksms secret %s within bulksms %s", secret, token)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bulksms, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a bulksms secret %s within bulksms but %s not valid", inactiveSecret, token)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Bulksms, 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("Bulksms.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Bulksms.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) } } }) } } trufflehog-3.60.0/pkg/detectors/buttercms/000077500000000000000000000000001451332102400205255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/buttercms/buttercms.go000066400000000000000000000041071451332102400230660ustar00rootroot00000000000000package buttercms import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"buttercms"}) + `\b([a-z0-9]{40})\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{"buttercms"} } // FromData will find and optionally verify ButterCMS 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ButterCMS, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.buttercms.com/v2/posts/?auth_token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ButterCMS } trufflehog-3.60.0/pkg/detectors/buttercms/buttercms_test.go000066400000000000000000000055121451332102400241260ustar00rootroot00000000000000//go:build detectors // +build detectors package buttercms import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestButterCMS_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("BUTTERCMS_TOKEN") inactiveSecret := testSecrets.MustGetField("BUTTERCMS_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 buttercms secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ButterCMS, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a buttercms 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_ButterCMS, 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("ButterCMS.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ButterCMS.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) } } }) } } trufflehog-3.60.0/pkg/detectors/caflou/000077500000000000000000000000001451332102400177665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/caflou/caflou.go000066400000000000000000000042711451332102400215720ustar00rootroot00000000000000package caflou import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"caflou"}) + `\b([a-bA-Z0-9\S]{155})\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{"caflou"} } // FromData will find and optionally verify Caflou 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Caflou, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://app.caflou.com/api/v1/accounts", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Caflou } trufflehog-3.60.0/pkg/detectors/caflou/caflou_test.go000066400000000000000000000054461451332102400226360ustar00rootroot00000000000000//go:build detectors // +build detectors package caflou import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCaflou_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CAFLOU") inactiveSecret := testSecrets.MustGetField("CAFLOU_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 caflou secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Caflou, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a caflou 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_Caflou, 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("Caflou.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Caflou.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) } } }) } } trufflehog-3.60.0/pkg/detectors/calendarific/000077500000000000000000000000001451332102400211215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/calendarific/calendarific.go000066400000000000000000000041631451332102400240600ustar00rootroot00000000000000package calendarific import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"calendarific"}) + `\b([a-z0-9]{40})\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{"calendarific"} } // FromData will find and optionally verify Calendarific 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Calendarific, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://calendarific.com/api/v2/holidays?&api_key="+resMatch+"&country=US&year=2019", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Calendarific } trufflehog-3.60.0/pkg/detectors/calendarific/calendarific_test.go000066400000000000000000000055501451332102400251200ustar00rootroot00000000000000//go:build detectors // +build detectors package calendarific import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCalendarific_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CALENDARIFIC_TOKEN") inactiveSecret := testSecrets.MustGetField("CALENDARIFIC_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 calendarific secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Calendarific, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a calendarific 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_Calendarific, 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("Calendarific.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Calendarific.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) } } }) } } trufflehog-3.60.0/pkg/detectors/calendlyapikey/000077500000000000000000000000001451332102400215135ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/calendlyapikey/calendlyapikey.go000066400000000000000000000042671451332102400250510ustar00rootroot00000000000000package calendlyapikey import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"calendly"}) + `\b([a-zA-Z-0-9]{20}.[a-zA-Z-0-9]{171}.[a-zA-Z-0-9_]{43})\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{"calendly"} } // FromData will find and optionally verify CalendlyApiKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CalendlyApiKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.calendly.com/users/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CalendlyApiKey } trufflehog-3.60.0/pkg/detectors/calendlyapikey/calendlyapikey_test.go000066400000000000000000000055741451332102400261120ustar00rootroot00000000000000//go:build detectors // +build detectors package calendlyapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCalendlyApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CALENDLYAPIKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("CALENDLYAPIKEY_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 calendlyapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CalendlyApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a calendlyapikey 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_CalendlyApiKey, 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("CalendlyApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CalendlyApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/calorieninja/000077500000000000000000000000001451332102400211535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/calorieninja/calorieninja.go000066400000000000000000000042621451332102400241440ustar00rootroot00000000000000package calorieninja import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"calorieninja"}) + `\b([0-9A-Za-z]{40})\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{"calorieninja"} } // FromData will find and optionally verify Calorieninja 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CalorieNinja, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.calorieninjas.com/v1/nutrition?query", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CalorieNinja } trufflehog-3.60.0/pkg/detectors/calorieninja/calorieninja_test.go000066400000000000000000000055421451332102400252050ustar00rootroot00000000000000//go:build detectors // +build detectors package calorieninja import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCalorieninja_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CALORIENINJA") inactiveSecret := testSecrets.MustGetField("CALORIENINJA_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 calorieninja secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CalorieNinja, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a calorieninja 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_CalorieNinja, 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("Calorieninja.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Calorieninja.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) } } }) } } trufflehog-3.60.0/pkg/detectors/campayn/000077500000000000000000000000001451332102400201455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/campayn/campayn.go000066400000000000000000000041421451332102400221250ustar00rootroot00000000000000package campayn import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"campayn"}) + `\b([a-z0-9]{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{"campayn"} } // FromData will find and optionally verify Campayn 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Campayn, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://campayn.com/api/v1/lists", nil) if err != nil { continue } req.Header.Add("Authorization", "TRUEREST apikey="+resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Campayn } trufflehog-3.60.0/pkg/detectors/campayn/campayn_test.go000066400000000000000000000054661451332102400231760ustar00rootroot00000000000000//go:build detectors // +build detectors package campayn import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCampayn_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CAMPAYN_TOKEN") inactiveSecret := testSecrets.MustGetField("CAMPAYN_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 campayn secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Campayn, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a campayn 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_Campayn, 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("Campayn.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Campayn.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cannyio/000077500000000000000000000000001451332102400201555ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cannyio/cannyio.go000066400000000000000000000043171451332102400221510ustar00rootroot00000000000000package cannyio import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"canny"}) + `\b([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[0-9]{4}-[a-z0-9]{12})\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{"canny"} } // FromData will find and optionally verify CannyIo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CannyIo, Raw: []byte(resMatch), } if verify { payload := strings.NewReader("apiKey=" + resMatch) req, err := http.NewRequestWithContext(ctx, "POST", "https://canny.io/api/v1/boards/list", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CannyIo } trufflehog-3.60.0/pkg/detectors/cannyio/cannyio_test.go000066400000000000000000000054661451332102400232160ustar00rootroot00000000000000//go:build detectors // +build detectors package cannyio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCannyIo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CANNYIO_TOKEN") inactiveSecret := testSecrets.MustGetField("CANNYIO_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 cannyio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CannyIo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cannyio 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_CannyIo, 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("CannyIo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CannyIo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/capsulecrm/000077500000000000000000000000001451332102400206535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/capsulecrm/capsulecrm.go000066400000000000000000000042211451332102400233370ustar00rootroot00000000000000package capsulecrm import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"capsulecrm"}) + `\b([a-zA-Z0-9-._+=]{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{"capsulecrm"} } // FromData will find and optionally verify CapsuleCRM 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CapsuleCRM, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.capsulecrm.com/api/v2/users", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CapsuleCRM } trufflehog-3.60.0/pkg/detectors/capsulecrm/capsulecrm_test.go000066400000000000000000000055161451332102400244060ustar00rootroot00000000000000//go:build detectors // +build detectors package capsulecrm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCapsuleCRM_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("CAPSULECRM") inactiveSecret := testSecrets.MustGetField("CAPSULECRM_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 capsulecrm secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CapsuleCRM, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a capsulecrm 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_CapsuleCRM, 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("CapsuleCRM.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CapsuleCRM.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) } } }) } } trufflehog-3.60.0/pkg/detectors/captaindata/000077500000000000000000000000001451332102400207665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/captaindata/captaindata.go000066400000000000000000000050641451332102400235730ustar00rootroot00000000000000package captaindata import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"captaindata"}) + `\b([0-9a-f]{64})\b`) projIdPat = regexp.MustCompile(detectors.PrefixRegex([]string{"captaindata"}) + `\b([0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})\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{"captaindata"} } // FromData will find and optionally verify CaptainData 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) projIdMatches := projIdPat.FindAllStringSubmatch(dataStr, -1) for _, projIdMatch := range projIdMatches { if len(projIdMatch) != 2 { continue } resProjIdMatch := strings.TrimSpace(projIdMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CaptainData, Raw: []byte(resMatch), RawV2: []byte(resProjIdMatch + resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.captaindata.co/v2/"+resProjIdMatch, nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CaptainData } trufflehog-3.60.0/pkg/detectors/captaindata/captaindata_test.go000066400000000000000000000056441451332102400246360ustar00rootroot00000000000000//go:build detectors // +build detectors package captaindata import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCaptainData_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } projId := testSecrets.MustGetField("CAPTAINDATA_PROJID") secret := testSecrets.MustGetField("CAPTAINDATA") inactiveSecret := testSecrets.MustGetField("CAPTAINDATA_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 captaindata project %s with captaindata secret %s within", projId, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CaptainData, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a captaindata project %s with captaindata secret %s within but not valid", projId, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CaptainData, 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("CaptainData.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CaptainData.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/carboninterface/000077500000000000000000000000001451332102400216425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/carboninterface/carboninterface.go000066400000000000000000000046531451332102400253260ustar00rootroot00000000000000package carboninterface import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"carboninterface"}) + `\b([a-zA-Z0-9]{21})\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{"carboninterface"} } // FromData will find and optionally verify CarbonInterface 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CarbonInterface, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"type":"flight","passengers":2,"legs":[{"departure_airport":"sfo","destination_airport":"yyz"},{"departure_airport":"yyz","destination_airport":"sfo"}]}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://www.carboninterface.com/api/v1/estimates", payload) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) req.Header.Add("Content-type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CarbonInterface } trufflehog-3.60.0/pkg/detectors/carboninterface/carboninterface_test.go000066400000000000000000000056001451332102400263560ustar00rootroot00000000000000//go:build detectors // +build detectors package carboninterface import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCarbonInterface_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("CARBONINTERFACE") inactiveSecret := testSecrets.MustGetField("CARBONINTERFACE_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 carboninterface secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CarbonInterface, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a carboninterface 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_CarbonInterface, 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("CarbonInterface.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CarbonInterface.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cashboard/000077500000000000000000000000001451332102400204435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cashboard/cashboard.go000066400000000000000000000052261451332102400227250ustar00rootroot00000000000000package cashboard import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cashboard"}) + `\b([0-9A-Z]{3}-[0-9A-Z]{3}-[0-9A-Z]{3}-[0-9A-Z]{3})\b`) userPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cashboard"}) + `\b([0-9a-z]{1,})\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{"cashboard"} } // FromData will find and optionally verify Cashboard 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) userMatches := userPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, userMatch := range userMatches { if len(userMatch) != 2 { continue } resUser := strings.TrimSpace(userMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cashboard, Raw: []byte(resMatch), RawV2: []byte(resMatch + resUser), } if verify { data := fmt.Sprintf("%s:%s", resUser, resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cashboardapp.com/account.xml", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cashboard } trufflehog-3.60.0/pkg/detectors/cashboard/cashboard_test.go000066400000000000000000000056141451332102400237650ustar00rootroot00000000000000//go:build detectors // +build detectors package cashboard import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCashboard_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CASHBOARD") user := testSecrets.MustGetField("SCANNER_USERNAME") inactiveSecret := testSecrets.MustGetField("CASHBOARD_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 cashboard secret %s within %s", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cashboard, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cashboard secret %s within %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cashboard, 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("Cashboard.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cashboard.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) } } }) } } trufflehog-3.60.0/pkg/detectors/caspio/000077500000000000000000000000001451332102400177735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/caspio/caspio.go000066400000000000000000000057011451332102400216030ustar00rootroot00000000000000package caspio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"caspio"}) + `\b([a-z0-9]{50})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"caspio"}) + `\b([a-z0-9]{50})\b`) domainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"caspio"}) + `\b([a-z0-9]{8})\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{"caspio"} } // FromData will find and optionally verify Caspio 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) for _, domainMatch := range domainMatches { if len(domainMatch) != 2 { continue } resDomainMatch := strings.TrimSpace(domainMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Caspio, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch + resDomainMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`grant_type=client_credentials&client_id=%s&client_secret=%s`, resIdMatch, resMatch)) req, err := http.NewRequest("POST", fmt.Sprintf("https://%s.caspio.com/oauth/token", resDomainMatch), payload) if err != nil { continue } req.Header.Add("Content-Type", "text/plain") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Caspio } trufflehog-3.60.0/pkg/detectors/caspio/caspio_test.go000066400000000000000000000056721451332102400226510ustar00rootroot00000000000000//go:build detectors // +build detectors package caspio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCaspio_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CASPIO") id := testSecrets.MustGetField("CASPIO_ID") subdomain := testSecrets.MustGetField("CASPIO_SUBDOMAIN") inactiveSecret := testSecrets.MustGetField("CASPIO_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 caspio secret %s within caspio %s and caspio subdomain %s", secret, id, subdomain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Caspio, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a caspio secret %s within caspio %s and caspio subdomain %s but not valid", inactiveSecret, id, subdomain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Caspio, 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("Caspio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Caspio.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/censys/000077500000000000000000000000001451332102400200215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/censys/censys.go000066400000000000000000000047241451332102400216630ustar00rootroot00000000000000package censys import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"censys"}) + `\b([a-zA-Z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"censys"}) + `\b([a-z0-9-]{36})\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{"censys"} } // FromData will find and optionally verify Censys 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Censys, Raw: []byte(tokenPatMatch), RawV2: []byte(tokenPatMatch + userPatMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://search.censys.io/api/v1/account", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Censys } trufflehog-3.60.0/pkg/detectors/censys/censys_test.go000066400000000000000000000055571451332102400227270ustar00rootroot00000000000000//go:build detectors // +build detectors package censys import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCensys_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("CENSYS") id := testSecrets.MustGetField("CENSYS_ID") inactiveSecret := testSecrets.MustGetField("CENSYS_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 censys secret %s within censys %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Censys, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a censys secret %s within censys %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Censys, 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("Censys.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Censys.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) } } }) } } trufflehog-3.60.0/pkg/detectors/centralstationcrm/000077500000000000000000000000001451332102400222515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/centralstationcrm/centralstationcrm.go000066400000000000000000000042231451332102400263350ustar00rootroot00000000000000package centralstationcrm import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"centralstation"}) + `\b([a-z0-9]{30})\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{"centralstationcrm"} } // FromData will find and optionally verify CentralStationCRM 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CentralStationCRM, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.centralstationcrm.net/api/users.json", nil) if err != nil { continue } req.Header.Add("X-apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CentralStationCRM } trufflehog-3.60.0/pkg/detectors/centralstationcrm/centralstationcrm_test.go000066400000000000000000000056241451332102400274020ustar00rootroot00000000000000//go:build detectors // +build detectors package centralstationcrm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCentralStationCRM_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("CENTRALSTATIONCRM") inactiveSecret := testSecrets.MustGetField("CENTRALSTATIONCRM_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 centralstationcrm secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CentralStationCRM, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a centralstationcrm 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_CentralStationCRM, 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("CentralStationCRM.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CentralStationCRM.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cexio/000077500000000000000000000000001451332102400176245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cexio/cexio.go000066400000000000000000000103321451332102400212610ustar00rootroot00000000000000package cexio import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "io" "net/http" "net/url" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cexio", "cex.io"}) + `\b([0-9A-Za-z]{24,27})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cexio", "cex.io"}) + `\b([0-9A-Za-z]{24,27})\b`) userIdPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cexio", "cex.io"}) + `\b([a-z]{2}[0-9]{9})\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{"cexio", "cex.io"} } // FromData will find and optionally verify CexIO 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) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) userIdMatches := userIdPat.FindAllStringSubmatch(dataStr, -1) for _, userIdMatch := range userIdMatches { if len(userIdMatch) != 2 { continue } resUserIdMatch := strings.TrimSpace(userIdMatch[1]) for _, keyMatch := range keyMatches { if len(keyMatch) != 2 { continue } resKeyMatch := strings.TrimSpace(keyMatch[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CexIO, Raw: []byte(resKeyMatch), RawV2: []byte(resUserIdMatch + resSecretMatch), } if verify { timestamp := strconv.FormatInt(time.Now().Unix()*1000, 10) signature := getCexIOPassphrase(resSecretMatch, resKeyMatch, timestamp, resUserIdMatch) payload := url.Values{} payload.Add("key", resKeyMatch) payload.Add("signature", signature) payload.Add("nonce", timestamp) req, err := http.NewRequestWithContext(ctx, "POST", "https://cex.io/api/balance/", strings.NewReader(payload.Encode())) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := client.Do(req) if err == nil { defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(body) validResponse := strings.Contains(bodyString, `timestamp`) var responseObject Response if err := json.Unmarshal(body, &responseObject); err != nil { continue } if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resUserIdMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resKeyMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } type Response struct { Error string `json:"error"` } func getCexIOPassphrase(apiSecret string, apiKey string, nonce string, userId string) string { msg := nonce + userId + apiKey mac := hmac.New(sha256.New, []byte(apiSecret)) mac.Write([]byte(msg)) macsum := mac.Sum(nil) return strings.ToUpper(hex.EncodeToString(macsum)) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CexIO } trufflehog-3.60.0/pkg/detectors/cexio/cexio_test.go000066400000000000000000000057651451332102400223360ustar00rootroot00000000000000//go:build detectors // +build detectors package cexio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCexIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } userId := testSecrets.MustGetField("CEXIO_USERID") key := testSecrets.MustGetField("CEXIO_KEY") inactiveKey := testSecrets.MustGetField("CEXIO_KEY_INACTIVE") secret := testSecrets.MustGetField("CEXIO") inactiveSecret := testSecrets.MustGetField("CEXIO_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 cexio userId %s with cexio key %s and cexio secret %s within", userId, key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CexIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cexio userId %s with cexio key %s and cexio secret %s within but not valid", userId, inactiveKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CexIO, 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("CexIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CexIO.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/chartmogul/000077500000000000000000000000001451332102400206625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/chartmogul/chartmogul.go000066400000000000000000000043661451332102400233670ustar00rootroot00000000000000package chartmogul import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"chartmogul"}) + `\b([a-z0-9]{32})\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{"chartmogul"} } // FromData will find and optionally verify Chartmogul 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Chartmogul, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.chartmogul.com/v1/ping", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Chartmogul } trufflehog-3.60.0/pkg/detectors/chartmogul/chartmogul_test.go000066400000000000000000000055161451332102400244240ustar00rootroot00000000000000//go:build detectors // +build detectors package chartmogul import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestChartmogul_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CHARTMOGUL") inactiveSecret := testSecrets.MustGetField("CHARTMOGUL_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 chartmogul secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Chartmogul, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a chartmogul 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_Chartmogul, 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("Chartmogul.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Chartmogul.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) } } }) } } trufflehog-3.60.0/pkg/detectors/chatbot/000077500000000000000000000000001451332102400201415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/chatbot/chatbot.go000066400000000000000000000041631451332102400221200ustar00rootroot00000000000000package chatbot import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"chatbot"}) + `\b([a-zA-Z0-9_]{32})\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{"chatbot"} } // FromData will find and optionally verify Chatbot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Chatbot, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.chatbot.com/stories", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Chatbot } trufflehog-3.60.0/pkg/detectors/chatbot/chatbot_test.go000066400000000000000000000054661451332102400231660ustar00rootroot00000000000000//go:build detectors // +build detectors package chatbot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestChatbot_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CHATBOT_TOKEN") inactiveSecret := testSecrets.MustGetField("CHATBOT_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 chatbot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Chatbot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a chatbot 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_Chatbot, 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("Chatbot.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Chatbot.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) } } }) } } trufflehog-3.60.0/pkg/detectors/chatfule/000077500000000000000000000000001451332102400203105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/chatfule/chatfule.go000066400000000000000000000041621451332102400224350ustar00rootroot00000000000000package chatfule import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"chatfuel"}) + `\b([a-zA-Z0-9]{128})\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{"chatfuel"} } // FromData will find and optionally verify Chatfule 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Chatfule, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", "https://dashboard.chatfuel.com/api/bots", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Chatfule } trufflehog-3.60.0/pkg/detectors/chatfule/chatfule_test.go000066400000000000000000000054041451332102400234740ustar00rootroot00000000000000//go:build detectors // +build detectors package chatfule import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestChatfule_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CHATFULE") inactiveSecret := testSecrets.MustGetField("CHATFULE_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 chatfuel secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Chatfule, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a chatfuel 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_Chatfule, 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("Chatfuel.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Chatfuel.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/checio/000077500000000000000000000000001451332102400177475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/checio/checio.go000066400000000000000000000041401451332102400215270ustar00rootroot00000000000000package checio import ( "context" // "log" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"checio"}) + `\b(pk_[a-z0-9]{45})\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{"checio"} } // FromData will find and optionally verify ChecIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ChecIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.chec.io/v1/products?limit=25", nil) if err != nil { continue } req.Header.Add("X-Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ChecIO } trufflehog-3.60.0/pkg/detectors/checio/checio_test.go000066400000000000000000000054461451332102400226000ustar00rootroot00000000000000//go:build detectors // +build detectors package checio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestChecIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CHECIO") inactiveSecret := testSecrets.MustGetField("CHECIO_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 checio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ChecIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a checio 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_ChecIO, 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("ChecIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ChecIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/checklyhq/000077500000000000000000000000001451332102400204705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/checklyhq/checklyhq.go000066400000000000000000000041771451332102400230030ustar00rootroot00000000000000package checklyhq import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"checklyhq"}) + `\b([a-z0-9]{32})\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{"checklyhq"} } // FromData will find and optionally verify ChecklyHQ 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ChecklyHQ, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.checklyhq.com/v1/checks", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ChecklyHQ } trufflehog-3.60.0/pkg/detectors/checklyhq/checklyhq_test.go000066400000000000000000000055041451332102400240350ustar00rootroot00000000000000//go:build detectors // +build detectors package checklyhq import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestChecklyHQ_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CHECKLYHQ") inactiveSecret := testSecrets.MustGetField("CHECKLYHQ_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 checklyhq secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ChecklyHQ, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a checklyhq 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_ChecklyHQ, 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("ChecklyHQ.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ChecklyHQ.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) } } }) } } trufflehog-3.60.0/pkg/detectors/checkout/000077500000000000000000000000001451332102400203225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/checkout/checkout.go000066400000000000000000000054211451332102400224600ustar00rootroot00000000000000package checkout import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // Tokens starting with sk_test are used for the app's sandbox environment while tokens starting with sk only are for production environment keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"checkout"}) + `\b((sk_|sk_test_)[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"checkout"}) + `\b(cus_[0-9a-zA-Z]{26})\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{"checkout"} } // FromData will find and optionally verify Checkout 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 3 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Checkout, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { // Used the app's sandbox environment for this case since I can't create a live account. req, err := http.NewRequestWithContext(ctx, "GET", "https://api.sandbox.checkout.com/customers/"+resIdMatch, nil) if err != nil { continue } req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Checkout } trufflehog-3.60.0/pkg/detectors/checkout/checkout_test.go000066400000000000000000000056531451332102400235260ustar00rootroot00000000000000//go:build detectors // +build detectors package checkout import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCheckout_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("CHECKOUT") inactiveSecret := testSecrets.MustGetField("CHECKOUT_INACTIVE") secretId := testSecrets.MustGetField("CHECKOUT_ID") 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 checkout secret %s with checkout id %s within", secret, secretId)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Checkout, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a checkout secret %s with checkout id %s within but not valid", inactiveSecret, secretId)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Checkout, 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("Checkout.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Checkout.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) } } }) } } trufflehog-3.60.0/pkg/detectors/checkvist/000077500000000000000000000000001451332102400205005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/checkvist/checkvist.go000066400000000000000000000052501451332102400230140ustar00rootroot00000000000000package checkvist import ( "context" "net/http" "net/url" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"checkvist"}) + `\b([0-9a-zA-Z]{14})\b`) emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"checkvist"}) + `\b([\w\.-]+@[\w-]+\.[\w\.-]{2,5})\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{"checkvist"} } // FromData will find and optionally verify Checkvist 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) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } resEmailMatch := strings.TrimSpace(emailMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Checkvist, Raw: []byte(resMatch), RawV2: []byte(resMatch + resEmailMatch), } if verify { payload := url.Values{} payload.Add("username", resEmailMatch) payload.Add("remote_key", resMatch) req, err := http.NewRequest("GET", "https://checkvist.com/auth/login.json?version=2", strings.NewReader(payload.Encode())) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Checkvist } trufflehog-3.60.0/pkg/detectors/checkvist/checkvist_test.go000066400000000000000000000055751451332102400240650ustar00rootroot00000000000000//go:build detectors // +build detectors package checkvist import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCheckvist_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } user := testSecrets.MustGetField("CHECKVIST_EMAIL") secret := testSecrets.MustGetField("CHECKVIST") inactiveSecret := testSecrets.MustGetField("CHECKVIST_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 checkvist user %s with checkvist secret %s within", user, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Checkvist, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a checkvist user %s with checkvist secret %s within but not valid", user, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Checkvist, 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("Checkvist.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Checkvist.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/cicero/000077500000000000000000000000001451332102400177615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cicero/cicero.go000066400000000000000000000042171451332102400215600ustar00rootroot00000000000000package cicero import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cicero"}) + `\b([0-9a-z]{40})\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{"cicero"} } // FromData will find and optionally verify Cicero 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cicero, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://cicero.azavea.com/v3.1/account/credits_remaining?key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cicero } trufflehog-3.60.0/pkg/detectors/cicero/cicero_test.go000066400000000000000000000054461451332102400226240ustar00rootroot00000000000000//go:build detectors // +build detectors package cicero import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCicero_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CICERO") inactiveSecret := testSecrets.MustGetField("CICERO_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 cicero secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cicero, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cicero 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_Cicero, 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("Cicero.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cicero.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) } } }) } } trufflehog-3.60.0/pkg/detectors/circleci/000077500000000000000000000000001451332102400202725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/circleci/circleci.go000066400000000000000000000035461451332102400224060ustar00rootroot00000000000000package circleci import ( "context" "net/http" "regexp" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/common" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"circle"}) + `([a-fA-F0-9]{40})`) ) // 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{"circle"} } // FromData will find and optionally verify Circle 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) for _, match := range matches { token := match[1] s := detectors.Result{ DetectorType: detectorspb.DetectorType_Circle, Raw: []byte(token), } if verify { client := common.SaneHttpClient() // https://circleci.com/docs/api/#authentication req, err := http.NewRequestWithContext(ctx, "GET", "https://circleci.com/api/v2/me", nil) if err != nil { continue } req.Header.Add("Accept", "application/json;") req.Header.Add("Circle-Token", token) res, err := client.Do(req) if err == nil { defer res.Body.Close() } if res != nil && res.StatusCode >= 200 && res.StatusCode < 300 { s.Verified = true } } if !s.Verified { if detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) { continue } } results = append(results, s) } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Circle } trufflehog-3.60.0/pkg/detectors/circleci/circleci_test.go000066400000000000000000000054301451332102400234370ustar00rootroot00000000000000//go:build detectors // +build detectors package circleci import ( "context" "fmt" "os" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCircleCI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CIRCLECI") secretInactive := testSecrets.MustGetField("CIRCLECI_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 circle secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Circle, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a circle secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Circle, 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("CircleCI.FromData() error = %v, wantErr %v", err, tt.wantErr) return } if os.Getenv("FORCE_PASS_DIFF") == "true" { return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CircleCI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clarifai/000077500000000000000000000000001451332102400202675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clarifai/clarifai.go000066400000000000000000000046071451332102400223770ustar00rootroot00000000000000package clarifai import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clarifai"}) + `\b([a-zA-Z0-9]{32})\b`) // could be an api key tied to an app or a personal access token (pat) ) // 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{"clarifai"} } // FromData will find and optionally verify Clarifai 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Clarifai, Raw: []byte(resMatch), } if verify { // test for api key req, err := http.NewRequestWithContext(ctx, "GET", "https://api.clarifai.com/v2/inputs", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Key %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } if !s1.Verified { // test for pat req, err := http.NewRequestWithContext(ctx, "GET", "https://api.clarifai.com/v2/users/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Key %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Clarifai } trufflehog-3.60.0/pkg/detectors/clarifai/clarifai_test.go000066400000000000000000000062421451332102400234330ustar00rootroot00000000000000//go:build detectors // +build detectors package clarifai import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClarifai_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } apiKey := testSecrets.MustGetField("CLARIFAI_API_KEY") inactiveApiKey := testSecrets.MustGetField("CLARIFAI_API_KEY_INACTIVE") pat := testSecrets.MustGetField("CLARIFAI_PAT") 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 clarifai api key %s within", apiKey)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Clarifai, Verified: true, }, }, wantErr: false, }, { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clarifai pat %s within", pat)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Clarifai, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clarifai secret %s within but unverified", inactiveApiKey)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Clarifai, 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("Clarifai.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Clarifai.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clearbit/000077500000000000000000000000001451332102400203025ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clearbit/clearbit.go000066400000000000000000000042261451332102400224220ustar00rootroot00000000000000package clearbit import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clearbit"}) + `\b([0-9a-z_]{35})\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{"clearbit"} } // FromData will find and optionally verify Clearbit 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Clearbit, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://person.clearbit.com/v1/people/email/alex@alexmaccaw.com", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Clearbit } trufflehog-3.60.0/pkg/detectors/clearbit/clearbit_test.go000066400000000000000000000054721451332102400234650ustar00rootroot00000000000000//go:build detectors // +build detectors package clearbit import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClearbit_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("CLEARBIT") inactiveSecret := testSecrets.MustGetField("CLEARBIT_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 clearbit secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Clearbit, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clearbit 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_Clearbit, 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("Clearbit.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Clearbit.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clickhelp/000077500000000000000000000000001451332102400204535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clickhelp/clickhelp.go000066400000000000000000000057551451332102400227540ustar00rootroot00000000000000package clickhelp import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. serverPat = regexp.MustCompile(`\b([0-9A-Za-z]{3,20}.try.clickhelp.co)\b`) emailPat = regexp.MustCompile(`\b([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-z]+)\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clickhelp"}) + `\b([0-9A-Za-z]{24})\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{"clickhelp"} } // FromData will find and optionally verify Clickhelp 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) serverMatches := serverPat.FindAllStringSubmatch(dataStr, -1) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range serverMatches { if len(match) != 2 { continue } resServer := strings.TrimSpace(match[1]) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } resEmail := strings.TrimSpace(emailMatch[1]) for _, keyMatch := range keyMatches { if len(keyMatch) != 2 { continue } resKey := strings.TrimSpace(keyMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ClickHelp, Raw: []byte(resServer), RawV2: []byte(resServer + resEmail), } if verify { data := fmt.Sprintf("%s:%s", resEmail, resKey) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/api/v1/projects", resServer), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resServer, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ClickHelp } trufflehog-3.60.0/pkg/detectors/clickhelp/clickhelp_test.go000066400000000000000000000060311451332102400237770ustar00rootroot00000000000000//go:build detectors // +build detectors package clickhelp import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClickhelp_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) } email := testSecrets.MustGetField("SCANNERS_EMAIL") server := testSecrets.MustGetField("CLICKHELP_SERVER") key := testSecrets.MustGetField("CLICKHELP") inactiveKey := testSecrets.MustGetField("CLICKHELP_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 clickhelp secret %s within clickhelp email %s and clickhelp server %s", key, email, server)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClickHelp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clickhelp secret %s within clickhelp email %s and clickhelp server %s but not valid", inactiveKey, email, server)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClickHelp, 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("Clickhelp.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Clickhelp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clicksendsms/000077500000000000000000000000001451332102400211775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clicksendsms/clicksendsms.go000066400000000000000000000051111451332102400242060ustar00rootroot00000000000000package clicksendsms import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(common.UUIDPatternUpperCase) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"sms"}) + common.EmailPattern) ) // 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{"clicksendsms"} } // FromData will find and optionally verify ClickSendsms 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { resIdMatch := strings.TrimSpace(idMatch[0][strings.LastIndex(idMatch[0], " ")+1:]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ClickSendsms, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { data := fmt.Sprintf("%s:%s", resIdMatch, resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://rest.clicksend.com/v3/account", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ClickSendsms } trufflehog-3.60.0/pkg/detectors/clicksendsms/clicksendsms_test.go000066400000000000000000000057311451332102400252550ustar00rootroot00000000000000//go:build detectors // +build detectors package clicksendsms import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClickSendsms_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLICKSENDSMS_TOKEN") inactiveSecret := testSecrets.MustGetField("CLICKSENDSMS_INACTIVE") email := testSecrets.MustGetField("CLICKSENDSMS_EMAIL") 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 clicksendsms secret %s within clicksendsmsemail %s", secret, email)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClickSendsms, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clicksendsms secret %s within clicksendsmsemail %s but not valid", inactiveSecret, email)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClickSendsms, 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("ClickSendsms.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ClickSendsms.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clickuppersonaltoken/000077500000000000000000000000001451332102400227545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clickuppersonaltoken/clickuppersonaltoken.go000066400000000000000000000042221451332102400275420ustar00rootroot00000000000000package clickuppersonaltoken import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clickup"}) + `\b(pk_[0-9]{8}_[0-9A-Z]{32})\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{"clickup"} } // FromData will find and optionally verify ClickupPersonalToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ClickupPersonalToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.clickup.com/api/v2/user", nil) if err != nil { continue } req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ClickupPersonalToken } trufflehog-3.60.0/pkg/detectors/clickuppersonaltoken/clickuppersonaltoken_test.go000066400000000000000000000056621451332102400306120ustar00rootroot00000000000000//go:build detectors // +build detectors package clickuppersonaltoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClickupPersonalToken_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("CLICKUPPERSONALTOKEN") inactiveSecret := testSecrets.MustGetField("CLICKUPPERSONALTOKEN_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 clickuppersonaltoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClickupPersonalToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clickuppersonaltoken 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_ClickupPersonalToken, 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("ClickupPersonalToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ClickupPersonalToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cliengo/000077500000000000000000000000001451332102400201355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cliengo/cliengo.go000066400000000000000000000041531451332102400221070ustar00rootroot00000000000000package cliengo import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cliengo"}) + `\b([0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})\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{"cliengo"} } // FromData will find and optionally verify Cliengo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cliengo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cliengo.com/1.0/account?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cliengo } trufflehog-3.60.0/pkg/detectors/cliengo/cliengo_test.go000066400000000000000000000053721451332102400231520ustar00rootroot00000000000000//go:build detectors // +build detectors package cliengo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCliengo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLIENGO") inactiveSecret := testSecrets.MustGetField("CLIENGO_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 cliengo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cliengo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cliengo 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_Cliengo, 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("Cliengo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cliengo.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/clinchpad/000077500000000000000000000000001451332102400204425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clinchpad/clinchpad.go000066400000000000000000000043771451332102400227310ustar00rootroot00000000000000package clinchpad import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clinchpad"}) + `\b([a-z0-9]{32})\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{"clinchpad"} } // FromData will find and optionally verify Clinchpad 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Clinchpad, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("api-key:%s", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://www.clinchpad.com/api/v1/pipelines", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Clinchpad } trufflehog-3.60.0/pkg/detectors/clinchpad/clinchpad_test.go000066400000000000000000000055121451332102400237600ustar00rootroot00000000000000//go:build detectors // +build detectors package clinchpad import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClinchpad_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLINCHPAD_TOKEN") inactiveSecret := testSecrets.MustGetField("CLINCHPAD_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 clinchpad secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Clinchpad, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clinchpad 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_Clinchpad, 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("Clinchpad.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Clinchpad.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clockify/000077500000000000000000000000001451332102400203205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clockify/clockify.go000066400000000000000000000042151451332102400224540ustar00rootroot00000000000000package clockify import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clockify"}) + `\b([a-zA-Z0-9]{48})\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{"clockify"} } // FromData will find and optionally verify Clockify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Clockify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.clockify.me/api/v1/user", nil) if err != nil { continue } req.Header.Add("content-type", "application/json") req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Clockify } trufflehog-3.60.0/pkg/detectors/clockify/clockify_test.go000066400000000000000000000054721451332102400235210ustar00rootroot00000000000000//go:build detectors // +build detectors package clockify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClockify_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("CLOCKIFY") inactiveSecret := testSecrets.MustGetField("CLOCKIFY_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 clockify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Clockify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clockify 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_Clockify, 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("Clockify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Clockify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/clockworksms/000077500000000000000000000000001451332102400212365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clockworksms/clockworksms.go000066400000000000000000000051421451332102400243100ustar00rootroot00000000000000package clockworksms import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. userKeyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clockwork", "textanywhere"}) + `\b([0-9]{5})\b`) tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clockwork", "textanywhere"}) + `\b([0-9a-zA-Z]{24})\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{"clockworksms", "textanywhere"} } // FromData will find and optionally verify Clockworksms 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) userKeyMatches := userKeyPat.FindAllStringSubmatch(dataStr, -1) tokenMatches := tokenPat.FindAllStringSubmatch(dataStr, -1) for _, match := range userKeyMatches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, tokenMatch := range tokenMatches { if len(tokenMatch) != 2 { continue } tokenRes := strings.TrimSpace(tokenMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ClockworkSMS, Raw: []byte(resMatch), RawV2: []byte(resMatch + tokenRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.textanywhere.com/API/v1.0/REST/status", nil) if err != nil { continue } req.Header.Add("user_key", resMatch) req.Header.Add("access_token", tokenRes) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ClockworkSMS } trufflehog-3.60.0/pkg/detectors/clockworksms/clockworksms_test.go000066400000000000000000000057471451332102400253620ustar00rootroot00000000000000//go:build detectors // +build detectors package clockworksms import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClockworksms_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } userKey := testSecrets.MustGetField("CLOCKWORKSMS_USERKEY") token := testSecrets.MustGetField("CLOCKWORKSMS_TOKEN") inactiveToken := testSecrets.MustGetField("CLOCKWORKSMS_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 clockworksms secret %s and clockworksms token %s within", userKey, token)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClockworkSMS, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clockworksms secret %s and clockworksms token %s within but not valid", userKey, inactiveToken)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClockworkSMS, 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("Clockworksms.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Clockworksms.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) } } }) } } trufflehog-3.60.0/pkg/detectors/closecrm/000077500000000000000000000000001451332102400203245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/closecrm/close.go000066400000000000000000000043541451332102400217660ustar00rootroot00000000000000package closecrm import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(api_[a-z0-9A-Z.]{45})\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{"close"} } // FromData will find and optionally verify Close 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Close, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.close.com/api/v1/me/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Close } trufflehog-3.60.0/pkg/detectors/closecrm/close_test.go000066400000000000000000000054451451332102400230270ustar00rootroot00000000000000//go:build detectors // +build detectors package closecrm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClose_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOSE_TOKEN") inactiveSecret := testSecrets.MustGetField("CLOSE_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 close secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Close, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a close 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_Close, 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("Close.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Close.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudconvert/000077500000000000000000000000001451332102400212245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudconvert/cloudconvert.go000066400000000000000000000043751451332102400242730ustar00rootroot00000000000000package cloudconvert import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudconvert"}) + common.BuildRegexJWT("30,34", "200,500", "600,700")) ) // 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{"cloudconvert"} } // FromData will find and optionally verify CloudConvert 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CloudConvert, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloudconvert.com/v2/users/me", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.cloudconvert+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CloudConvert } trufflehog-3.60.0/pkg/detectors/cloudconvert/cloudconvert_test.go000066400000000000000000000055421451332102400253270ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudconvert import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudConvert_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOUDCONVERT") inactiveSecret := testSecrets.MustGetField("CLOUDCONVERT_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 cloudconvert secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudConvert, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudconvert 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_CloudConvert, 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("CloudConvert.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CloudConvert.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudelements/000077500000000000000000000000001451332102400213605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudelements/cloudelements.go000066400000000000000000000051031451332102400245510ustar00rootroot00000000000000package cloudelements import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudelements"}) + `\b([a-zA-Z0-9]{43})\b`) orgPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudelements"}) + `\b([a-z0-9]{32})\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{"cloudelements"} } // FromData will find and optionally verify CloudElements 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) orgMatches := orgPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, orgMatch := range orgMatches { if len(orgMatch) != 2 { continue } resOrgMatch := strings.TrimSpace(orgMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CloudElements, Raw: []byte(resMatch), RawV2: []byte(resMatch + resOrgMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://staging.cloud-elements.com/elements/api-v2/accounts", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("User %s, Organization %s", resMatch, resOrgMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CloudElements } trufflehog-3.60.0/pkg/detectors/cloudelements/cloudelements_test.go000066400000000000000000000057161451332102400256220ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudelements import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudElements_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOUDELEMENTS") org := testSecrets.MustGetField("CLOUDELEMENTS_ORG") inactiveSecret := testSecrets.MustGetField("CLOUDELEMENTS_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 cloudelements secret %s within cloudelements %s", secret, org)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudElements, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudelements secret %s within cloudelements %s but not valid", inactiveSecret, org)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudElements, 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("CloudElements.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CloudElements.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudflareapitoken/000077500000000000000000000000001451332102400223705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudflareapitoken/cloudflareapitoken.go000066400000000000000000000037631451332102400266030ustar00rootroot00000000000000package cloudflareapitoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudflare"}) + `\b([A-Za-z0-9_-]{40})\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{"cloudflare"} } // FromData will find and optionally verify CloudflareApiToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CloudflareApiToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloudflare.com/client/v4/user/tokens/verify", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CloudflareApiToken } trufflehog-3.60.0/pkg/detectors/cloudflareapitoken/cloudflareapitoken_test.go000066400000000000000000000064331451332102400276370ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudflareapitoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudflareApiToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOUDFLARE_API_TOKEN") inactiveSecret := testSecrets.MustGetField("CLOUDFLARE_API_INACTIVE") secret2 := testSecrets.MustGetField("CLOUDFLARE_API_TOKEN2") 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 cloudflareapitoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareApiToken, Verified: true, }, }, wantErr: false, }, { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudflareapitoken secret %s within", secret2)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareApiToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudflareapitoken secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareApiToken, 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("CloudflareApiToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CloudflareApiToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudflarecakey/000077500000000000000000000000001451332102400216525ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudflarecakey/cloudflarecakey.go000066400000000000000000000041321451332102400253360ustar00rootroot00000000000000package cloudflarecakey import ( "context" // "fmt" // "log" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudflare"}) + `\b(v[A-Za-z0-9._-]{173,})\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{"cloudflare"} } // FromData will find and optionally verify CloudflareCaKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CloudflareCaKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloudflare.com/client/v4/certificates?zone_id=a", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("user-agent", "curl/7.68.0") // pretend to be from curl so we do not wait 100+ seconds -> nice try did not work req.Header.Add("X-Auth-User-Service-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CloudflareCaKey } trufflehog-3.60.0/pkg/detectors/cloudflarecakey/cloudflarecakey_test.go000066400000000000000000000055271451332102400264060ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudflarecakey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudflareCaKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOUDFLARE_ORIGIN_CA_KEY") inactiveSecret := testSecrets.MustGetField("CLOUDFLARE_ORIGIN_CA_KEY_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 cloudflarecakey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareCaKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudflarecakey secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareCaKey, 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("CloudflareCaKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CloudflareCaKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudflareglobalapikey/000077500000000000000000000000001451332102400232215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudflareglobalapikey/cloudflareglobalapikey.go000066400000000000000000000053631451332102400302630ustar00rootroot00000000000000package cloudflareglobalapikey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() apiKeyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudflare"}) + `([A-Za-z0-9_-]{37})`) // email pattern thanks https://golangcode.com/validate-an-email-address/ // emailPat = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") emailPat = regexp.MustCompile(`\b([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)\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{"cloudflare"} } // FromData will find and optionally verify CloudflareGlobalApiKey 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) apiKeyMatches := apiKeyPat.FindAllStringSubmatch(dataStr, -1) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) for _, apiKeyMatch := range apiKeyMatches { if len(apiKeyMatch) != 2 { continue } apiKeyRes := strings.TrimSpace(apiKeyMatch[1]) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } emailRes := strings.TrimSpace(emailMatch[1]) if detectors.IsKnownFalsePositive(apiKeyRes, detectors.DefaultFalsePositives, true) { // wait- (apiKeyRes, email) might be false positive does not mean (apiKeyRes, another_email) is ? continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CloudflareGlobalApiKey, Redacted: emailRes, Raw: []byte(apiKeyRes), RawV2: []byte(apiKeyRes + emailRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloudflare.com/client/v4/user", nil) if err != nil { continue } req.Header.Add("X-Auth-Email", emailRes) req.Header.Add("X-Auth-Key", apiKeyRes) req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CloudflareGlobalApiKey } trufflehog-3.60.0/pkg/detectors/cloudflareglobalapikey/cloudflareglobalapikey_test.go000066400000000000000000000062121451332102400313140ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudflareglobalapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudflareGlobalApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } globalApiKey := testSecrets.MustGetField("CLOUDFLARE_GLOBAL_API_KEY") globalApiKeyEmail := testSecrets.MustGetField("CLOUDFLARE_GLOBAL_API_KEY_EMAIL") inactiveglobalApiKey := testSecrets.MustGetField("CLOUDFLARE_GLOBAL_API_KEY_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 cloudflare globalapikey secret %s within with email %s", globalApiKey, globalApiKeyEmail)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareGlobalApiKey, Redacted: globalApiKeyEmail, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudflare globalapikey secret %s with email %s within but unverified", inactiveglobalApiKey, globalApiKeyEmail)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudflareGlobalApiKey, Redacted: globalApiKeyEmail, 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("CloudflareGlobalApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CloudflareGlobalApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudimage/000077500000000000000000000000001451332102400206265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudimage/cloudimage.go000066400000000000000000000045261451332102400232750ustar00rootroot00000000000000package cloudimage import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudimage"}) + `\b([a-z0-9_]{30})\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{"cloudimage"} } // FromData will find and optionally verify CloudImage 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CloudImage, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"scope":"urls","urls":["/sample.li/paris.jpg?width=400","/sample.li/flat.jpg?width=400"]} `) timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequest("POST", "https://api.cloudimage.com/invalidate", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-Client-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CloudImage } trufflehog-3.60.0/pkg/detectors/cloudimage/cloudimage_test.go000066400000000000000000000054301451332102400243270ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudimage import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudImage_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOUDIMAGE") inactiveSecret := testSecrets.MustGetField("CLOUDIMAGE_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 cloudimage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CloudImage, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudimage 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_CloudImage, 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("CloudImage.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CloudImage.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/cloudmersive/000077500000000000000000000000001451332102400212165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudmersive/cloudmersive.go000066400000000000000000000044211451332102400242470ustar00rootroot00000000000000package cloudmersive import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudmersive"}) + `\b([a-z0-9-]{36})\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{"cloudmersive"} } // FromData will find and optionally verify Cloudmersive 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cloudmersive, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"AddressString":"string","CapitalizationMode":"string"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.cloudmersive.com/validate/address/parse", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cloudmersive } trufflehog-3.60.0/pkg/detectors/cloudmersive/cloudmersive_test.go000066400000000000000000000055421451332102400253130ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudmersive import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudmersive_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("CLOUDMERSIVE") inactiveSecret := testSecrets.MustGetField("CLOUDMERSIVE_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 cloudmersive secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cloudmersive, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudmersive 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_Cloudmersive, 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("Cloudmersive.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cloudmersive.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudplan/000077500000000000000000000000001451332102400204765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudplan/cloudplan.go000066400000000000000000000041361451332102400230120ustar00rootroot00000000000000package cloudplan import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudplan"}) + `\b([A-Z0-9-]{32})\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{"cloudplan"} } // FromData will find and optionally verify Cloudplan 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cloudplan, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloudplan.biz/api/user/me", nil) if err != nil { continue } req.Header.Add("session_id", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cloudplan } trufflehog-3.60.0/pkg/detectors/cloudplan/cloudplan_test.go000066400000000000000000000054631451332102400240550ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudplan import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudplan_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("CLOUDPLAN") inactiveSecret := testSecrets.MustGetField("CLOUDPLAN_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 cloudplan secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cloudplan, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudplan 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_Cloudplan, 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("Cloudplan.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cloudplan.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloudsmith/000077500000000000000000000000001451332102400206705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloudsmith/cloudsmith.go000066400000000000000000000042241451332102400233740ustar00rootroot00000000000000package cloudsmith import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloudsmith"}) + `\b([0-9a-f]{40})\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{"cloudsmith"} } // FromData will find and optionally verify Cloudsmith 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cloudsmith, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloudsmith.io/v1/user/self/", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cloudsmith } trufflehog-3.60.0/pkg/detectors/cloudsmith/cloudsmith_test.go000066400000000000000000000055161451332102400244400ustar00rootroot00000000000000//go:build detectors // +build detectors package cloudsmith import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloudsmith_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CLOUDSMITH") inactiveSecret := testSecrets.MustGetField("CLOUDSMITH_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 cloudsmith secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cloudsmith, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloudsmith 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_Cloudsmith, 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("Cloudsmith.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cloudsmith.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloverly/000077500000000000000000000000001451332102400203545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloverly/cloverly.go000066400000000000000000000042051451332102400225430ustar00rootroot00000000000000package cloverly import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloverly"}) + `\b([a-z0-9:_]{28})\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{"cloverly"} } // FromData will find and optionally verify Cloverly 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cloverly, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.cloverly.com/2019-03-beta/account", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cloverly } trufflehog-3.60.0/pkg/detectors/cloverly/cloverly_test.go000066400000000000000000000054721451332102400236110ustar00rootroot00000000000000//go:build detectors // +build detectors package cloverly import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloverly_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("CLOVERLY") inactiveSecret := testSecrets.MustGetField("CLOVERLY_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 cloverly secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cloverly, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloverly 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_Cloverly, 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("Cloverly.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cloverly.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cloze/000077500000000000000000000000001451332102400176315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cloze/cloze.go000066400000000000000000000047521451332102400213040ustar00rootroot00000000000000package cloze import ( "context" "net/http" "net/url" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloze"}) + `\b([0-9a-f]{32})\b`) emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cloze"}) + `\b([\w\.-]+@[\w-]+\.[\w\.-]{2,5})\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{"cloze"} } // FromData will find and optionally verify Cloze 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) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } resEmailMatch := strings.TrimSpace(emailMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Cloze, Raw: []byte(resMatch), } if verify { payload := url.Values{} payload.Add("user", resEmailMatch) payload.Add("api_key", resMatch) req, err := http.NewRequest("GET", "https://api.cloze.com/v1/profile?"+payload.Encode(), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Cloze } trufflehog-3.60.0/pkg/detectors/cloze/cloze_test.go000066400000000000000000000055141451332102400223400ustar00rootroot00000000000000//go:build detectors // +build detectors package cloze import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCloze_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } email := testSecrets.MustGetField("CLOZE_EMAIL") secret := testSecrets.MustGetField("CLOZE") inactiveSecret := testSecrets.MustGetField("CLOZE_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 cloze user %s with cloze secret %s within", email, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cloze, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cloze user %s with cloze secret %s within but not valid", email, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Cloze, 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("Cloze.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Cloze.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/clustdoc/000077500000000000000000000000001451332102400203355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/clustdoc/clustdoc.go000066400000000000000000000042471451332102400225130ustar00rootroot00000000000000package clustdoc import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"clustdoc"}) + `\b([0-9a-zA-Z]{60})\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{"clustdoc"} } // FromData will find and optionally verify ClustDoc 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ClustDoc, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://clustdoc.com/api/users", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ClustDoc } trufflehog-3.60.0/pkg/detectors/clustdoc/clustdoc_test.go000066400000000000000000000054721451332102400235530ustar00rootroot00000000000000//go:build detectors // +build detectors package clustdoc import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestClustDoc_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("CLUSTDOC") inactiveSecret := testSecrets.MustGetField("CLUSTDOC_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 clustdoc secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ClustDoc, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a clustdoc 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_ClustDoc, 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("ClustDoc.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ClustDoc.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) } } }) } } trufflehog-3.60.0/pkg/detectors/codacy/000077500000000000000000000000001451332102400177575ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/codacy/codacy.go000066400000000000000000000041721451332102400215540ustar00rootroot00000000000000package codacy import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"codacy"}) + `\b([0-9A-Za-z]{20})\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{"codacy"} } // FromData will find and optionally verify Codacy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Codacy, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.codacy.com/api/v3/user", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("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 } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Codacy } trufflehog-3.60.0/pkg/detectors/codacy/codacy_test.go000066400000000000000000000054461451332102400226200ustar00rootroot00000000000000//go:build detectors // +build detectors package codacy import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCodacy_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("CODACY") inactiveSecret := testSecrets.MustGetField("CODACY_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 codacy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Codacy, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a codacy 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_Codacy, 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("Codacy.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Codacy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/codeclimate/000077500000000000000000000000001451332102400207665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/codeclimate/codeclimate.go000066400000000000000000000043051451332102400235700ustar00rootroot00000000000000package codeclimate import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"codeclimate"}) + `\b([a-f0-9]{40})\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{"codeclimate"} } // FromData will find and optionally verify Codeclimate 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Codeclimate, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.codeclimate.com/v1/user", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.api+json") req.Header.Add("Authorization", fmt.Sprintf("Token token=%s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Codeclimate } trufflehog-3.60.0/pkg/detectors/codeclimate/codeclimate_test.go000066400000000000000000000054561451332102400246370ustar00rootroot00000000000000package codeclimate import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCodeclimate_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CODECLIMATE") inactiveSecret := testSecrets.MustGetField("CODECLIMATE_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 codeclimate secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Codeclimate, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a codeclimate 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_Codeclimate, 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("Codeclimate.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Codeclimate.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) } } }) } } trufflehog-3.60.0/pkg/detectors/codemagic/000077500000000000000000000000001451332102400204305ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/codemagic/codemagic.go000066400000000000000000000042501451332102400226730ustar00rootroot00000000000000package codemagic import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"codemagic"}) + common.BuildRegex(common.AlphaNumPattern, "_", 43)) ) // 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{"codemagic"} } // FromData will find and optionally verify Codemagic 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Codemagic, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.codemagic.io/apps", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-auth-token", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Codemagic } trufflehog-3.60.0/pkg/detectors/codemagic/codemagic_test.go000066400000000000000000000055041451332102400237350ustar00rootroot00000000000000//go:build detectors // +build detectors package codemagic import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCodemagic_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CODEMAGIC") inactiveSecret := testSecrets.MustGetField("CODEMAGIC_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 codemagic secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Codemagic, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a codemagic 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_Codemagic, 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("Codemagic.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Codemagic.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) } } }) } } trufflehog-3.60.0/pkg/detectors/codequiry/000077500000000000000000000000001451332102400205215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/codequiry/codequiry.go000066400000000000000000000041341451332102400230560ustar00rootroot00000000000000package codequiry import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"codequiry"}) + `\b([a-zA-Z-0-9]{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{"codequiry"} } // FromData will find and optionally verify Codequiry 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Codequiry, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://codequiry.com/api/v1/checks", nil) if err != nil { continue } req.Header.Add("apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Codequiry } trufflehog-3.60.0/pkg/detectors/codequiry/codequiry_test.go000066400000000000000000000055041451332102400241170ustar00rootroot00000000000000//go:build detectors // +build detectors package codequiry import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCodequiry_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CODEQUIRY") inactiveSecret := testSecrets.MustGetField("CODEQUIRY_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 codequiry secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Codequiry, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a codequiry 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_Codequiry, 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("Codequiry.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Codequiry.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) } } }) } } trufflehog-3.60.0/pkg/detectors/coinapi/000077500000000000000000000000001451332102400201375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/coinapi/coinapi.go000066400000000000000000000041241451332102400221110ustar00rootroot00000000000000package coinapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"coinapi"}) + `\b([A-Z0-9-]{36})\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{"coinapi"} } // FromData will find and optionally verify CoinApi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CoinApi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://rest.coinapi.io/v1/exchanges", nil) if err != nil { continue } req.Header.Add("X-CoinAPI-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CoinApi } trufflehog-3.60.0/pkg/detectors/coinapi/coinapi_test.go000066400000000000000000000054601451332102400231540ustar00rootroot00000000000000//go:build detectors // +build detectors package coinapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCoinApi_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("COINAPI") inactiveSecret := testSecrets.MustGetField("COINAPI_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 coinapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CoinApi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a coinapi 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_CoinApi, 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("CoinApi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CoinApi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/coinbase/000077500000000000000000000000001451332102400203005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/coinbase/coinbase.go000066400000000000000000000041721451332102400224160ustar00rootroot00000000000000package coinbase import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"coinbase"}) + `\b([a-zA-Z-0-9]{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{"coinbase"} } // FromData will find and optionally verify Coinbase 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Coinbase, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.coinbase.com/v2/user", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Coinbase } trufflehog-3.60.0/pkg/detectors/coinbase/coinbase_test.go000066400000000000000000000060421451332102400234530ustar00rootroot00000000000000//go:build detectors // +build detectors package coinbase // import ( // "context" // "fmt" // "testing" // "time" // "github.com/kylelemons/godebug/pretty" // "github.com/trufflesecurity/trufflehog/v3/pkg/common" // "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" // "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" // ) // func TestCoinbase_FromChunk(t *testing.T) { // ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) // defer cancel() // testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") // if err != nil { // t.Fatalf("could not get test secrets from GCP: %s", err) // } // secret := testSecrets.MustGetField("COINBASE_TOKEN") // inactiveSecret := testSecrets.MustGetField("COINBASE_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 coinbase secret %s within", secret)), // verify: true, // }, // want: []detectors.Result{ // { // DetectorType: detectorspb.DetectorType_Coinbase, // Verified: true, // }, // }, // wantErr: false, // }, // { // name: "found, unverified", // s: Scanner{}, // args: args{ // ctx: context.Background(), // data: []byte(fmt.Sprintf(""You can find a coinbase 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_Coinbase, // 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("Coinbase.FromData() error = %v, wantErr %v", err, tt.wantErr) // return // } // for i := range got { // if len(got[i].Raw) == 0 { // t.Fatal("no raw secret present") // } // got[i].Raw = nil // } // if diff := pretty.Compare(got, tt.want); diff != "" { // t.Errorf("Coinbase.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) { // for n := 0; n < b.N; n++ { // s.FromData(ctx, false, data) // } // }) // } // } trufflehog-3.60.0/pkg/detectors/coinlayer/000077500000000000000000000000001451332102400205025ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/coinlayer/coinlayer.go000066400000000000000000000047551451332102400230310ustar00rootroot00000000000000package coinlayer import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"coinlayer"}) + `\b([a-z0-9]{32})\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{"coinlayer"} } // FromData will find and optionally verify Coinlayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Coinlayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.coinlayer.com/api/livelive?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"success": true`) || strings.Contains(bodyString, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Coinlayer } trufflehog-3.60.0/pkg/detectors/coinlayer/coinlayer_test.go000066400000000000000000000055041451332102400240610ustar00rootroot00000000000000//go:build detectors // +build detectors package coinlayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCoinlayer_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("COINLAYER") inactiveSecret := testSecrets.MustGetField("COINLAYER_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 coinlayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Coinlayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a coinlayer 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_Coinlayer, 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("Coinlayer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Coinlayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/coinlib/000077500000000000000000000000001451332102400201345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/coinlib/coinlib.go000066400000000000000000000041201451332102400220770ustar00rootroot00000000000000package coinlib import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"coinlib"}) + `\b([a-z0-9]{16})\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{"coinlib"} } // FromData will find and optionally verify Coinlib 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Coinlib, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://coinlib.io/api/v1/global?key=%s&pref=EUR", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Coinlib } trufflehog-3.60.0/pkg/detectors/coinlib/coinlib_test.go000066400000000000000000000054601451332102400231460ustar00rootroot00000000000000//go:build detectors // +build detectors package coinlib import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCoinlib_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("COINLIB") inactiveSecret := testSecrets.MustGetField("COINLIB_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 coinlib secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Coinlib, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a coinlib 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_Coinlib, 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("Coinlib.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Coinlib.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) } } }) } } trufflehog-3.60.0/pkg/detectors/coinmarketcap/000077500000000000000000000000001451332102400213355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/coinmarketcap/coinmarketcap.go000066400000000000000000000044501451332102400245070ustar00rootroot00000000000000package coinmarketcap import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"coinmarketcap", "CMC"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"coinmarketcap", "CMC"} } // FromData will find and optionally verify Coinmarketcap 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CoinMarketCap, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?start=1&limit=5000&convert=USD", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("X-CMC_PRO_API_KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CoinMarketCap } trufflehog-3.60.0/pkg/detectors/coinmarketcap/coinmarketcap_test.go000066400000000000000000000055541451332102400255540ustar00rootroot00000000000000//go:build detectors // +build detectors package coinmarketcap import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCoinmarketcap_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COINMARKETCAP") inactiveSecret := testSecrets.MustGetField("COINMARKETCAP_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 coinmarketcap secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CoinMarketCap, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a coinmarketcap 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_CoinMarketCap, 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("Coinmarketcap.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Coinmarketcap.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) } } }) } } trufflehog-3.60.0/pkg/detectors/collect2/000077500000000000000000000000001451332102400202245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/collect2/collect2.go000066400000000000000000000042601451332102400222640ustar00rootroot00000000000000package collect2 import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"collect2"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"collect2"} } // FromData will find and optionally verify Collect2 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Collect2, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://collect2.com/api/%s/datarecord/", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Collect2 } trufflehog-3.60.0/pkg/detectors/collect2/collect2_test.go000066400000000000000000000054721451332102400233310ustar00rootroot00000000000000//go:build detectors // +build detectors package collect2 import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCollect2_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COLLECT2") inactiveSecret := testSecrets.MustGetField("COLLECT2_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 collect2 secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Collect2, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a collect2 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_Collect2, 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("Collect2.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Collect2.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) } } }) } } trufflehog-3.60.0/pkg/detectors/column/000077500000000000000000000000001451332102400200125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/column/column.go000066400000000000000000000044541451332102400216450ustar00rootroot00000000000000package column import ( "context" "encoding/base64" "fmt" "log" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"column"}) + `\b((?:test|live)_[a-zA-Z0-9]{27})\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{"column"} } // FromData will find and optionally verify Column 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) log.Println("resmatch: " + resMatch) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Column, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.column.com/entities", nil) if err != nil { continue } // req.SetBasicAuth(resMatch, "") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(resMatch)))) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { s1.Verified = false // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. // if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { // continue // } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Column } trufflehog-3.60.0/pkg/detectors/column/column_test.go000066400000000000000000000053671451332102400227100ustar00rootroot00000000000000//go:build detectors // +build detectors package column import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestColumn_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COLUMN_ACTIVE") inactiveSecret := testSecrets.MustGetField("COLUMN_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 column secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Column, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a column 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_Column, 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("Column.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Column.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/commercejs/000077500000000000000000000000001451332102400206445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/commercejs/commercejs.go000066400000000000000000000041451451332102400233260ustar00rootroot00000000000000package commercejs import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"commercejs"}) + `\b([a-z0-9_]{48})\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{"commercejs"} } // FromData will find and optionally verify CommerceJS 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CommerceJS, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.chec.io/v1/categories", nil) if err != nil { continue } req.Header.Add("X-Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CommerceJS } trufflehog-3.60.0/pkg/detectors/commercejs/commercejs_test.go000066400000000000000000000055161451332102400243700ustar00rootroot00000000000000//go:build detectors // +build detectors package commercejs import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCommerceJS_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("COMMERCEJS") inactiveSecret := testSecrets.MustGetField("COMMERCEJS_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 commercejs secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CommerceJS, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a commercejs 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_CommerceJS, 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("CommerceJS.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CommerceJS.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) } } }) } } trufflehog-3.60.0/pkg/detectors/commodities/000077500000000000000000000000001451332102400210315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/commodities/commodities.go000066400000000000000000000046151451332102400237020ustar00rootroot00000000000000package commodities import ( "context" "io" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"commodities"}) + `\b([a-zA-Z0-9]{60})\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{"commodities"} } // FromData will find and optionally verify Commodities 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Commodities, Raw: []byte(resMatch), } if verify { client.Timeout = 5 * time.Second req, err := http.NewRequestWithContext(ctx, "GET", "https://commodities-api.com/api/latest?access_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"success":true`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Commodities } trufflehog-3.60.0/pkg/detectors/commodities/commodities_test.go000066400000000000000000000055301451332102400247360ustar00rootroot00000000000000//go:build detectors // +build detectors package commodities import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCommodities_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COMMODITIES") inactiveSecret := testSecrets.MustGetField("COMMODITIES_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 commodities secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Commodities, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a commodities 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_Commodities, 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("Commodities.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Commodities.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) } } }) } } trufflehog-3.60.0/pkg/detectors/companyhub/000077500000000000000000000000001451332102400206625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/companyhub/companyhub.go000066400000000000000000000050701451332102400233600ustar00rootroot00000000000000package companyhub import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"companyhub"}) + `\b([0-9a-zA-Z]{20})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"companyhub"}) + `\b([a-zA-Z0-9$%^=-]{4,32})\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{"companyhub"} } // FromData will find and optionally verify CompanyHub 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CompanyHub, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.companyhub.com/v1/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("%s %s", resIdMatch, resMatch)) req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CompanyHub } trufflehog-3.60.0/pkg/detectors/companyhub/companyhub_test.go000066400000000000000000000056711451332102400244260ustar00rootroot00000000000000//go:build detectors // +build detectors package companyhub import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCompanyHub_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COMPANYHUB_TOKEN") inactiveSecret := testSecrets.MustGetField("COMPANYHUB_INACTIVE") user := testSecrets.MustGetField("COMPANYHUB_USER") 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 companyhub secret %s within companyhubuser %s", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CompanyHub, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a companyhub secret %s within companyhubuser %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CompanyHub, 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("CompanyHub.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CompanyHub.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) } } }) } } trufflehog-3.60.0/pkg/detectors/confluent/000077500000000000000000000000001451332102400205125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/confluent/confluent.go000066400000000000000000000052231451332102400230400ustar00rootroot00000000000000package confluent import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"confluent"}) + `\b([a-zA-Z0-9]{16})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"confluent"}) + `\b([a-zA-Z0-9\+\/]{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{"confluent"} } // FromData will find and optionally verify Confluent 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secret := range secretMatches { if len(secret) != 2 { continue } resSecret := strings.TrimSpace(secret[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Confluent, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { data := fmt.Sprintf("%s:%s", resMatch, resSecret) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.confluent.cloud/iam/v2/api-keys/"+resMatch, nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resSecret, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Confluent } trufflehog-3.60.0/pkg/detectors/confluent/confluent_test.go000066400000000000000000000056401451332102400241020ustar00rootroot00000000000000//go:build detectors // +build detectors package confluent import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestConfluent_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CONFLUENT_TOKEN") key := testSecrets.MustGetField("CONFLUENT_KEY") inactiveSecret := testSecrets.MustGetField("CONFLUENT_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 confluent secret %s within confluent %s", secret, key)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Confluent, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a confluent secret %s within confluent %s but not valid", inactiveSecret, key)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Confluent, 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("Confluent.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Confluent.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) } } }) } } trufflehog-3.60.0/pkg/detectors/contentfulpersonalaccesstoken/000077500000000000000000000000001451332102400246655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/contentfulpersonalaccesstoken/contentfulpersonalaccesstoken.go000066400000000000000000000036401451332102400333670ustar00rootroot00000000000000package contentfulpersonalaccesstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(`\b(CFPAT-[a-zA-Z0-9_\-]{43})\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{"CFPAT-"} } // FromData will find and optionally verify ContentfulDelivery 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) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range keyMatches { if len(match) != 2 { continue } keyRes := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ContentfulPersonalAccessToken, Raw: []byte(keyRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.contentful.com/organizations", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", keyRes)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(keyRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ContentfulPersonalAccessToken } trufflehog-3.60.0/pkg/detectors/contentfulpersonalaccesstoken/contentfulpersonalaccesstoken_test.go000066400000000000000000000057361451332102400344360ustar00rootroot00000000000000//go:build detectors // +build detectors package contentfulpersonalaccesstoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestContentfulPersonalAccessToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CONTENTFULPERSONALACCESSTOKEN") inactiveSecret := testSecrets.MustGetField("CONTENTFULPERSONALACCESSTOKEN_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 contentful secret %s within contentful https://api.contentful.com/organizations", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ContentfulPersonalAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a contentful secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ContentfulPersonalAccessToken, 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("ContentfulPersonalAccessToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ContentfulPersonalAccessToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/conversiontools/000077500000000000000000000000001451332102400217635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/conversiontools/conversiontools.go000066400000000000000000000045721451332102400255700ustar00rootroot00000000000000package conversiontools import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"conversiontools"}) + `\b(ey[a-zA-Z0-9_.]{157,165})\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{"conversiontools"} } // FromData will find and optionally verify ConversionTools 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ConversionTools, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{ "type": "convert.website_to_jpg", "options": { "url": "http://google.com", "images": "yes" }}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.conversiontools.io/v1/tasks", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ConversionTools } trufflehog-3.60.0/pkg/detectors/conversiontools/conversiontools_test.go000066400000000000000000000056001451332102400266200ustar00rootroot00000000000000//go:build detectors // +build detectors package conversiontools import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestConversionTools_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CONVERSIONTOOLS") inactiveSecret := testSecrets.MustGetField("CONVERSIONTOOLS_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 conversiontools secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ConversionTools, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a conversiontools 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_ConversionTools, 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("ConversionTools.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ConversionTools.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) } } }) } } trufflehog-3.60.0/pkg/detectors/convertapi/000077500000000000000000000000001451332102400206675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/convertapi/convertapi.go000066400000000000000000000041321451332102400233700ustar00rootroot00000000000000package convertapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"convertapi"}) + `\b([0-9a-zA-Z]{16})\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{"convertapi"} } // FromData will find and optionally verify ConvertApi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ConvertApi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://v2.convertapi.com/user?Secret=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ConvertApi } trufflehog-3.60.0/pkg/detectors/convertapi/convertapi_test.go000066400000000000000000000055161451332102400244360ustar00rootroot00000000000000//go:build detectors // +build detectors package convertapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestConvertApi_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CONVERTAPI") inactiveSecret := testSecrets.MustGetField("CONVERTAPI_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 convertapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ConvertApi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a convertapi 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_ConvertApi, 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("ConvertApi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ConvertApi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/convertkit/000077500000000000000000000000001451332102400207055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/convertkit/convertkit.go000066400000000000000000000041161451332102400234260ustar00rootroot00000000000000package convertkit import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"convertkit"}) + `\b([a-z0-9A-Z_]{22})\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{"convertkit"} } // FromData will find and optionally verify Convertkit 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Convertkit, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.convertkit.com/v3/forms?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Convertkit } trufflehog-3.60.0/pkg/detectors/convertkit/convertkit_test.go000066400000000000000000000055241451332102400244710ustar00rootroot00000000000000//go:build detectors // +build detectors package convertkit import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestConvertkit_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CONVERTKIT_TOKEN") inactiveSecret := testSecrets.MustGetField("CONVERTKIT_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 convertkit secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Convertkit, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a convertkit 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_Convertkit, 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("Convertkit.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Convertkit.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) } } }) } } trufflehog-3.60.0/pkg/detectors/convier/000077500000000000000000000000001451332102400201625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/convier/convier.go000066400000000000000000000046651451332102400221710ustar00rootroot00000000000000package convier import ( "context" "fmt" "io" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"convier"}) + `\b([0-9]{2}\|[a-zA-Z0-9]{40})\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{"convier"} } // FromData will find and optionally verify Convier 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Convier, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequest("POST", "https://convier.me/api/event", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"error":false`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Convier } trufflehog-3.60.0/pkg/detectors/convier/convier_test.go000066400000000000000000000053721451332102400232240ustar00rootroot00000000000000//go:build detectors // +build detectors package convier import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestConvier_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CONVIER") inactiveSecret := testSecrets.MustGetField("CONVIER_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 convier secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Convier, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a convier 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_Convier, 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("Convier.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Convier.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/copper/000077500000000000000000000000001451332102400200055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/copper/copper.go000066400000000000000000000052771451332102400216370ustar00rootroot00000000000000package copper import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"copper"}) + `\b([a-z0-9]{32})\b`) idPat = regexp.MustCompile(`\b([a-z0-9]{4,25}@[a-zA-Z0-9]{2,12}.[a-zA-Z0-9]{2,6})\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{"copper"} } // FromData will find and optionally verify Copper 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Copper, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { payload := strings.NewReader(`{ "page_size": 25, "sort_by": "name" }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.copper.com/developer_api/v1/tasks/search", payload) if err != nil { continue } req.Header.Add("X-PW-AccessToken", resMatch) req.Header.Add("X-PW-Application", "developer_api") req.Header.Add("X-PW-UserEmail", resIdMatch) req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Copper } trufflehog-3.60.0/pkg/detectors/copper/copper_test.go000066400000000000000000000055711451332102400226730ustar00rootroot00000000000000//go:build detectors // +build detectors package copper import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCopper_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COOPER_TOKEN") inactiveSecret := testSecrets.MustGetField("COOPER_INACTIVE") id := testSecrets.MustGetField("COOPER_ID") 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 copper secret %s within copperid %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Copper, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a copper secret %s within copperid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Copper, 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("Copper.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Copper.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) } } }) } } trufflehog-3.60.0/pkg/detectors/couchbase/000077500000000000000000000000001451332102400204515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/couchbase/couchbase.go000066400000000000000000000111761451332102400227420ustar00rootroot00000000000000package couchbase import ( "context" "fmt" "log" "regexp" "strings" "time" "unicode" "github.com/couchbase/gocb/v2" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. connectionStringPat = regexp.MustCompile(`\bcb\.[a-z0-9]+\.cloud\.couchbase\.com\b`) usernamePat = `?()/\+=\s\n` passwordPat = `^<>;.*&|ÂŖ\n\s` //passwordPat = regexp.MustCompile(`(?i)(?:pass|pwd)(?:.|[\n\r]){0,15}(\b[^<>;.*&|ÂŖ\n\s]{8,100}$)`) // passwordPat = regexp.MustCompile(`(?im)(?:pass|pwd)\S{0,40}?[:=\s]{1,3}[ '"=]{0,1}([^:?()/\+=\s\n]{4,40})\b`) ) func meetsCouchbasePasswordRequirements(password string) (string, bool) { var hasLower, hasUpper, hasNumber, hasSpecialChar bool for _, char := range password { switch { case unicode.IsLower(char): hasLower = true case unicode.IsUpper(char): hasUpper = true case unicode.IsNumber(char): hasNumber = true case unicode.IsPunct(char) || unicode.IsSymbol(char): hasSpecialChar = true } if hasLower && hasUpper && hasNumber && hasSpecialChar { return password, true } } return "", false } // 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{"couchbase://", "couchbases://"} } // FromData will find and optionally verify Couchbase 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) connectionStringMatches := connectionStringPat.FindAllStringSubmatch(dataStr, -1) // prepend 'couchbases://' to the connection string as the connection // string format is couchbases://cb.stuff.cloud.couchbase.com but the // cb.stuff.cloud.couchbase.com may be separated from the couchbases:// in codebases. for i, connectionStringMatch := range connectionStringMatches { connectionStringMatches[i][0] = "couchbases://" + connectionStringMatch[0] } usernameRegexState := common.UsernameRegexCheck(usernamePat) usernameMatches := usernameRegexState.Matches(data) passwordRegexState := common.PasswordRegexCheck(passwordPat) passwordMatches := passwordRegexState.Matches(data) for _, connectionStringMatch := range connectionStringMatches { resConnectionStringMatch := strings.TrimSpace(connectionStringMatch[0]) for _, resUsernameMatch := range usernameMatches { for _, resPasswordMatch := range passwordMatches { _, metPasswordRequirements := meetsCouchbasePasswordRequirements(resPasswordMatch) if !metPasswordRequirements { continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Couchbase, Raw: []byte(fmt.Sprintf("%s:%s@%s", resUsernameMatch, resPasswordMatch, resConnectionStringMatch)), } if verify { options := gocb.ClusterOptions{ Authenticator: gocb.PasswordAuthenticator{ Username: resUsernameMatch, Password: resPasswordMatch, }, } // Sets a pre-configured profile called "wan-development" to help avoid latency issues // when accessing Capella from a different Wide Area Network // or Availability Zone (e.g. your laptop). if err := options.ApplyProfile(gocb.ClusterConfigProfileWanDevelopment); err != nil { log.Fatal("apply profile err", err) } // Initialize the Connection cluster, err := gocb.Connect(resConnectionStringMatch, options) if err != nil { continue } // We'll ping the KV nodes in our cluster. pings, err := cluster.Ping(&gocb.PingOptions{ Timeout: time.Second * 5, }) if err != nil { continue } for _, ping := range pings.Services { for _, pingEndpoint := range ping { if pingEndpoint.State == gocb.PingStateOk { s1.Verified = true break } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resPasswordMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Couchbase } trufflehog-3.60.0/pkg/detectors/couchbase/couchbase_test.go000066400000000000000000000057631451332102400240060ustar00rootroot00000000000000//go:build detectors // +build detectors package couchbase import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCouchbase_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } endpoint := testSecrets.MustGetField("COUCHBASE_ENDPOINT") username := testSecrets.MustGetField("COUCHBASE_USERNAME") password := testSecrets.MustGetField("COUCHBASE_PASSWORD") inactiveSecret := testSecrets.MustGetField("COUCHBASE_INACTIVE_PASSWORD") 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("db uri: %s \n username = %s \n password = %s", endpoint, username, password)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Couchbase, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("db uri: %s \n username = %s \n password = %s", endpoint, username, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Couchbase, 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("Couchbase.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Couchbase.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) } } }) } } trufflehog-3.60.0/pkg/detectors/countrylayer/000077500000000000000000000000001451332102400212555ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/countrylayer/countrylayer.go000066400000000000000000000041571451332102400243530ustar00rootroot00000000000000package countrylayer import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"countrylayer"}) + `\b([a-z0-9]{32})\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{"countrylayer"} } // FromData will find and optionally verify CountryLayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CountryLayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.countrylayer.com/v2/all?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CountryLayer } trufflehog-3.60.0/pkg/detectors/countrylayer/countrylayer_test.go000066400000000000000000000055421451332102400254110ustar00rootroot00000000000000//go:build detectors // +build detectors package countrylayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCountryLayer_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("COUNTRYLAYER") inactiveSecret := testSecrets.MustGetField("COUNTRYLAYER_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 countrylayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CountryLayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a countrylayer 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_CountryLayer, 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("CountryLayer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CountryLayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/courier/000077500000000000000000000000001451332102400201655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/courier/courier.go000066400000000000000000000036511451332102400221710ustar00rootroot00000000000000package courier import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"courier"}) + `\b(pk\_[a-zA-Z0-9]{1,}\_[a-zA-Z0-9]{28})\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{"courier"} } // FromData will find and optionally verify Courier 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Courier, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.courier.com/preferences", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Courier } trufflehog-3.60.0/pkg/detectors/courier/courier_test.go000066400000000000000000000054601451332102400232300ustar00rootroot00000000000000//go:build detectors // +build detectors package courier import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCourier_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COURIER") inactiveSecret := testSecrets.MustGetField("COURIER_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 courier secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Courier, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a courier 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_Courier, 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("Courier.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Courier.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) } } }) } } trufflehog-3.60.0/pkg/detectors/coveralls/000077500000000000000000000000001451332102400205075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/coveralls/coveralls.go000066400000000000000000000042341451332102400230330ustar00rootroot00000000000000package coveralls import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"coveralls"}) + `\b([a-zA-Z0-9-]{37})\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{"coveralls"} } // FromData will find and optionally verify Coveralls 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Coveralls, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://coveralls.io/api/repos/github/secretscanner02/scanner", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Coveralls } trufflehog-3.60.0/pkg/detectors/coveralls/coveralls_test.go000066400000000000000000000055121451332102400240720ustar00rootroot00000000000000//go:build detectors // +build detectors package coveralls import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCoveralls_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("COVERALLS_TOKEN") inactiveSecret := testSecrets.MustGetField("COVERALLS_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 coveralls secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Coveralls, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a coveralls 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_Coveralls, 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("Coveralls.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Coveralls.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) } } }) } } trufflehog-3.60.0/pkg/detectors/craftmypdf/000077500000000000000000000000001451332102400206545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/craftmypdf/craftmypdf.go000066400000000000000000000042441451332102400233460ustar00rootroot00000000000000package craftmypdf import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"craftmypdf"}) + `\b([0-9a-zA-Z]{35})\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{"craftmypdf"} } // FromData will find and optionally verify CraftMyPDF 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CraftMyPDF, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.craftmypdf.com/v1/get-account-info", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CraftMyPDF } trufflehog-3.60.0/pkg/detectors/craftmypdf/craftmypdf_test.go000066400000000000000000000055161451332102400244100ustar00rootroot00000000000000//go:build detectors // +build detectors package craftmypdf import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCraftMyPDF_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CRAFTMYPDF") inactiveSecret := testSecrets.MustGetField("CRAFTMYPDF_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 craftmypdf secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CraftMyPDF, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a craftmypdf 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_CraftMyPDF, 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("CraftMyPDF.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CraftMyPDF.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) } } }) } } trufflehog-3.60.0/pkg/detectors/crossbrowsertesting/000077500000000000000000000000001451332102400226505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/crossbrowsertesting/crossbrowsertesting.go000066400000000000000000000050721451332102400273360ustar00rootroot00000000000000package crossbrowsertesting import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"crossbrowsertesting"}) + `\b([0-9a-z]{16})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"crossbrowsertesting"}) + `\b([a-z0-9]{4,25}@[a-zA-Z0-9]{2,12}.[a-zA-Z0-9]{2,6})\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{"crossbrowsertesting"} } // FromData will find and optionally verify CrossBrowserTesting 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CrossBrowserTesting, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://crossbrowsertesting.com/api/v3/livetests", nil) if err != nil { continue } req.SetBasicAuth(resIdMatch, resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CrossBrowserTesting } trufflehog-3.60.0/pkg/detectors/crossbrowsertesting/crossbrowsertesting_test.go000066400000000000000000000060321451332102400303720ustar00rootroot00000000000000//go:build detectors // +build detectors package crossbrowsertesting import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCrossBrowserTesting_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CROSSBROWSERTESTING") id := testSecrets.MustGetField("CROSSBROWSERTESTING_USER") inactiveSecret := testSecrets.MustGetField("CROSSBROWSERTESTING_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 crossbrowsertesting secret %s within crossbrowsertesting %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CrossBrowserTesting, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a crossbrowsertesting secret %s within crossbrowsertesting %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CrossBrowserTesting, 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("CrossBrowserTesting.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CrossBrowserTesting.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) } } }) } } trufflehog-3.60.0/pkg/detectors/crowdin/000077500000000000000000000000001451332102400201625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/crowdin/crowdin.go000066400000000000000000000041711451332102400221610ustar00rootroot00000000000000package crowdin import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"crowdin"}) + `\b([0-9A-Za-z]{80})\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{"crowdin"} } // FromData will find and optionally verify Crowdin 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Crowdin, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.crowdin.com/api/v2/storages", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Crowdin } trufflehog-3.60.0/pkg/detectors/crowdin/crowdin_test.go000066400000000000000000000054371451332102400232260ustar00rootroot00000000000000//go:build detectors // +build detectors package crowdin import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCrowdin_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("CROWDIN") inactiveSecret := testSecrets.MustGetField("CROWDIN_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 crowdin secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Crowdin, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a crowdin 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_Crowdin, 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("Crowdin.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Crowdin.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) } } }) } } trufflehog-3.60.0/pkg/detectors/cryptocompare/000077500000000000000000000000001451332102400214045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/cryptocompare/cryptocompare.go000066400000000000000000000045621451332102400246310ustar00rootroot00000000000000package cryptocompare import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"cryptocompare"}) + `\b([a-z-0-9]{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{"cryptocompare"} } // FromData will find and optionally verify CryptoCompare 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CryptoCompare, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://min-api.cryptocompare.com/data/blockchain/list?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) errCode := strings.Contains(bodyString, `"Response":"Success"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if errCode { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CryptoCompare } trufflehog-3.60.0/pkg/detectors/cryptocompare/cryptocompare_test.go000066400000000000000000000055541451332102400256720ustar00rootroot00000000000000//go:build detectors // +build detectors package cryptocompare import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCryptoCompare_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("CRYPTOCOMPARE") inactiveSecret := testSecrets.MustGetField("CRYPTOCOMPARE_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 cryptocompare secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CryptoCompare, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a cryptocompare 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_CryptoCompare, 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("CryptoCompare.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CryptoCompare.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) } } }) } } trufflehog-3.60.0/pkg/detectors/currencycloud/000077500000000000000000000000001451332102400213765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/currencycloud/currencycloud.go000066400000000000000000000050631451332102400246120ustar00rootroot00000000000000package currencycloud import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"currencycloud"}) + `\b([0-9a-z]{64})\b`) emailPat = regexp.MustCompile(`\b([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-z]+)\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{"currencycloud"} } // FromData will find and optionally verify Currencycloud 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) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, emailmatch := range emailMatches { if len(emailmatch) != 2 { continue } resEmailMatch := strings.TrimSpace(emailmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CurrencyCloud, Raw: []byte(resMatch), } if verify { // Get authentication token payload := strings.NewReader(`{"login_id":"` + resEmailMatch + `","api_key":"` + resMatch + `"`) req, err := http.NewRequestWithContext(ctx, "POST", "https://devapi.currencycloud.com/v2/authenticate/api", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") 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, "auth_token") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CurrencyCloud } trufflehog-3.60.0/pkg/detectors/currencycloud/currencycloud_test.go000066400000000000000000000056651451332102400256610ustar00rootroot00000000000000//go:build detectors // +build detectors package currencycloud import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCurrencycloud_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("CURRENCYCLOUD") email := testSecrets.MustGetField("SCANNERS_EMAIL") inactiveSecret := testSecrets.MustGetField("CURRENCYCLOUD_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 currencycloud secret %s within %s", secret, email)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CurrencyCloud, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a currencycloud secret %s within %s but not valid", inactiveSecret, email)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CurrencyCloud, 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("Currencycloud.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Currencycloud.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) } } }) } } trufflehog-3.60.0/pkg/detectors/currencyfreaks/000077500000000000000000000000001451332102400215435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/currencyfreaks/currencyfreaks.go000066400000000000000000000041601451332102400251210ustar00rootroot00000000000000package currencyfreaks import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"currencyfreaks"}) + `\b([0-9a-z]{32})\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{"currencyfreaks"} } // FromData will find and optionally verify Currencyfreaks 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Currencyfreaks, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.currencyfreaks.com/latest?apikey="+resMatch+"&format=xml", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Currencyfreaks } trufflehog-3.60.0/pkg/detectors/currencyfreaks/currencyfreaks_test.go000066400000000000000000000055661451332102400261730ustar00rootroot00000000000000//go:build detectors // +build detectors package currencyfreaks import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCurrencyfreaks_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("CURRENCYFREAKS") inactiveSecret := testSecrets.MustGetField("CURRENCYFREAKS_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 currencyfreaks secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Currencyfreaks, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a currencyfreaks 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_Currencyfreaks, 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("Currencyfreaks.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Currencyfreaks.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) } } }) } } trufflehog-3.60.0/pkg/detectors/currencylayer/000077500000000000000000000000001451332102400214045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/currencylayer/currencylayer.go000066400000000000000000000050041451332102400246210ustar00rootroot00000000000000package currencylayer import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"currencylayer"}) + `\b([a-z0-9]{32})\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{"currencylayer"} } // FromData will find and optionally verify Currencylayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Currencylayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.currencylayer.com/live?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err2 := io.ReadAll(res.Body) if err2 == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"success": true`) || strings.Contains(bodyString, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Currencylayer } trufflehog-3.60.0/pkg/detectors/currencylayer/currencylayer_test.go000066400000000000000000000055331451332102400256670ustar00rootroot00000000000000//go:build detectors // +build detectors package currencylayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCurrencylayer_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("CURRENCYLAYER") inactiveSecret := testSecrets.MustGetField("CURRENCYLAYER_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 currencylayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Currencylayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a currencylayer 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_Currencylayer, 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("Currencylayer.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Currencylayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/currencyscoop/000077500000000000000000000000001451332102400214135ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/currencyscoop/currencyscoop.go000066400000000000000000000042541451332102400246450ustar00rootroot00000000000000package currencyscoop import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"currencyscoop"}) + `\b([a-z0-9]{32})\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{"currencyscoop"} } // FromData will find and optionally verify Currencyscoop 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CurrencyScoop, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.currencyscoop.com/v1/latest?api_key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CurrencyScoop } trufflehog-3.60.0/pkg/detectors/currencyscoop/currencyscoop_test.go000066400000000000000000000055541451332102400257100ustar00rootroot00000000000000//go:build detectors // +build detectors package currencyscoop import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCurrencyscoop_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("CURRENCYSCOOP") inactiveSecret := testSecrets.MustGetField("CURRENCYSCOOP_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 currencyscoop secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CurrencyScoop, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a currencyscoop 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_CurrencyScoop, 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("Currencyscoop.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Currencyscoop.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) } } }) } } trufflehog-3.60.0/pkg/detectors/currentsapi/000077500000000000000000000000001451332102400210545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/currentsapi/currentsapi.go000066400000000000000000000041731451332102400237470ustar00rootroot00000000000000package currentsapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"currentsapi"}) + `\b([a-zA-Z0-9\S]{48})\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{"currentsapi"} } // FromData will find and optionally verify CurrentsAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CurrentsAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.currentsapi.services/v1/latest-news", nil) if err != nil { continue } req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CurrentsAPI } trufflehog-3.60.0/pkg/detectors/currentsapi/currentsapi_test.go000066400000000000000000000055301451332102400250040ustar00rootroot00000000000000//go:build detectors // +build detectors package currentsapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCurrentsAPI_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("CURRENTSAPI") inactiveSecret := testSecrets.MustGetField("CURRENTSAPI_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 currentsapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CurrentsAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a currentsapi 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_CurrentsAPI, 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("CurrentsAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CurrentsAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/customerguru/000077500000000000000000000000001451332102400212615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/customerguru/customerguru.go000066400000000000000000000047271451332102400243660ustar00rootroot00000000000000package customerguru import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"guru"}) + `\b([a-z0-9A-Z]{30})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"guru"}) + `\b([a-z0-9A-Z]{50})\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{"customerguru"} } // FromData will find and optionally verify CustomerGuru 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CustomerGuru, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://customer.guru/export/customers?api_secret="+resIdMatch+"&api_token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CustomerGuru } trufflehog-3.60.0/pkg/detectors/customerguru/customerguru_test.go000066400000000000000000000057151451332102400254230ustar00rootroot00000000000000//go:build detectors // +build detectors package customerguru import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCustomerGuru_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CUSTOMERGURU_TOKEN") inactiveSecret := testSecrets.MustGetField("CUSTOMERGURU_INACTIVE") key := testSecrets.MustGetField("CUSTOMERGURU_KEY") 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 customerguru secret %s within customergurukey %s", secret, key)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CustomerGuru, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a customerguru secret %s within customergurukey %s but not valid", inactiveSecret, key)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CustomerGuru, 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("CustomerGuru.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CustomerGuru.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) } } }) } } trufflehog-3.60.0/pkg/detectors/customerio/000077500000000000000000000000001451332102400207065ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/customerio/customerio.go000066400000000000000000000054601451332102400234330ustar00rootroot00000000000000package customerio import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"customer"}) + `\b([a-z0-9A-Z]{20})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"customer"}) + `\b([a-z0-9A-Z]{20})\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{"customerio"} } // FromData will find and optionally verify CustomerIO 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_CustomerIO, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { payload := strings.NewReader("name=purchase&data%5Bprice%5D=23.45&data%5Bproduct%5D=socks") data := fmt.Sprintf("%s:%s", resIdMatch, resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "POST", "https://track.customer.io/api/v1/customers/5/events", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_CustomerIO } trufflehog-3.60.0/pkg/detectors/customerio/customerio_test.go000066400000000000000000000056521451332102400244750ustar00rootroot00000000000000//go:build detectors // +build detectors package customerio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestCustomerIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("CUSTOMERIO_TOKEN") inactiveSecret := testSecrets.MustGetField("CUSTOMERIO_INACTIVE") id := testSecrets.MustGetField("CUSTOMERIO_ID") 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 customerio secret %s within customerid %s ", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CustomerIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a customerio secret %s within customerid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_CustomerIO, 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("CustomerIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("CustomerIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/d7network/000077500000000000000000000000001451332102400204415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/d7network/d7network.go000066400000000000000000000040531451332102400227160ustar00rootroot00000000000000package d7network import ( "context" "net/http" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"d7network"}) + `\b([a-zA-Z0-9\W\S]{23}\=)`) ) // 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{"d7network"} } // FromData will find and optionally verify D7Network 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_D7Network, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://rest-api.d7networks.com/secure/balance", nil) if err != nil { continue } req.Header.Add("Authorization", "Basic "+resMatch) res, err := http.DefaultClient.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_D7Network } trufflehog-3.60.0/pkg/detectors/d7network/d7network_test.go000066400000000000000000000055121451332102400237560ustar00rootroot00000000000000//go:build detectors // +build detectors package d7network import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestD7Network_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("D7NETWORK_TOKEN") inactiveSecret := testSecrets.MustGetField("D7NETWORK_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 d7network secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_D7Network, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a d7network 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_D7Network, 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("D7Network.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("D7Network.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dailyco/000077500000000000000000000000001451332102400201415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dailyco/dailyco.go000066400000000000000000000041511451332102400221150ustar00rootroot00000000000000package dailyco import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"daily"}) + `\b([0-9a-f]{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{"daily"} } // FromData will find and optionally verify DailyCO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DailyCO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.daily.co/v1/rooms", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DailyCO } trufflehog-3.60.0/pkg/detectors/dailyco/dailyco_test.go000066400000000000000000000054601451332102400231600ustar00rootroot00000000000000//go:build detectors // +build detectors package dailyco import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDailyCO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DAILYCO") inactiveSecret := testSecrets.MustGetField("DAILYCO_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 dailyco secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DailyCO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dailyco 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_DailyCO, 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("DailyCO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DailyCO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dandelion/000077500000000000000000000000001451332102400204525ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dandelion/dandelion.go000066400000000000000000000041471451332102400227440ustar00rootroot00000000000000package dandelion import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dandelion"}) + `\b([a-z0-9]{32})\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{"dandelion"} } // FromData will find and optionally verify Dandelion 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dandelion, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.dandelion.eu/datatxt/li/v1/?text=Smart&token=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dandelion } trufflehog-3.60.0/pkg/detectors/dandelion/dandelion_test.go000066400000000000000000000055041451332102400240010ustar00rootroot00000000000000//go:build detectors // +build detectors package dandelion import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDandelion_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("DANDELION") inactiveSecret := testSecrets.MustGetField("DANDELION_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 dandelion secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dandelion, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dandelion 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_Dandelion, 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("Dandelion.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dandelion.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dareboost/000077500000000000000000000000001451332102400204775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dareboost/dareboost.go000066400000000000000000000047271451332102400230220ustar00rootroot00000000000000package dareboost import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dareboost"}) + `\b([0-9a-zA-Z]{60})\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{"dareboost"} } // FromData will find and optionally verify Dareboost 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dareboost, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{ "token": "` + resMatch + `", "location": "Paris"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.dareboost.com/0.8/config", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"status":200`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dareboost } trufflehog-3.60.0/pkg/detectors/dareboost/dareboost_test.go000066400000000000000000000055041451332102400240530ustar00rootroot00000000000000//go:build detectors // +build detectors package dareboost import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDareboost_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DAREBOOST") inactiveSecret := testSecrets.MustGetField("DAREBOOST_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 dareboost secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dareboost, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dareboost 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_Dareboost, 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("Dareboost.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dareboost.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) } } }) } } trufflehog-3.60.0/pkg/detectors/databox/000077500000000000000000000000001451332102400201375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/databox/databox.go000066400000000000000000000047471451332102400221240ustar00rootroot00000000000000package databox import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"databox"}) + common.BuildRegex(common.RegexPattern, "", 21)) ) // 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{"databox"} } // FromData will find and optionally verify Databox 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Databox, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) payload := strings.NewReader(`{ "data":[ { "$sales": 420, "$visitors": 123000 } ] }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://push.databox.com", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/vnd.databox.v2+json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Databox } trufflehog-3.60.0/pkg/detectors/databox/databox_test.go000066400000000000000000000054601451332102400231540ustar00rootroot00000000000000//go:build detectors // +build detectors package databox import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDatabox_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DATABOX") inactiveSecret := testSecrets.MustGetField("DATABOX_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 databox secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Databox, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a databox 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_Databox, 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("Databox.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Databox.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) } } }) } } trufflehog-3.60.0/pkg/detectors/databrickstoken/000077500000000000000000000000001451332102400216655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/databrickstoken/databrickstoken.go000066400000000000000000000047561451332102400254000ustar00rootroot00000000000000package databrickstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. domain = regexp.MustCompile(`\b(https:\/\/[a-z0-9-]+\.cloud\.databricks\.com)\b`) keyPat = regexp.MustCompile(`\b(dapi[a-z0-9]{32})\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{"databricks", "dapi"} } // FromData will find and optionally verify Databrickstoken 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) domainMatches := domain.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, domainmatch := range domainMatches { if len(domainmatch) != 2 { continue } resDomainMatch := strings.TrimSpace(domainmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DatabricksToken, Raw: []byte(resMatch), RawV2: []byte(resMatch + resDomainMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", resDomainMatch + "/api/2.0/clusters/list", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DatabricksToken } trufflehog-3.60.0/pkg/detectors/databrickstoken/databrickstoken_test.go000066400000000000000000000056001451332102400264240ustar00rootroot00000000000000//go:build detectors // +build detectors package databrickstoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDatabrickstoken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DATABRICKSTOKEN") inactiveSecret := testSecrets.MustGetField("DATABRICKSTOKEN_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 databrickstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DatabricksToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a databrickstoken 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_DatabricksToken, 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("Databrickstoken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Databrickstoken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/datadogtoken/000077500000000000000000000000001451332102400211615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/datadogtoken/datadogtoken.go000066400000000000000000000073431451332102400241630ustar00rootroot00000000000000package datadogtoken import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. appPat = regexp.MustCompile(detectors.PrefixRegex([]string{"datadog", "dd"}) + `\b([a-zA-Z-0-9]{40})\b`) apiPat = regexp.MustCompile(detectors.PrefixRegex([]string{"datadog", "dd"}) + `\b([a-zA-Z-0-9]{32})\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{"datadog"} } // FromData will find and optionally verify DatadogToken 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 := appPat.FindAllStringSubmatch(dataStr, -1) apiMatches := apiPat.FindAllStringSubmatch(dataStr, -1) for _, apiMatch := range apiMatches { if len(apiMatch) != 2 { continue } resApiMatch := strings.TrimSpace(apiMatch[1]) appIncluded := false for _, appMatch := range appMatches { if len(appMatch) != 2 { continue } resAppMatch := strings.TrimSpace(appMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DatadogToken, Raw: []byte(resAppMatch), RawV2: []byte(resAppMatch + resApiMatch), ExtraData: map[string]string{ "Type": "Application+APIKey", }, } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.datadoghq.com/api/v2/users", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("DD-API-KEY", resApiMatch) req.Header.Add("DD-APPLICATION-KEY", resAppMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resApiMatch, detectors.DefaultFalsePositives, true) { continue } } } } appIncluded = true results = append(results, s1) } if !appIncluded { s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DatadogToken, Raw: []byte(resApiMatch), RawV2: []byte(resApiMatch), ExtraData: map[string]string{ "Type": "APIKeyOnly", }, } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.datadoghq.com/api/v1/validate", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("DD-API-KEY", resApiMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resApiMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DatadogToken } trufflehog-3.60.0/pkg/detectors/datadogtoken/datadogtoken_test.go000066400000000000000000000071651451332102400252240ustar00rootroot00000000000000//go:build detectors // +build detectors package datadogtoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDatadogToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } apiKey := testSecrets.MustGetField("DATADOGTOKEN_TOKEN") appKey := testSecrets.MustGetField("DATADOGTOKEN_APPKEY") inactiveAppKey := testSecrets.MustGetField("DATADOGTOKEN_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 datadogtoken secret %s within datadog %s", appKey, apiKey)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DatadogToken, Verified: true, ExtraData: map[string]string{ "Type": "Application+APIKey", }, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a datadogtoken secret %s within but datadog %s not valid", inactiveAppKey, apiKey)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DatadogToken, Verified: false, ExtraData: map[string]string{ "Type": "Application+APIKey", }, }, }, wantErr: false, }, { name: "api key found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a datadogtoken secret %s", apiKey)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DatadogToken, Verified: true, ExtraData: map[string]string{ "Type": "APIKeyOnly", }, }, }, 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("DatadogToken.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]) } got[i].Raw = nil got[i].RawV2 = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DatadogToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/datafire/000077500000000000000000000000001451332102400202745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/datafire/datafire.go000066400000000000000000000041761451332102400224120ustar00rootroot00000000000000package datafire import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"datafire"}) + `\b([a-z0-9\S]{175,190})\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{"datafire"} } // FromData will find and optionally verify DataFire 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DataFire, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.datafire.io/projects/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DataFire } trufflehog-3.60.0/pkg/detectors/datafire/datafire_test.go000066400000000000000000000054721451332102400234510ustar00rootroot00000000000000//go:build detectors // +build detectors package datafire import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDataFire_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("DATAFIRE") inactiveSecret := testSecrets.MustGetField("DATAFIRE_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 datafire secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DataFire, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a datafire 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_DataFire, 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("DataFire.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DataFire.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) } } }) } } trufflehog-3.60.0/pkg/detectors/datagov/000077500000000000000000000000001451332102400201425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/datagov/datagov.go000066400000000000000000000041301451332102400221140ustar00rootroot00000000000000package datagov import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"data.gov"}) + `\b([a-zA-Z0-9]{40})\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{"data.gov"} } // FromData will find and optionally verify DataGov 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DataGov, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.ers.usda.gov/data/arms/state?api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DataGov } trufflehog-3.60.0/pkg/detectors/datagov/datagov_test.go000066400000000000000000000054621451332102400231640ustar00rootroot00000000000000//go:build detectors // +build detectors package datagov import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDataGov_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("DATAGOV") inactiveSecret := testSecrets.MustGetField("DATAGOV_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 data.gov secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DataGov, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a data.gov 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_DataGov, 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("DataGov.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DataGov.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) } } }) } } trufflehog-3.60.0/pkg/detectors/debounce/000077500000000000000000000000001451332102400203015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/debounce/debounce.go000066400000000000000000000041151451332102400224150ustar00rootroot00000000000000package debounce import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"debounce"}) + `\b([a-zA-Z0-9]{13})\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{"debounce"} } // FromData will find and optionally verify Debounce 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Debounce, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.debounce.io/v1/?api="+resMatch+"&email=some@gmail.com", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Debounce } trufflehog-3.60.0/pkg/detectors/debounce/debounce_test.go000066400000000000000000000055001451332102400234530ustar00rootroot00000000000000//go:build detectors // +build detectors package debounce import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDebounce_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DEBOUNCE_TOKEN") inactiveSecret := testSecrets.MustGetField("DEBOUNCE_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 debounce secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Debounce, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a debounce 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_Debounce, 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("Debounce.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Debounce.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) } } }) } } trufflehog-3.60.0/pkg/detectors/deepai/000077500000000000000000000000001451332102400177445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/deepai/deepai.go000066400000000000000000000047051451332102400215300ustar00rootroot00000000000000package deepai import ( "bytes" "context" "io" "mime/multipart" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"deepai"}) + `\b([a-z0-9-]{36})\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{"deepai"} } // FromData will find and optionally verify DeepAI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DeepAI, Raw: []byte(resMatch), } if verify { body := &bytes.Buffer{} writer := multipart.NewWriter(body) fw, err := writer.CreateFormField("text") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader("test")) if err != nil { continue } writer.Close() req, err := http.NewRequestWithContext(ctx, "POST", "https://api.deepai.org/api/text-tagging", bytes.NewReader(body.Bytes())) if err != nil { continue } req.Header.Add("Content-Type", writer.FormDataContentType()) req.Header.Add("api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DeepAI } trufflehog-3.60.0/pkg/detectors/deepai/deepai_test.go000066400000000000000000000054461451332102400225720ustar00rootroot00000000000000//go:build detectors // +build detectors package deepai import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDeepAI_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("DEEPAI") inactiveSecret := testSecrets.MustGetField("DEEPAI_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 deepai secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DeepAI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a deepai 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_DeepAI, 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("DeepAI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DeepAI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/deepgram/000077500000000000000000000000001451332102400203015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/deepgram/deepgram.go000066400000000000000000000042571451332102400224240ustar00rootroot00000000000000package deepgram import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"deepgram"}) + `\b([0-9a-z]{40})\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{"deepgram"} } // FromData will find and optionally verify Deepgram 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Deepgram, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.deepgram.com/v1/projects", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Deepgram } trufflehog-3.60.0/pkg/detectors/deepgram/deepgram_test.go000066400000000000000000000054721451332102400234630ustar00rootroot00000000000000//go:build detectors // +build detectors package deepgram import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDeepgram_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("DEEPGRAM") inactiveSecret := testSecrets.MustGetField("DEEPGRAM_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 deepgram secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Deepgram, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a deepgram 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_Deepgram, 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("Deepgram.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Deepgram.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) } } }) } } trufflehog-3.60.0/pkg/detectors/delighted/000077500000000000000000000000001451332102400204465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/delighted/delighted.go000066400000000000000000000047131451332102400227330ustar00rootroot00000000000000package delighted import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"delighted"}) + `\b([a-z0-9A-Z]{32})\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{"delighted"} } // FromData will find and optionally verify Delighted 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Delighted, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) payload := strings.NewReader(`{ "email": "jony@appleseed.com", "properties": { "Purchase Experience": "Mobile App", "State": "CA" } }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.delighted.com/v1/people.json", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Delighted } trufflehog-3.60.0/pkg/detectors/delighted/delighted_test.go000066400000000000000000000055121451332102400237700ustar00rootroot00000000000000//go:build detectors // +build detectors package delighted import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDelighted_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DELIGHTED_TOKEN") inactiveSecret := testSecrets.MustGetField("DELIGHTED_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 delighted secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Delighted, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a delighted 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_Delighted, 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("Delighted.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Delighted.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) } } }) } } trufflehog-3.60.0/pkg/detectors/demio/000077500000000000000000000000001451332102400176125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/demio/demio.go000066400000000000000000000046551451332102400212500ustar00rootroot00000000000000package demio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"demio"}) + `\b([a-z0-9A-Z]{32})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"demio"}) + `\b([a-z0-9A-Z]{10,20})\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{"demio"} } // FromData will find and optionally verify Demio 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) idMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idMatches { if len(match) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Demio, Raw: []byte(resMatch), } if verify { url := fmt.Sprintf("https://my.demio.com/api/v1/ping/query?api_key=%s&api_secret=%s", resMatch, resIdMatch) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Demio } trufflehog-3.60.0/pkg/detectors/demio/demio_test.go000066400000000000000000000055211451332102400223000ustar00rootroot00000000000000package demio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDemio_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DEMIO") inactiveSecret := testSecrets.MustGetField("DEMIO_INACTIVE") keySecret := testSecrets.MustGetField("DEMIO_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 a demio secret %s within demio %s", secret, keySecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Demio, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a demio secret %s within but not valid demio %s", inactiveSecret, keySecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Demio, 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("Demio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Demio.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) } } }) } } trufflehog-3.60.0/pkg/detectors/deputy/000077500000000000000000000000001451332102400200275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/deputy/deputy.go000066400000000000000000000047031451332102400216740ustar00rootroot00000000000000package deputy import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"deputy"}) + `\b([0-9a-z]{32})\b`) urlPat = regexp.MustCompile(`\b([0-9a-z]{1,}\.as\.deputy\.com)\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{"deputy"} } // FromData will find and optionally verify Deputy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, urlMatch := range urlMatches { if len(urlMatch) != 2 { continue } resURL := strings.TrimSpace(urlMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Deputy, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/api/v1/me", resURL), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("OAuth %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Deputy } trufflehog-3.60.0/pkg/detectors/deputy/deputy_test.go000066400000000000000000000055451451332102400227400ustar00rootroot00000000000000//go:build detectors // +build detectors package deputy import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDeputy_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DEPUTY") url := testSecrets.MustGetField("DEPUTY_URL") inactiveSecret := testSecrets.MustGetField("DEPUTY_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 deputy secret %s within %s", secret, url)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Deputy, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a deputy secret %s within %s but not valid", inactiveSecret, url)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Deputy, 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("Deputy.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Deputy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/detectify/000077500000000000000000000000001451332102400204755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/detectify/detectify.go000066400000000000000000000041461451332102400230110ustar00rootroot00000000000000package detectify import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"detectify"}) + `\b([0-9a-z]{32})\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{"detectify"} } // FromData will find and optionally verify Detectify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Detectify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.detectify.com/rest/v2/assets/", nil) if err != nil { continue } req.Header.Add("X-Detectify-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Detectify } trufflehog-3.60.0/pkg/detectors/detectify/detectify_test.go000066400000000000000000000055041451332102400240470ustar00rootroot00000000000000//go:build detectors // +build detectors package detectify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDetectify_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DETECTIFY") inactiveSecret := testSecrets.MustGetField("DETECTIFY_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 detectify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Detectify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a detectify 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_Detectify, 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("Detectify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Detectify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/detectlanguage/000077500000000000000000000000001451332102400214715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/detectlanguage/detectlanguage.go000066400000000000000000000042471451332102400250030ustar00rootroot00000000000000package detectlanguage import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"detectlanguage"}) + `\b([a-z0-9]{32})\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{"detectlanguage"} } // FromData will find and optionally verify DetectLanguage 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DetectLanguage, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://ws.detectlanguage.com/0.2/user/status", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DetectLanguage } trufflehog-3.60.0/pkg/detectors/detectlanguage/detectlanguage_test.go000066400000000000000000000055661451332102400260470ustar00rootroot00000000000000//go:build detectors // +build detectors package detectlanguage import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDetectLanguage_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("DETECTLANGUAGE") inactiveSecret := testSecrets.MustGetField("DETECTLANGUAGE_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 detectlanguage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DetectLanguage, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a detectlanguage 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_DetectLanguage, 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("DetectLanguage.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DetectLanguage.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) } } }) } } trufflehog-3.60.0/pkg/detectors/detectors.go000066400000000000000000000123661451332102400210500ustar00rootroot00000000000000package detectors import ( "context" "crypto/rand" "math/big" "net/url" "strings" "unicode" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb" "github.com/trufflesecurity/trufflehog/v3/pkg/sources" ) // Detector defines an interface for scanning for and verifying secrets. type Detector interface { // FromData will scan bytes for results, and optionally verify them. FromData(ctx context.Context, verify bool, data []byte) ([]Result, error) // Keywords are used for efficiently pre-filtering chunks using substring operations. // Use unique identifiers that are part of the secret if you can, or the provider name. Keywords() []string // Type returns the DetectorType number from detectors.proto for the given detector. Type() detectorspb.DetectorType } // Versioner is an optional interface that a detector can implement to // differentiate instances of the same detector type. type Versioner interface { Version() int } // EndpointCustomizer is an optional interface that a detector can implement to // support verifying against user-supplied endpoints. type EndpointCustomizer interface { SetEndpoints(...string) error DefaultEndpoint() string } type Result struct { // DetectorType is the type of Detector. DetectorType detectorspb.DetectorType // DetectorName is the name of the Detector. Used for custom detectors. DetectorName string // DecoderType is the type of Decoder. DecoderType detectorspb.DecoderType Verified bool // Raw contains the raw secret identifier data. Prefer IDs over secrets since it is used for deduping after hashing. Raw []byte // RawV2 contains the raw secret identifier that is a combination of both the ID and the secret. // This is used for secrets that are multi part and could have the same ID. Ex: AWS credentials RawV2 []byte // Redacted contains the redacted version of the raw secret identification data for display purposes. // A secret ID should be used if available. Redacted string ExtraData map[string]string StructuredData *detectorspb.StructuredData // This field should only be populated if the verification process itself failed in a way that provides no // information about the verification status of the candidate secret, such as if the verification request timed out. VerificationError error } type ResultWithMetadata struct { // SourceMetadata contains source-specific contextual information. SourceMetadata *source_metadatapb.MetaData // SourceID is the ID of the source that the API uses to map secrets to specific sources. SourceID sources.SourceID // SourceType is the type of Source. SourceType sourcespb.SourceType // SourceName is the name of the Source. SourceName string Result // Data from the sources.Chunk which this result was emitted for Data []byte } // CopyMetadata returns a detector result with included metadata from the source chunk. func CopyMetadata(chunk *sources.Chunk, result Result) ResultWithMetadata { return ResultWithMetadata{ SourceMetadata: chunk.SourceMetadata, SourceID: chunk.SourceID, SourceType: chunk.SourceType, SourceName: chunk.SourceName, Result: result, Data: chunk.Data, } } // CleanResults returns all verified secrets, and if there are no verified secrets, // just one unverified secret if there are any. func CleanResults(results []Result) []Result { if len(results) == 0 { return results } var cleaned = make(map[string]Result, 0) for _, s := range results { if s.Verified { cleaned[s.Redacted] = s } } if len(cleaned) == 0 { return results[:1] } results = results[:0] for _, r := range cleaned { results = append(results, r) } return results } // PrefixRegex ensures that at least one of the given keywords is within // 20 characters of the capturing group that follows. // This can help prevent false positives. func PrefixRegex(keywords []string) string { pre := `(?i)(?:` middle := strings.Join(keywords, "|") post := `)(?:.|[\n\r]){0,40}` return pre + middle + post } // KeyIsRandom is a Low cost check to make sure that 'keys' include a number to reduce FPs. // Golang doesn't support regex lookaheads, so must be done in separate calls. // TODO improve checks. Shannon entropy did not work well. func KeyIsRandom(key string) bool { for _, ch := range key { if unicode.IsDigit(ch) { return true } } return false } func MustGetBenchmarkData() map[string][]byte { sizes := map[string]int{ "xsmall": 10, // 10 bytes "small": 100, // 100 bytes "medium": 1024, // 1KB "large": 10 * 1024, // 10KB "xlarge": 100 * 1024, // 100KB "xxlarge": 1024 * 1024, // 1MB } data := make(map[string][]byte) for key, size := range sizes { // Generating a byte slice of a specific size with random data. content := make([]byte, size) for i := 0; i < size; i++ { randomByte, err := rand.Int(rand.Reader, big.NewInt(256)) if err != nil { panic(err) } content[i] = byte(randomByte.Int64()) } data[key] = content } return data } func RedactURL(u url.URL) string { u.User = url.UserPassword(u.User.Username(), "********") return strings.TrimSpace(strings.Replace(u.String(), "%2A", "*", -1)) } trufflehog-3.60.0/pkg/detectors/detectors_test.go000066400000000000000000000014131451332102400220760ustar00rootroot00000000000000//go:build detectors // +build detectors package detectors import "testing" func TestPrefixRegex(t *testing.T) { tests := []struct { keywords []string expected string }{ { keywords: []string{"securitytrails"}, expected: `(?i)(?:securitytrails)(?:.|[\n\r]){0,40}`, }, { keywords: []string{"zipbooks"}, expected: `(?i)(?:zipbooks)(?:.|[\n\r]){0,40}`, }, { keywords: []string{"wrike"}, expected: `(?i)(?:wrike)(?:.|[\n\r]){0,40}`, }, } for _, tt := range tests { got := PrefixRegex(tt.keywords) if got != tt.expected { t.Errorf("PrefixRegex(%v) got: %v want: %v", tt.keywords, got, tt.expected) } } } func BenchmarkPrefixRegex(b *testing.B) { kws := []string{"securitytrails"} for i := 0; i < b.N; i++ { PrefixRegex(kws) } } trufflehog-3.60.0/pkg/detectors/dfuse/000077500000000000000000000000001451332102400176235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dfuse/dfuse.go000066400000000000000000000041601451332102400212610ustar00rootroot00000000000000package dfuse import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(web\_[0-9a-z]{32})\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{"dfuse"} } // FromData will find and optionally verify Dfuse 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dfuse, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"api_key":"` + resMatch + `"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://auth.dfuse.io/v1/auth/issue", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dfuse } trufflehog-3.60.0/pkg/detectors/dfuse/dfuse_test.go000066400000000000000000000054341451332102400223250ustar00rootroot00000000000000//go:build detectors // +build detectors package dfuse import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDfuse_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DFUSE") inactiveSecret := testSecrets.MustGetField("DFUSE_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 dfuse secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dfuse, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dfuse 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_Dfuse, 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("Dfuse.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dfuse.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) } } }) } } trufflehog-3.60.0/pkg/detectors/diffbot/000077500000000000000000000000001451332102400201325ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/diffbot/diffbot.go000066400000000000000000000046641451332102400221100ustar00rootroot00000000000000package diffbot import ( "context" "fmt" "io" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"diffbot"}) + `\b([a-z0-9]{32})\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{"diffbot"} } // FromData will find and optionally verify Diffbot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Diffbot, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequest("GET", fmt.Sprintf("https://api.diffbot.com/v4/account?token=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"token":`) && strings.Contains(bodyString, `"planCredits":`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Diffbot } trufflehog-3.60.0/pkg/detectors/diffbot/diffbot_test.go000066400000000000000000000053721451332102400231440ustar00rootroot00000000000000//go:build detectors // +build detectors package diffbot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDiffbot_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DIFFBOT") inactiveSecret := testSecrets.MustGetField("DIFFBOT_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 diffbot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Diffbot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a diffbot 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_Diffbot, 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("Diffbot.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Diffbot.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/diggernaut/000077500000000000000000000000001451332102400206465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/diggernaut/diggernaut.go000066400000000000000000000042651451332102400233350ustar00rootroot00000000000000package diggernaut import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"diggernaut"}) + `\b([0-9a-z]{40})\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{"diggernaut"} } // FromData will find and optionally verify Diggernaut 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Diggernaut, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.diggernaut.com/api/projects", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Diggernaut } trufflehog-3.60.0/pkg/detectors/diggernaut/diggernaut_test.go000066400000000000000000000055161451332102400243740ustar00rootroot00000000000000//go:build detectors // +build detectors package diggernaut import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDiggernaut_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DIGGERNAUT") inactiveSecret := testSecrets.MustGetField("DIGGERNAUT_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 diggernaut secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Diggernaut, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a diggernaut 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_Diggernaut, 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("Diggernaut.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Diggernaut.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) } } }) } } trufflehog-3.60.0/pkg/detectors/digitaloceantoken/000077500000000000000000000000001451332102400222015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/digitaloceantoken/digitaloceantoken.go000066400000000000000000000036541451332102400262240ustar00rootroot00000000000000package digitaloceantoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ocean", "do"}) + `\b([A-Za-z0-9_-]{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{"digitalocean"} } // FromData will find and optionally verify DigitalOceanToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DigitalOceanToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.digitalocean.com/v2/account", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DigitalOceanToken } trufflehog-3.60.0/pkg/detectors/digitaloceantoken/digitaloceantoken_test.go000066400000000000000000000055631451332102400272640ustar00rootroot00000000000000//go:build detectors // +build detectors package digitaloceantoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDigitalOceanToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DIGITALOCEAN_PERSONAL_ACCESS_TOKEN") inactiveSecret := testSecrets.MustGetField("DIGITALOCEAN_PERSONAL_ACCESS_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 digitaloceantoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DigitalOceanToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a digitaloceantoken secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DigitalOceanToken, 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("DigitalOceanToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DigitalOceanToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/digitaloceanv2/000077500000000000000000000000001451332102400214105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/digitaloceanv2/digitaloceanv2.go000066400000000000000000000062371451332102400246420ustar00rootroot00000000000000package digitaloceanv2 import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b((?:dop|doo|dor)_v1_[a-f0-9]{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{"dop_v1_", "doo_v1_", "dor_v1_"} } // FromData will find and optionally verify DigitalOceanV2 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DigitalOceanV2, Raw: []byte(resMatch), } if verify { switch { case strings.HasPrefix(resMatch, "dor_v1_"): req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.digitalocean.com/v1/oauth/token?grant_type=refresh_token&refresh_token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"access_token"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } case strings.HasPrefix(resMatch, "doo_v1_"), strings.HasPrefix(resMatch, "dop_v1_"): req, err := http.NewRequestWithContext(ctx, "GET", "https://api.digitalocean.com/v2/account", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DigitalOceanV2 } trufflehog-3.60.0/pkg/detectors/digitaloceanv2/digitaloceanv2_test.go000066400000000000000000000055661451332102400257050ustar00rootroot00000000000000//go:build detectors // +build detectors package digitaloceanv2 import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDigitalOceanV2_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DIGITALOCEANV2") inactiveSecret := testSecrets.MustGetField("DIGITALOCEANV2_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 digitaloceanv2 secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DigitalOceanV2, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a digitaloceanv2 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_DigitalOceanV2, 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("DigitalOceanV2.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DigitalOceanV2.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) } } }) } } trufflehog-3.60.0/pkg/detectors/discordbottoken/000077500000000000000000000000001451332102400217125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/discordbottoken/discordbottoken.go000066400000000000000000000044471451332102400254470ustar00rootroot00000000000000package discordbottoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"discord"}) + `\b([0-9]{17})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"discord"}) + `\b([A-Za-z0-9_-]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27})\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{"discord"} } // FromData will find and optionally verify DiscordBotToken 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) idMatch := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idMatch { if len(match) != 2 { continue } resId := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DiscordBotToken, Redacted: resId, Raw: []byte(resMatch), RawV2: []byte(resMatch + resId), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://discord.com/api/v8/users/"+resId, nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bot %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DiscordBotToken } trufflehog-3.60.0/pkg/detectors/discordbottoken/discordbottoken_test.go000066400000000000000000000061221451332102400264760ustar00rootroot00000000000000//go:build detectors // +build detectors package discordbottoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDiscordBotToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DISCORDBOTTOKEN_TOKEN") inactiveSecret := testSecrets.MustGetField("DISCORDBOTTOKEN_INACTIVE") idSecret := testSecrets.MustGetField("DISCORDBOTTOKEN_USERID") 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 discordbot secret %s within https://discord.com/api/v8/users/%s", secret, idSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DiscordBotToken, Redacted: idSecret, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a discordbot secret %s within https://discord.com/api/v8/users/%s but not valid", inactiveSecret, idSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DiscordBotToken, Redacted: idSecret, 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("DiscordBotToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DiscordBotToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/discordwebhook/000077500000000000000000000000001451332102400215235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/discordwebhook/discordwebhook.go000066400000000000000000000034271451332102400250660ustar00rootroot00000000000000package discordwebhook import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`(https:\/\/discord\.com\/api\/webhooks\/[0-9]{18}\/[0-9a-zA-Z-]{68})`) ) // 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{"discord"} } // FromData will find and optionally verify DiscordWebhook 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DiscordWebhook, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DiscordWebhook } trufflehog-3.60.0/pkg/detectors/discordwebhook/discordwebhook_test.go000066400000000000000000000056251451332102400261270ustar00rootroot00000000000000//go:build detectors // +build detectors package discordwebhook import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDiscordWebhook_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DISCORDWEBHOOK") inactiveSecret := testSecrets.MustGetField("DISCORDWEBHOOK_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 discordwebhook secret %s within discordwebhook", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DiscordWebhook, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a discordwebhook secret %s within discordwebhook but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DiscordWebhook, 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("DiscordWebhook.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DiscordWebhook.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) } } }) } } trufflehog-3.60.0/pkg/detectors/disqus/000077500000000000000000000000001451332102400200255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/disqus/disqus.go000066400000000000000000000041031451332102400216620ustar00rootroot00000000000000package disqus import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"disqus"}) + `\b([a-zA-Z0-9]{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{"disqus"} } // FromData will find and optionally verify Disqus 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Disqus, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://disqus.com/api/3.0/trends/listThreads.json?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Disqus } trufflehog-3.60.0/pkg/detectors/disqus/disqus_test.go000066400000000000000000000054461451332102400227340ustar00rootroot00000000000000//go:build detectors // +build detectors package disqus import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDisqus_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DISQUS") inactiveSecret := testSecrets.MustGetField("DISQUS_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 disqus secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Disqus, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a disqus 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_Disqus, 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("Disqus.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Disqus.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ditto/000077500000000000000000000000001451332102400176405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ditto/ditto.go000066400000000000000000000042501451332102400213130ustar00rootroot00000000000000package ditto import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ditto"}) + `\b([a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}\.[a-z0-9]{40})\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{"ditto"} } // FromData will find and optionally verify Ditto 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Ditto, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.dittowords.com/variants", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ditto } trufflehog-3.60.0/pkg/detectors/ditto/ditto_test.go000066400000000000000000000054341451332102400223570ustar00rootroot00000000000000//go:build detectors // +build detectors package ditto import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDitto_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DITTO") inactiveSecret := testSecrets.MustGetField("DITTO_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 ditto secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Ditto, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ditto 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_Ditto, 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("Ditto.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ditto.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dnscheck/000077500000000000000000000000001451332102400202775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dnscheck/dnscheck.go000066400000000000000000000046741451332102400224230ustar00rootroot00000000000000package dnscheck import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dnscheck"}) + `\b([a-z0-9A-Z]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dnscheck"}) + `\b([a-z0-9A-Z-]{36})\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{"dnscheck"} } // FromData will find and optionally verify Dnscheck 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dnscheck, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.dnscheck.co/api/v1/groups/"+resIdMatch+"?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dnscheck } trufflehog-3.60.0/pkg/detectors/dnscheck/dnscheck_test.go000066400000000000000000000056151451332102400234560ustar00rootroot00000000000000//go:build detectors // +build detectors package dnscheck import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDnscheck_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("DNSCHECK") inactiveSecret := testSecrets.MustGetField("DNSCHECK_INACTIVE") id := testSecrets.MustGetField("DNSCHECK_ID") 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 dnscheck secret %s within dnscheckid %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dnscheck, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dnscheck secret %s within dnscheckid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dnscheck, 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("Dnscheck.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dnscheck.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dockerhub/000077500000000000000000000000001451332102400204635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dockerhub/dockerhub.go000066400000000000000000000054431451332102400227660ustar00rootroot00000000000000package dockerhub import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Can use email or username for login. usernamePat = regexp.MustCompile(`(?im)(?:user|usr|-u)\S{0,40}?[:=\s]{1,3}[ '"=]?([a-zA-Z0-9]{4,40})\b`) emailPat = regexp.MustCompile(common.EmailPattern) // Can use password or personal access token (PAT) for login, but this scanner will only check for PATs. accessTokenPat = regexp.MustCompile(`\bdckr_pat_([a-zA-Z0-9_-]){27}\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{"dckr_pat_"} } // FromData will find and optionally verify Dockerhub 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) emailMatches := emailPat.FindAllString(dataStr, -1) dataStr = emailPat.ReplaceAllString(dataStr, "") usernameMatches := usernamePat.FindAllStringSubmatch(dataStr, -1) accessTokenMatches := accessTokenPat.FindAllString(dataStr, -1) userMatches := emailMatches for _, usernameMatch := range usernameMatches { if len(usernameMatch) > 1 { userMatches = append(userMatches, usernameMatch[1]) } } for _, resUserMatch := range userMatches { for _, resAccessTokenMatch := range accessTokenMatches { s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dockerhub, Raw: []byte(fmt.Sprintf("%s: %s", resUserMatch, resAccessTokenMatch)), } if verify { payload := strings.NewReader(fmt.Sprintf(`{"username": "%s", "password": "%s"}`, resUserMatch, resAccessTokenMatch)) req, err := http.NewRequestWithContext(ctx, "GET", "https://hub.docker.com/v2/users/login", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { continue } // Valid credentials can still return a 401 status code if 2FA is enabled if (res.StatusCode >= 200 && res.StatusCode < 300) || (res.StatusCode == 401 && strings.Contains(string(body), "login_2fa_token")) { s1.Verified = true } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dockerhub } trufflehog-3.60.0/pkg/detectors/dockerhub/dockerhub_test.go000066400000000000000000000063751451332102400240320ustar00rootroot00000000000000//go:build detectors // +build detectors package dockerhub import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDockerhub_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } username := testSecrets.MustGetField("DOCKERHUB_USERNAME") email := testSecrets.MustGetField("DOCKERHUB_EMAIL") pat := testSecrets.MustGetField("DOCKERHUB_PAT") inactivePat := testSecrets.MustGetField("DOCKERHUB_INACTIVE_PAT") 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("docker login -u %s -p %s", username, pat)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dockerhub, Verified: true, }, }, wantErr: false, }, { name: "found, verified (email)", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("docker login -u %s -p %s", email, pat)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dockerhub, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("docker login -u %s -p %s", username, inactivePat)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dockerhub, 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("Dockerhub.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dockerhub.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) } } }) } } trufflehog-3.60.0/pkg/detectors/docparser/000077500000000000000000000000001451332102400204775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/docparser/docparser.go000066400000000000000000000041471451332102400230160ustar00rootroot00000000000000package docparser import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"docparser"}) + `\b([a-f0-9]{40})\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{"docparser"} } // FromData will find and optionally verify Docparser 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Docparser, Raw: []byte(resMatch), } if verify { url := fmt.Sprintf("https://api.docparser.com/v1/parsers?api_key=%s", resMatch) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Docparser } trufflehog-3.60.0/pkg/detectors/docparser/docparser_test.go000066400000000000000000000054321451332102400240530ustar00rootroot00000000000000package docparser import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDocparser_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DOCPARSER") inactiveSecret := testSecrets.MustGetField("DOCPARSER_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 docparser secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Docparser, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a docparser 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_Docparser, 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("Docparser.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Docparser.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) } } }) } } trufflehog-3.60.0/pkg/detectors/documo/000077500000000000000000000000001451332102400200035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/documo/documo.go000066400000000000000000000042311451332102400216200ustar00rootroot00000000000000package documo import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(ey[a-zA-Z0-9]{34}.ey[a-zA-Z0-9]{154}.[a-zA-Z0-9_-]{43})\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{"documo"} } // FromData will find and optionally verify Documo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Documo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.documo.com/v1/me", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Documo } trufflehog-3.60.0/pkg/detectors/documo/documo_test.go000066400000000000000000000054461451332102400226700ustar00rootroot00000000000000//go:build detectors // +build detectors package documo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDocumo_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("DOCUMO") inactiveSecret := testSecrets.MustGetField("DOCUMO_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 documo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Documo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a documo 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_Documo, 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("Documo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Documo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/docusign/000077500000000000000000000000001451332102400203305ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/docusign/docusign.go000066400000000000000000000066101451332102400224750ustar00rootroot00000000000000package docusign import ( "context" "encoding/base64" "fmt" "github.com/go-errors/errors" "net/http" "regexp" "strings" "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{} type Response struct { AccessToken string `json:"access_token"` } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = 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{"integration", "id"}) + common.UUIDPattern) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"secret"}) + common.UUIDPattern) ) // 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{"docusign"} } // FromData will find and optionally verify Docusign 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIDMatch := strings.TrimSpace(idMatch[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Docusign, Raw: []byte(resIDMatch), Redacted: resIDMatch, RawV2: []byte(resIDMatch + resSecretMatch), } // Verify client id and secret pair by using an *undocumented* client_credentials grant type on the oauth2 endpoint. // If verifier breaks in the future, confirm that the oauth2 endpoint is still accepting the client_credentials grant type. if verify { req, err := http.NewRequestWithContext(ctx, "POST", "https://account-d.docusign.com/oauth/token?grant_type=client_credentials", nil) if err != nil { continue } encodedCredentials := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", resIDMatch, resSecretMatch))) req.Header.Add("Accept", "application/vnd.docusign+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", encodedCredentials)) res, err := client.Do(req) if err != nil { return nil, errors.WrapPrefix(err, "Error making request", 0) } verifiedBodyResponse, err := common.ResponseContainsSubstring(res.Body, "ey") res.Body.Close() if err != nil { return nil, err } if err == nil { if res.StatusCode >= 200 && res.StatusCode < 300 && verifiedBodyResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resIDMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Docusign } trufflehog-3.60.0/pkg/detectors/docusign/docusign_test.go000066400000000000000000000062141451332102400235340ustar00rootroot00000000000000//go:build detectors // +build detectors package docusign import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDocusign_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } integrationKey := testSecrets.MustGetField("DOCUSIGN_INTEGRATION_KEY_ACTIVE") activeSecret := testSecrets.MustGetField("DOCUSIGN_SECRET_ACTIVE") inactiveSecret := testSecrets.MustGetField("DOCUSIGN_SECRET_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 docusign id %s and secret %s within", integrationKey, activeSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Docusign, Verified: true, RawV2: []byte(integrationKey + activeSecret), Redacted: integrationKey, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a docusign id %s and secret %s within", integrationKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Docusign, Verified: false, RawV2: []byte(integrationKey + inactiveSecret), Redacted: integrationKey, }, }, 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("Docusign.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Docusign.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) } } }) } } trufflehog-3.60.0/pkg/detectors/doppler/000077500000000000000000000000001451332102400201625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/doppler/doppler.go000066400000000000000000000041251451332102400221600ustar00rootroot00000000000000package doppler import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(dp\.pt\.[a-zA-Z0-9]{43})\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{"dp.pt."} } // FromData will find and optionally verify Doppler 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Doppler, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.doppler.com/v3/workplace", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Doppler } trufflehog-3.60.0/pkg/detectors/doppler/doppler_test.go000066400000000000000000000054601451332102400232220ustar00rootroot00000000000000//go:build detectors // +build detectors package doppler import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDoppler_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("DOPPLER") inactiveSecret := testSecrets.MustGetField("DOPPLER_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 doppler secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Doppler, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a doppler 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_Doppler, 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("Doppler.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Doppler.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dotmailer/000077500000000000000000000000001451332102400204755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dotmailer/dotmailer.go000066400000000000000000000050161451332102400230060ustar00rootroot00000000000000package dotmailer import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dotmailer"}) + `\b(apiuser-[a-z0-9]{12}@apiconnector.com)\b`) passPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dotmailer"}) + `\b([a-zA-Z0-9\S]{8,24})\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{"dotmailer"} } // FromData will find and optionally verify Dotmailer 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) passMatches := passPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range passMatches { if len(idMatch) != 2 { continue } resPassMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dotmailer, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://r3-api.dotmailer.com/v2/account-info", nil) if err != nil { continue } req.SetBasicAuth(resMatch, resPassMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resPassMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dotmailer } trufflehog-3.60.0/pkg/detectors/dotmailer/dotmailer_test.go000066400000000000000000000056301451332102400240470ustar00rootroot00000000000000//go:build detectors // +build detectors package dotmailer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDotmailer_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("DOTMAILER") pass := testSecrets.MustGetField("DOTMAILER_PASS") inactiveSecret := testSecrets.MustGetField("DOTMAILER_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 dotmailer secret %s within dotmailer pass %s", secret, pass)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dotmailer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dotmailer secret %s within dotmailer pass %s but not valid ", inactiveSecret, pass)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dotmailer, 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("Dotmailer.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dotmailer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dovico/000077500000000000000000000000001451332102400200005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dovico/dovico.go000066400000000000000000000050421451332102400216130ustar00rootroot00000000000000package dovico import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dovico"}) + `\b([0-9a-z]{32}\.[0-9a-z]{1,}\b)`) userPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dovico"}) + `\b([0-9a-z]{32}\.[0-9a-z]{1,}\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{"dovico"} } // FromData will find and optionally verify Dovico 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) userMatches := userPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, user := range userMatches { if len(user) != 2 { continue } resUser := strings.TrimSpace(user[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dovico, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.dovico.com/Employees/?version=7", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf(`WRAP access_token="client=%s&user_token=%s"`, resMatch, resUser)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dovico } trufflehog-3.60.0/pkg/detectors/dovico/dovico_test.go000066400000000000000000000057321451332102400226600ustar00rootroot00000000000000//go:build detectors // +build detectors package dovico import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDovico_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("DOVICO_CLIENT") user := testSecrets.MustGetField("DOVICO_USER") inactiveSecret := testSecrets.MustGetField("DOVICO_CLIENT_INACTIVE") inactiveUser := testSecrets.MustGetField("DOVICO_USER_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 dovico secret %s within dovico user %s ", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dovico, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dovico secret %s within dovico user %s but not valid", inactiveSecret, inactiveUser)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dovico, 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("Dovico.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dovico.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dronahq/000077500000000000000000000000001451332102400201515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dronahq/dronahq.go000066400000000000000000000041211451332102400221320ustar00rootroot00000000000000package dronahq import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dronahq"}) + `\b([a-z0-9]{50})\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{"dronahq"} } // FromData will find and optionally verify DronaHQ 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DronaHQ, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://plugin.api.dronahq.com/users/?tokenkey=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DronaHQ } trufflehog-3.60.0/pkg/detectors/dronahq/dronahq_test.go000066400000000000000000000054601451332102400232000ustar00rootroot00000000000000//go:build detectors // +build detectors package dronahq import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDronaHQ_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DRONAHQ") inactiveSecret := testSecrets.MustGetField("DRONAHQ_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 dronahq secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DronaHQ, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dronahq 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_DronaHQ, 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("DronaHQ.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DronaHQ.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) } } }) } } trufflehog-3.60.0/pkg/detectors/droneci/000077500000000000000000000000001451332102400201405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/droneci/droneci.go000066400000000000000000000041621451332102400221150ustar00rootroot00000000000000package droneci import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"droneci"}) + `\b([a-zA-Z0-9]{32})\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{"droneci"} } // FromData will find and optionally verify DroneCI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_DroneCI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.drone.io/api/user", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_DroneCI } trufflehog-3.60.0/pkg/detectors/droneci/droneci_test.go000066400000000000000000000054661451332102400231640ustar00rootroot00000000000000//go:build detectors // +build detectors package droneci import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDroneCI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DRONECI_TOKEN") inactiveSecret := testSecrets.MustGetField("DRONECI_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 droneci secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_DroneCI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a droneci 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_DroneCI, 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("DroneCI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("DroneCI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dropbox/000077500000000000000000000000001451332102400201725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dropbox/dropbox.go000066400000000000000000000036021451332102400221770ustar00rootroot00000000000000package dropbox import ( "context" "fmt" "net/http" "regexp" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( keyPat = regexp.MustCompile(`\b(sl\.[A-Za-z0-9\-\_]{130,140})\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{"sl."} } // FromData will find and optionally verify Dropbox 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) for _, match := range matches { s := detectors.Result{ DetectorType: detectorspb.DetectorType_Dropbox, Raw: []byte(match[1]), } if verify { baseURL := "https://api.dropboxapi.com/2/users/get_current_account" client := common.SaneHttpClient() req, err := http.NewRequestWithContext(ctx, "POST", baseURL, nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", match[1])) res, err := client.Do(req) if err == nil { res.Body.Close() // The request body is unused. // 200 means good key for get current user // 400 is bad (malformed) // 403 bad scope if res.StatusCode == http.StatusOK { s.Verified = true } } } if !s.Verified && detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s) } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dropbox } trufflehog-3.60.0/pkg/detectors/dropbox/dropbox_test.go000066400000000000000000000052071451332102400232410ustar00rootroot00000000000000//go:build detectors // +build detectors package dropbox import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDropbox_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DROPBOX") secretInactive := testSecrets.MustGetField("DROPBOX_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 dropbox secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dropbox, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dropbox secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dropbox, 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("Dropbox.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dropbox.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) } } }) } } trufflehog-3.60.0/pkg/detectors/duply/000077500000000000000000000000001451332102400176525ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/duply/duply.go000066400000000000000000000050751451332102400213450ustar00rootroot00000000000000package duply import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"duply"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"duply"}) + `\b([0-9A-Z]{7}-[0-9A-Z]{7}-[0-9A-Z]{7}-[0-9A-Z]{7})\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{"duply"} } // FromData will find and optionally verify Duply 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Duply, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://gen.duply.co/v1/usage", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.SetBasicAuth(resIdMatch, resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Duply } trufflehog-3.60.0/pkg/detectors/duply/duply_test.go000066400000000000000000000054721451332102400224050ustar00rootroot00000000000000package duply import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDuply_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DUPLY") id := testSecrets.MustGetField("DUPLY_USER") inactiveSecret := testSecrets.MustGetField("DUPLY_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 duply secret %s within duply %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Duply, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a duply secret %s within duply %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Duply, 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("Duply.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Duply.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dwolla/000077500000000000000000000000001451332102400177775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dwolla/dwolla.go000066400000000000000000000054061451332102400216150ustar00rootroot00000000000000package dwolla import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = 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{"dwolla"}) + `\b([a-zA-Z-0-9]{50})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dwolla"}) + `\b([a-zA-Z-0-9]{50})\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{"dwolla"} } // FromData will find and optionally verify Dwolla 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range idMatches { if len(match) != 2 { continue } idMatch := strings.TrimSpace(match[1]) for _, secret := range secretMatches { if len(secret) != 2 { continue } secretMatch := strings.TrimSpace(secret[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dwolla, Raw: []byte(idMatch), RawV2: []byte(idMatch + secretMatch), } if verify { data := fmt.Sprintf("%s:%s", idMatch, secretMatch) encoded := b64.StdEncoding.EncodeToString([]byte(data)) payload := strings.NewReader("grant_type=client_credentials") req, err := http.NewRequestWithContext(ctx, "POST", "https://api-sandbox.dwolla.com/token", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", encoded)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(idMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dwolla } trufflehog-3.60.0/pkg/detectors/dwolla/dwolla_test.go000066400000000000000000000056151451332102400226560ustar00rootroot00000000000000//go:build detectors // +build detectors package dwolla import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDwolla_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) } id := testSecrets.MustGetField("DWOLLA") secret := testSecrets.MustGetField("DWOLLA_SECRET") inactiveSecret := testSecrets.MustGetField("DWOLLA_SECRET_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 dwolla secret %s within dwolla id %s but verified", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dwolla, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dwolla secret %s within dwolla id %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dwolla, 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("Dwolla.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dwolla.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dynalist/000077500000000000000000000000001451332102400203445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dynalist/dynalist.go000066400000000000000000000047061451332102400225310ustar00rootroot00000000000000package dynalist import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dynalist"}) + `\b([a-zA-Z0-9-_]{128})\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{"dynalist"} } // FromData will find and optionally verify Dynalist 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dynalist, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`{"token": "%s"}`, resMatch)) req, err := http.NewRequestWithContext(ctx, "POST", "https://dynalist.io/api/v1/file/list", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"_code":"Ok"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dynalist } trufflehog-3.60.0/pkg/detectors/dynalist/dynalist_test.go000066400000000000000000000054721451332102400235710ustar00rootroot00000000000000//go:build detectors // +build detectors package dynalist import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDynalist_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("DYNALIST") inactiveSecret := testSecrets.MustGetField("DYNALIST_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 dynalist secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dynalist, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dynalist 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_Dynalist, 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("Dynalist.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dynalist.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) } } }) } } trufflehog-3.60.0/pkg/detectors/dyspatch/000077500000000000000000000000001451332102400203345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/dyspatch/dyspatch.go000066400000000000000000000043371451332102400225110ustar00rootroot00000000000000package dyspatch import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"dyspatch"}) + `\b([A-Z0-9]{52})\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{"dyspatch"} } // FromData will find and optionally verify Dyspatch 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Dyspatch, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.dyspatch.io/templates", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.dyspatch.2020.11+json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", 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) validResponse := strings.Contains(body, "limited_usage") || strings.Contains(body, "data") if validResponse { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Dyspatch } trufflehog-3.60.0/pkg/detectors/dyspatch/dyspatch_test.go000066400000000000000000000055001451332102400235410ustar00rootroot00000000000000//go:build detectors // +build detectors package dyspatch import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestDyspatch_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("DYSPATCH_TOKEN") inactiveSecret := testSecrets.MustGetField("DYSPATCH_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 dyspatch secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Dyspatch, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a dyspatch 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_Dyspatch, 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("Dyspatch.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Dyspatch.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) } } }) } } trufflehog-3.60.0/pkg/detectors/eagleeyenetworks/000077500000000000000000000000001451332102400220725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/eagleeyenetworks/eagleeyenetworks.go000066400000000000000000000052361451332102400260040ustar00rootroot00000000000000package eagleeyenetworks import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"eagleeyenetworks"}) + `\b([a-zA-Z0-9]{15})\b`) email = regexp.MustCompile(detectors.PrefixRegex([]string{"eagleeyenetworks"}) + `\b([a-zA-Z0-9]{3,20}@[a-zA-Z0-9]{2,12}.[a-zA-Z0-9]{2,5})\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{"eagleeyenetworks"} } // FromData will find and optionally verify EagleEyeNetworks 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) emailMatches := email.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } resEmailPatMatch := strings.TrimSpace(emailMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EagleEyeNetworks, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`{"username": "%s", "password": "%s"}`, resEmailPatMatch, resMatch)) req, err := http.NewRequestWithContext(ctx, "POST", "https://login.eagleeyenetworks.com/g/aaa/authenticate", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EagleEyeNetworks } trufflehog-3.60.0/pkg/detectors/eagleeyenetworks/eagleeyenetworks_test.go000066400000000000000000000057741451332102400270520ustar00rootroot00000000000000//go:build detectors // +build detectors package eagleeyenetworks import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEagleEyeNetworks_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("EAGLEEYENETWORKS") email := testSecrets.MustGetField("EAGLEEYENETWORKS_USER") inactiveSecret := testSecrets.MustGetField("EAGLEEYENETWORKS_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 eagleeyenetworks secret %s within eagleeyenetworks %s", secret, email)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EagleEyeNetworks, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a eagleeyenetworks secret %s within eagleeyenetworks %s but not valid", inactiveSecret, email)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EagleEyeNetworks, 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("EagleEyeNetworks.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("EagleEyeNetworks.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) } } }) } } trufflehog-3.60.0/pkg/detectors/easyinsight/000077500000000000000000000000001451332102400210445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/easyinsight/easyinsight.go000066400000000000000000000054431451332102400237300ustar00rootroot00000000000000package easyinsight import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"easyinsight", "easy-insight"}) + `\b([0-9Aa-zA-Z]{20})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"easyinsight", "easy-insight"}) + `\b([a-zA-Z0-9]{20})\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{"easyinsight", "easy-insight"} } // FromData will find and optionally verify EasyInsight 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EasyInsight, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { data := fmt.Sprintf("%s:%s", resIdMatch, resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://www.easy-insight.com/app/api/users.json", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EasyInsight } trufflehog-3.60.0/pkg/detectors/easyinsight/easyinsight_test.go000066400000000000000000000056461451332102400247740ustar00rootroot00000000000000//go:build detectors // +build detectors package easyinsight import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEasyInsight_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("EASYINSIGHT") inactiveSecret := testSecrets.MustGetField("EASYINSIGHT_INACTIVE") id := testSecrets.MustGetField("EASYINSIGHT_ID") 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 easyinsight secret %s within easyid %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EasyInsight, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a easyinsight secret %s within easyid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EasyInsight, 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("EasyInsight.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("EasyInsight.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ecostruxureit/000077500000000000000000000000001451332102400214425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ecostruxureit/ecostruxureit.go000066400000000000000000000043371451332102400247250ustar00rootroot00000000000000package ecostruxureit import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ecostruxureit"}) + `\b(AK1[0-9a-zA-Z\/]{50,55})\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{"ecostruxureit"} } // FromData will find and optionally verify EcoStruxureIT 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EcoStruxureIT, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ecostruxureit.com/rest/v1/organizations", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EcoStruxureIT } trufflehog-3.60.0/pkg/detectors/ecostruxureit/ecostruxureit_test.go000066400000000000000000000055021451332102400257570ustar00rootroot00000000000000package ecostruxureit import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEcoStruxureIT_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ECOSTRUXUREIT") inactiveSecret := testSecrets.MustGetField("ECOSTRUXUREIT_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 ecostruxureit secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EcoStruxureIT, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ecostruxureit 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_EcoStruxureIT, 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("EcoStruxureIT.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("EcoStruxureIT.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) } } }) } } trufflehog-3.60.0/pkg/detectors/edamam/000077500000000000000000000000001451332102400177415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/edamam/edamam.go000066400000000000000000000047571451332102400215310ustar00rootroot00000000000000package edamam import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"edamam"}) + `\b([0-9a-z]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"edamam"}) + `\b([0-9a-z]{8})\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{"edamam"} } // FromData will find and optionally verify Edamam 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(match) != 2 { continue } resId := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Edamam, Raw: []byte(resMatch), RawV2: []byte(resMatch + resId), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.edamam.com/auto-complete?app_id=%s&app_key=%s&q=%s", resId, resMatch, ""), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Edamam } trufflehog-3.60.0/pkg/detectors/edamam/edamam_test.go000066400000000000000000000055651451332102400225660ustar00rootroot00000000000000//go:build detectors // +build detectors package edamam import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEdamam_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("EDAMAM") id := testSecrets.MustGetField("EDAMAM_ID") inactiveSecret := testSecrets.MustGetField("EDAMAM_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 edamam secret %s within edamam id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Edamam, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a edamam secret %s within edamam id %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Edamam, 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("Edamam.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Edamam.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) } } }) } } trufflehog-3.60.0/pkg/detectors/edenai/000077500000000000000000000000001451332102400177425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/edenai/edenai.go000066400000000000000000000042331451332102400215200ustar00rootroot00000000000000package edenai import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"edenai"}) + `\b([a-zA-Z0-9]{36}.[a-zA-Z0-9]{92}.[a-zA-Z0-9_]{43})\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{"edenai"} } // FromData will find and optionally verify EdenAI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EdenAI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.edenai.run/v1/automl/text/project", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EdenAI } trufflehog-3.60.0/pkg/detectors/edenai/edenai_test.go000066400000000000000000000054461451332102400225660ustar00rootroot00000000000000//go:build detectors // +build detectors package edenai import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEdenAI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("EDENAI") inactiveSecret := testSecrets.MustGetField("EDENAI_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 edenai secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EdenAI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a edenai 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_EdenAI, 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("EdenAI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("EdenAI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/eightxeight/000077500000000000000000000000001451332102400210265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/eightxeight/eightxeight.go000066400000000000000000000053731451332102400236760ustar00rootroot00000000000000package eightxeight import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"8x8"}) + `\b([a-zA-Z0-9]{43})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"8x8"}) + `\b([a-zA-Z0-9_]{18,30})\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{"8x8"} } // FromData will find and optionally verify EightxEight 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EightxEight, Raw: []byte(resMatch), RawV2: []byte(resMatch + resIdMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout payload := strings.NewReader(`{"source":"abcde","destination":"+6512345678","text":"Hello World!","encoding":"AUTO"}`) req, err := http.NewRequest("POST", fmt.Sprintf("https://sms.8x8.com/api/v1/subaccounts/%s/messages", resIdMatch), payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EightxEight } trufflehog-3.60.0/pkg/detectors/eightxeight/eightxeight_test.go000066400000000000000000000055321451332102400247320ustar00rootroot00000000000000//go:build detectors // +build detectors package eightxeight import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEightxEight_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("EIGHTXEIGHT") id := testSecrets.MustGetField("EIGHTXEIGHT_ID") inactiveSecret := testSecrets.MustGetField("EIGHTXEIGHT_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 8x8 secret %s within 8x8 %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EightxEight, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a 8x8 secret %s within 8x8 %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EightxEight, 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("EightxEight.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("EightxEight.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/elasticemail/000077500000000000000000000000001451332102400211515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/elasticemail/elasticemail.go000066400000000000000000000040471451332102400241410ustar00rootroot00000000000000package elasticemail import ( "context" "encoding/json" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"elastic"}) + `\b([A-Za-z0-9_-]{96})\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{"elasticemail"} } // FromData will find and optionally verify ElasticEmail 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ElasticEmail, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.elasticemail.com/v2/account/profileoverview?apikey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { data, readErr := io.ReadAll(res.Body) res.Body.Close() if readErr == nil { var ResVar struct { Success bool `json:"success"` } if err := json.Unmarshal(data, &ResVar); err == nil { if ResVar.Success { s1.Verified = true } } } } } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ElasticEmail } trufflehog-3.60.0/pkg/detectors/elasticemail/elasticemail_test.go000066400000000000000000000055501451332102400252000ustar00rootroot00000000000000//go:build detectors // +build detectors package elasticemail import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestElasticEmail_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ELASTICEMAIL_TOKEN") inactiveSecret := testSecrets.MustGetField("ELASTICEMAIL_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 elasticemail secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ElasticEmail, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a elasticemail 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_ElasticEmail, 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("ElasticEmail.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ElasticEmail.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) } } }) } } trufflehog-3.60.0/pkg/detectors/enablex/000077500000000000000000000000001451332102400201335ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/enablex/enablex.go000066400000000000000000000046211451332102400221030ustar00rootroot00000000000000package enablex import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"enablex"}) + `\b([a-zA-Z0-9]{36})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"enablex"}) + `\b([a-z0-9]{24})\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{"enablex"} } // FromData will find and optionally verify Enablex 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Enablex, Raw: []byte(tokenPatMatch), } if verify { req, err := http.NewRequest("GET", "https://api.enablex.io/voice/v1/call", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Enablex } trufflehog-3.60.0/pkg/detectors/enablex/enablex_test.go000066400000000000000000000055161451332102400231460ustar00rootroot00000000000000//go:build detectors // +build detectors package enablex import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEnablex_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ENABLEX") user := testSecrets.MustGetField("ENABLEX_USER") inactiveSecret := testSecrets.MustGetField("ENABLEX_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 enablex secret %s within enablex %s", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Enablex, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a enablex secret %s within enablex %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Enablex, 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("Enablex.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Enablex.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/endpoint_customizer.go000066400000000000000000000017431451332102400231550ustar00rootroot00000000000000package detectors import ( "fmt" "github.com/trufflesecurity/trufflehog/v3/pkg/common" ) // EndpointSetter implements a sensible default for the SetEndpoints function // of the EndpointCustomizer interface. A detector can embed this struct to // gain the functionality. type EndpointSetter struct { endpoints []string } func (e *EndpointSetter) SetEndpoints(endpoints ...string) error { if len(endpoints) == 0 { return fmt.Errorf("at least one endpoint required") } deduped := make([]string, 0, len(endpoints)) for _, endpoint := range endpoints { common.AddStringSliceItem(endpoint, &deduped) } e.endpoints = deduped return nil } func (e *EndpointSetter) Endpoints(defaultEndpoint string) []string { // The only valid time len(e.endpoints) == 0 is when EndpointSetter is // initializetd to its default state. That means SetEndpoints was never // called and we should use the default. if len(e.endpoints) == 0 { return []string{defaultEndpoint} } return e.endpoints } trufflehog-3.60.0/pkg/detectors/endpoint_customizer_test.go000066400000000000000000000006001451332102400242030ustar00rootroot00000000000000package detectors import ( "testing" "github.com/stretchr/testify/assert" ) func TestEmbeddedEndpointSetter(t *testing.T) { type Scanner struct{ EndpointSetter } var s Scanner assert.Equal(t, []string{"baz"}, s.Endpoints("baz")) assert.NoError(t, s.SetEndpoints("foo", "bar")) assert.Error(t, s.SetEndpoints()) assert.Equal(t, []string{"foo", "bar"}, s.Endpoints("baz")) } trufflehog-3.60.0/pkg/detectors/enigma/000077500000000000000000000000001451332102400177555ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/enigma/enigma.go000066400000000000000000000045621451332102400215530ustar00rootroot00000000000000package enigma import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"enigma"}) + `\b([a-zA-Z0-9]{40})\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{"enigma"} } // FromData will find and optionally verify Enigma 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Enigma, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"name":"Enigma Technologies, Inc.","person":{"first_name":"","last_name":""},"address":{"street_address1":"245 5th Ave","street_address2":"","city":"New York","state":"NY","postal_code":"10016"}}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.enigma.com/businesses/match", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Enigma } trufflehog-3.60.0/pkg/detectors/enigma/enigma_test.go000066400000000000000000000054461451332102400226140ustar00rootroot00000000000000//go:build detectors // +build detectors package enigma import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEnigma_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("ENIGMA") inactiveSecret := testSecrets.MustGetField("ENIGMA_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 enigma secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Enigma, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a enigma 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_Enigma, 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("Enigma.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Enigma.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) } } }) } } trufflehog-3.60.0/pkg/detectors/envoyapikey/000077500000000000000000000000001451332102400210605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/envoyapikey/envoyapikey.go000066400000000000000000000046231451332102400237570ustar00rootroot00000000000000package envoyapikey import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"envoy"}) + `\b([a-zA-Z0-9]{220})\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{"envoy"} } // FromData will find and optionally verify Envoy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EnvoyApiKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.envoy.com/v1/locations", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.envoy+json; version=3") req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() body, _ := io.ReadAll(res.Body) // Invalid API keys can also return status code 200, so check for presence of 'status 401' in response body. if res.StatusCode >= 200 && res.StatusCode < 300 || res.StatusCode == 403 { if !strings.Contains(string(body), `"status":401`) { s1.Verified = true } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EnvoyApiKey } trufflehog-3.60.0/pkg/detectors/envoyapikey/envoyapikey_test.go000066400000000000000000000055311451332102400250150ustar00rootroot00000000000000//go:build detectors // +build detectors package envoyapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEnvoyapikey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ENVOYAPIKEY") inactiveSecret := testSecrets.MustGetField("ENVOYAPIKEY_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 envoyapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EnvoyApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a envoyapikey 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_EnvoyApiKey, 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("Envoyapikey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Envoyapikey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/etherscan/000077500000000000000000000000001451332102400204715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/etherscan/etherscan.go000066400000000000000000000044001451332102400227720ustar00rootroot00000000000000package etherscan import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"etherscan"}) + `\b([0-9A-Z]{34})\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{"etherscan"} } // FromData will find and optionally verify Etherscan 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Etherscan, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.etherscan.io/api?module=account&action=balance&address=0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae&tag=latest&apikey="+resMatch, nil) if err != nil { continue } 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, `"OK"`) { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Etherscan } trufflehog-3.60.0/pkg/detectors/etherscan/etherscan_test.go000066400000000000000000000055041451332102400240370ustar00rootroot00000000000000//go:build detectors // +build detectors package etherscan import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEtherscan_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ETHERSCAN") inactiveSecret := testSecrets.MustGetField("ETHERSCAN_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 etherscan secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Etherscan, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a etherscan 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_Etherscan, 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("Etherscan.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Etherscan.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ethplorer/000077500000000000000000000000001451332102400205215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ethplorer/ethplorer.go000066400000000000000000000045221451332102400230570ustar00rootroot00000000000000package ethplorer import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ethplorer"}) + `\b([a-z0-9A-Z-]{22})\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{"ethplorer"} } // FromData will find and optionally verify Ethplorer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Ethplorer, Raw: []byte(resMatch), } if verify { payload := strings.NewReader("apiKey=" + resMatch + "&addresses=0xb2930b35844a230f00e51431acae96fe543a0347%2C0xb52d3141ee731fac89927476c6a5207b37cd72ff") req, err := http.NewRequestWithContext(ctx, "POST", "https://api-mon.ethplorer.io/createPool", payload) if err != nil { continue } req.Header.Add("accept", "application/json") req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ethplorer } trufflehog-3.60.0/pkg/detectors/ethplorer/ethplorer_test.go000066400000000000000000000055041451332102400241170ustar00rootroot00000000000000//go:build detectors // +build detectors package ethplorer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEthplorer_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("ETHPLORER") inactiveSecret := testSecrets.MustGetField("ETHPLORER_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 ethplorer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Ethplorer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ethplorer 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_Ethplorer, 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("Ethplorer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ethplorer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/etsyapikey/000077500000000000000000000000001451332102400207045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/etsyapikey/etsyapikey.go000066400000000000000000000041111451332102400234170ustar00rootroot00000000000000package etsyapikey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"etsy"}) + `\b([a-zA-Z-0-9]{24})\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{"etsy"} } // FromData will find and optionally verify EtsyApiKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_EtsyApiKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://openapi.etsy.com/v2/listings/active?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_EtsyApiKey } trufflehog-3.60.0/pkg/detectors/etsyapikey/etsyapikey_test.go000066400000000000000000000055101451332102400244620ustar00rootroot00000000000000//go:build detectors // +build detectors package etsyapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEtsyApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ETSYAPIKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("ETSYAPIKEY_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 etsy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_EtsyApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a etsy 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_EtsyApiKey, 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("EtsyApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("EtsyApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/everhour/000077500000000000000000000000001451332102400203545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/everhour/everhour.go000066400000000000000000000042671451332102400225530ustar00rootroot00000000000000package everhour import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"everhour"}) + `\b([0-9Aa-f]{4}-[0-9a-f]{4}-[0-9a-f]{6}-[0-9a-f]{6}-[0-9a-f]{8})\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{"everhour"} } // FromData will find and optionally verify Everhour 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Everhour, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.everhour.com/clients", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Everhour } trufflehog-3.60.0/pkg/detectors/everhour/everhour_test.go000066400000000000000000000054721451332102400236110ustar00rootroot00000000000000//go:build detectors // +build detectors package everhour import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestEverhour_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("EVERHOUR") inactiveSecret := testSecrets.MustGetField("EVERHOUR_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 everhour secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Everhour, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a everhour 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_Everhour, 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("Everhour.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Everhour.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) } } }) } } trufflehog-3.60.0/pkg/detectors/exchangerateapi/000077500000000000000000000000001451332102400216455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/exchangerateapi/exchangerateapi.go000066400000000000000000000044201451332102400253240ustar00rootroot00000000000000package exchangerateapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"exchangerate"}) + `\b([a-z0-9]{24})\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{"exchangerate"} } // FromData will find and optionally verify ExchangeRateAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ExchangeRateAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://v6.exchangerate-api.com/v6/%s/latest/USD", resMatch), nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.exchangerateapi+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ExchangeRateAPI } trufflehog-3.60.0/pkg/detectors/exchangerateapi/exchangerateapi_test.go000066400000000000000000000056001451332102400263640ustar00rootroot00000000000000//go:build detectors // +build detectors package exchangerateapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestExchangeRateAPI_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("EXCHANGERATEAPI") inactiveSecret := testSecrets.MustGetField("EXCHANGERATEAPI_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 exchangerateapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ExchangeRateAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a exchangerateapi 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_ExchangeRateAPI, 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("ExchangeRateAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ExchangeRateAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/exchangeratesapi/000077500000000000000000000000001451332102400220305ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/exchangeratesapi/exchangeratesapi.go000066400000000000000000000042071451332102400256750ustar00rootroot00000000000000package exchangeratesapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"exchangerates"}) + `\b([a-z0-9]{32})\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{"exchangerates"} } // FromData will find and optionally verify ExchangeRatesAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ExchangeRatesAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.exchangeratesapi.io/v1/latest?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ExchangeRatesAPI } trufflehog-3.60.0/pkg/detectors/exchangeratesapi/exchangeratesapi_test.go000066400000000000000000000056121451332102400267350ustar00rootroot00000000000000//go:build detectors // +build detectors package exchangeratesapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestExchangeRatesAPI_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("EXCHANGERATESAPI") inactiveSecret := testSecrets.MustGetField("EXCHANGERATESAPI_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 exchangeratesapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ExchangeRatesAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a exchangeratesapi 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_ExchangeRatesAPI, 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("ExchangeRatesAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ExchangeRatesAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/exportsdk/000077500000000000000000000000001451332102400205405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/exportsdk/exportsdk.go000066400000000000000000000050451451332102400231160ustar00rootroot00000000000000package exportsdk import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"exportsdk"}) + `\b([0-9a-z]{5,15}_[0-9a-z-]{36})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"exportsdk"}) + `\b([0-9a-z-]{36})\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{"exportsdk"} } // FromData will find and optionally verify ExportSDK 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ExportSDK, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{ "templateId": "` + resIdMatch + `"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.exportsdk.com/v1/pdf", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ExportSDK } trufflehog-3.60.0/pkg/detectors/exportsdk/exportsdk_test.go000066400000000000000000000056341451332102400241610ustar00rootroot00000000000000//go:build detectors // +build detectors package exportsdk import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestExportSDK_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("EXPORTSDK") id := testSecrets.MustGetField("EXPORTSDK_TEMPLATE") inactiveSecret := testSecrets.MustGetField("EXPORTSDK_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 exportsdk secret %s within exportsdk %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ExportSDK, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a exportsdk secret %s within exportsdk %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ExportSDK, 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("ExportSDK.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ExportSDK.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) } } }) } } trufflehog-3.60.0/pkg/detectors/extractorapi/000077500000000000000000000000001451332102400212225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/extractorapi/extractorapi.go000066400000000000000000000041611451332102400242600ustar00rootroot00000000000000package extractorapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"extractorapi"}) + `\b([a-zA-Z-0-9]{40})\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{"extractorapi"} } // FromData will find and optionally verify ExtractorAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ExtractorAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://extractorapi.com/api/v1/extractor?apikey="+resMatch+"&url=example.com", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ExtractorAPI } trufflehog-3.60.0/pkg/detectors/extractorapi/extractorapi_test.go000066400000000000000000000055421451332102400253230ustar00rootroot00000000000000//go:build detectors // +build detectors package extractorapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestExtractorAPI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("EXTRACTORAPI") inactiveSecret := testSecrets.MustGetField("EXTRACTORAPI_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 extractorapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ExtractorAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a extractorapi 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_ExtractorAPI, 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("ExtractorAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("ExtractorAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/facebookoauth/000077500000000000000000000000001451332102400213275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/facebookoauth/facebookoauth.go000066400000000000000000000051441451332102400244740ustar00rootroot00000000000000package facebookoauth import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. apiIdPat = regexp.MustCompile(detectors.PrefixRegex([]string{"facebook"}) + `\b([0-9]{15,18})\b`) // not actually sure of the upper bound apiSecretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"facebook"}) + `\b([A-Za-z0-9]{32})\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{"facebook"} } // FromData will find and optionally verify FacebookOAuth 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) apiIdMatches := apiIdPat.FindAllStringSubmatch(dataStr, -1) apiSecretMatches := apiSecretPat.FindAllStringSubmatch(dataStr, -1) for _, apiIdMatch := range apiIdMatches { if len(apiIdMatch) != 2 { continue } apiIdRes := strings.TrimSpace(apiIdMatch[1]) for _, apiSecretMatch := range apiSecretMatches { if len(apiSecretMatch) != 2 { continue } apiSecretRes := strings.TrimSpace(apiSecretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FacebookOAuth, Redacted: apiIdRes, Raw: []byte(apiSecretRes), RawV2: []byte(apiIdRes + apiSecretRes), } if verify { // thanks https://stackoverflow.com/questions/15621471/validate-a-facebook-app-id-and-app-secret req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://graph.facebook.com/%s?fields=roles&access_token=%s|%s", apiIdRes, apiIdRes, apiSecretRes), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(apiIdRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FacebookOAuth } trufflehog-3.60.0/pkg/detectors/facebookoauth/facebookoauth_test.go000066400000000000000000000060141451332102400255300ustar00rootroot00000000000000//go:build detectors // +build detectors package facebookoauth import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFacebookOAuth_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } appId := testSecrets.MustGetField("FACEBOOK_APP_ID") appSecret := testSecrets.MustGetField("FACEBOOK_APP_SECRET") inactiveAppSecret := testSecrets.MustGetField("FACEBOOK_APP_SECRET_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 facebook appid %s and secret %s within", appId, appSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FacebookOAuth, Redacted: appId, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a facebook appid %s and secret %s within but not valid", inactiveAppSecret, appId)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FacebookOAuth, Redacted: appId, 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("FacebookOAuth.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FacebookOAuth.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) } } }) } } trufflehog-3.60.0/pkg/detectors/faceplusplus/000077500000000000000000000000001451332102400212235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/faceplusplus/faceplusplus.go000066400000000000000000000051461451332102400242660ustar00rootroot00000000000000package faceplusplus import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"faceplusplus"}) + `\b([0-9a-zA-Z_-]{32})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"faceplusplus"}) + `\b([0-9a-zA-Z_-]{32})\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{"faceplusplus"} } // FromData will find and optionally verify Faceplusplus 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecret := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FacePlusPlus, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("https://api-us.faceplusplus.com/facepp/v3/faceset/getfacesets?api_key=%s&api_secret=%s", resMatch, resSecret), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FacePlusPlus } trufflehog-3.60.0/pkg/detectors/faceplusplus/faceplusplus_test.go000066400000000000000000000057161451332102400253300ustar00rootroot00000000000000//go:build detectors // +build detectors package faceplusplus import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFaceplusplus_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) } key := testSecrets.MustGetField("FACEPLUSPLUS_KEY") secret := testSecrets.MustGetField("FACEPLUSPLUS_SECRET") inactiveSecret := testSecrets.MustGetField("FACEPLUSPLUS_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 faceplusplus key %s within faceplusplus secret %s", key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FacePlusPlus, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a facepluspluskey %s within faceplusplussecret %s but not valid", key, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FacePlusPlus, 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("Faceplusplus.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Faceplusplus.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fakejson/000077500000000000000000000000001451332102400203155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fakejson/fakejson.go000066400000000000000000000050031451332102400224420ustar00rootroot00000000000000package fakejson import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fakejson"}) + `\b([a-zA-Z0-9]{22})\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{"fakejson"} } // FromData will find and optionally verify FakeJSON 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FakeJSON, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`{"token":"%s","data":{"user_name":"nameFirst","user_email":"internetEmail","_repeat":3}}`, resMatch)) req, err := http.NewRequestWithContext(ctx, "POST", "https://app.fakejson.com/q", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"user_email":`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FakeJSON } trufflehog-3.60.0/pkg/detectors/fakejson/fakejson_test.go000066400000000000000000000054721451332102400235130ustar00rootroot00000000000000//go:build detectors // +build detectors package fakejson import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFakeJSON_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("FAKEJSON") inactiveSecret := testSecrets.MustGetField("FAKEJSON_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 fakejson secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FakeJSON, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fakejson 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_FakeJSON, 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("FakeJSON.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FakeJSON.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) } } }) } } trufflehog-3.60.0/pkg/detectors/falsepositives.go000066400000000000000000000062031451332102400221050ustar00rootroot00000000000000package detectors import ( _ "embed" "math" "strings" "unicode" ) var DefaultFalsePositives = []FalsePositive{"example", "xxxxxx", "aaaaaa", "abcde", "00000", "sample", "www"} type FalsePositive string //go:embed "badlist.txt" var badList []byte //go:embed "words.txt" var wordList []byte //go:embed "programmingbooks.txt" var programmingBookWords []byte type Wordlists struct { wordList []string badList []string programmingBookWords []string } var FalsePositiveWordlists = Wordlists{ wordList: bytesToCleanWordList(wordList), badList: bytesToCleanWordList(badList), programmingBookWords: bytesToCleanWordList(programmingBookWords), } // IsKnownFalsePositives will not return a valid secret finding if any of the disqualifying conditions are met // Currently that includes: No number, english word in key, or matches common example pattens. // Only the secret key material should be passed into this function func IsKnownFalsePositive(match string, falsePositives []FalsePositive, wordCheck bool) bool { for _, fp := range falsePositives { if strings.Contains(strings.ToLower(match), string(fp)) { return true } } if wordCheck { // check against common substring badlist if hasDictWord(FalsePositiveWordlists.badList, match) { return true } // check for dictionary word substrings if hasDictWord(FalsePositiveWordlists.wordList, match) { return true } // check for programming book token substrings if hasDictWord(FalsePositiveWordlists.programmingBookWords, match) { return true } } return false } func hasDictWord(wordList []string, token string) bool { lower := strings.ToLower(token) for _, word := range wordList { if strings.Contains(lower, word) { return true } } return false } func HasDigit(key string) bool { for _, ch := range key { if unicode.IsDigit(ch) { return true } } return false } func bytesToCleanWordList(data []byte) []string { words := []string{} for _, word := range strings.Split(string(data), "\n") { if strings.TrimSpace(word) != "" { words = append(words, strings.TrimSpace(strings.ToLower(word))) } } return words } func StringShannonEntropy(input string) float64 { chars := make(map[rune]float64) inverseTotal := 1 / float64(len(input)) // precompute the inverse for _, char := range input { chars[char]++ } entropy := 0.0 for _, count := range chars { probability := count * inverseTotal entropy += probability * math.Log2(probability) } return -entropy } // FilterResultsWithEntropy filters out determinately unverified results that have a shannon entropy below the given value. func FilterResultsWithEntropy(results []Result, entropy float64) []Result { filteredResults := []Result{} for _, result := range results { if !result.Verified && result.VerificationError == nil { if result.RawV2 != nil { if StringShannonEntropy(string(result.RawV2)) >= entropy { filteredResults = append(filteredResults, result) } } else { if StringShannonEntropy(string(result.Raw)) >= entropy { filteredResults = append(filteredResults, result) } } } } return filteredResults } trufflehog-3.60.0/pkg/detectors/falsepositives_test.go000066400000000000000000000031421451332102400231430ustar00rootroot00000000000000//go:build detectors // +build detectors package detectors import ( _ "embed" "testing" ) func TestIsFalsePositive(t *testing.T) { type args struct { match string falsePositives []FalsePositive } tests := []struct { name string args args want bool }{ { name: "fp", args: args{ match: "example", falsePositives: DefaultFalsePositives, }, want: true, }, { name: "not fp", args: args{ match: "notafp123", falsePositives: DefaultFalsePositives, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := IsKnownFalsePositive(tt.args.match, tt.args.falsePositives, false); got != tt.want { t.Errorf("IsKnownFalsePositive() = %v, want %v", got, tt.want) } }) } } func TestStringShannonEntropy(t *testing.T) { type args struct { input string } tests := []struct { name string args args want float64 }{ { name: "entropy 1", args: args{ input: "aaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, want: 0, }, { name: "entropy 2", args: args{ input: "aaaaaaaaaaaaaaaaaaaaaaaaaaab", }, want: 0.22228483068568816, }, { name: "entropy 3", args: args{ input: "aaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaab", }, want: 0.22228483068568816, }, { name: "empty", args: args{ input: "", }, want: 0.0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := StringShannonEntropy(tt.args.input); got != tt.want { t.Errorf("StringShannonEntropy() = %v, want %v", got, tt.want) } }) } } trufflehog-3.60.0/pkg/detectors/fastforex/000077500000000000000000000000001451332102400205165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fastforex/fastforex.go000066400000000000000000000041321451332102400230460ustar00rootroot00000000000000package fastforex import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fastforex"}) + `\b([a-z0-9-]{28})\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{"fastforex"} } // FromData will find and optionally verify FastForex 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FastForex, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.fastforex.io/fetch-all?api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FastForex } trufflehog-3.60.0/pkg/detectors/fastforex/fastforex_test.go000066400000000000000000000055041451332102400241110ustar00rootroot00000000000000//go:build detectors // +build detectors package fastforex import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFastForex_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FASTFOREX") inactiveSecret := testSecrets.MustGetField("FASTFOREX_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 fastforex secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FastForex, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fastforex 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_FastForex, 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("FastForex.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FastForex.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fastlypersonaltoken/000077500000000000000000000000001451332102400226245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fastlypersonaltoken/fastlypersonaltoken.go000066400000000000000000000053111451332102400272620ustar00rootroot00000000000000package fastlypersonaltoken import ( "context" "encoding/json" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fastly"}) + `\b([A-Za-z0-9_-]{32})\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{"fastly"} } type fastlyUserRes struct { Login string `json:"login"` Name string `json:"name"` Role string `json:"role"` TwoFactorAuthEnabled bool `json:"two_factor_auth_enabled"` Locked bool `json:"locked"` } // FromData will find and optionally verify FastlyPersonalToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FastlyPersonalToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.fastly.com/current_user", nil) if err != nil { continue } req.Header.Add("Fastly-Key", resMatch) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { var userRes fastlyUserRes err = json.Unmarshal(bodyBytes, &userRes) if err != nil { continue } s1.Verified = true s1.ExtraData = map[string]string{ "username": userRes.Login, "name": userRes.Name, "role": userRes.Role, "locked": fmt.Sprintf("%t", userRes.Locked), "two_factor_auth_enabled": fmt.Sprintf("%t", userRes.TwoFactorAuthEnabled), } } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FastlyPersonalToken } trufflehog-3.60.0/pkg/detectors/fastlypersonaltoken/fastlypersonaltoken_test.go000066400000000000000000000056561451332102400303350ustar00rootroot00000000000000//go:build detectors // +build detectors package fastlypersonaltoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFastlyPersonalToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FASTLYPERSONALTOKEN_TOKEN") inactiveSecret := testSecrets.MustGetField("FASTLYPERSONALTOKEN_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 fastlypersonaltoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FastlyPersonalToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fastlypersonaltoken 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_FastlyPersonalToken, 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("FastlyPersonalToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FastlyPersonalToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/feedier/000077500000000000000000000000001451332102400201205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/feedier/feedier.go000066400000000000000000000042461451332102400220600ustar00rootroot00000000000000package feedier import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"feedier"}) + `\b([a-z0-9A-Z]{32})\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{"feedier"} } // FromData will find and optionally verify Feedier 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Feedier, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.feedier.com/v1/carriers", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Feedier } trufflehog-3.60.0/pkg/detectors/feedier/feedier_test.go000066400000000000000000000054661451332102400231240ustar00rootroot00000000000000//go:build detectors // +build detectors package feedier import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFeedier_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FEEDIER_TOKEN") inactiveSecret := testSecrets.MustGetField("FEEDIER_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 feedier secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Feedier, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a feedier 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_Feedier, 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("Feedier.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Feedier.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fetchrss/000077500000000000000000000000001451332102400203365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fetchrss/fetchrss.go000066400000000000000000000040401451332102400225040ustar00rootroot00000000000000package fetchrss import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fetchrss"}) + `\b([0-9A-Za-z.]{40})\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{"fetchrss"} } // FromData will find and optionally verify Fetchrss 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fetchrss, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://fetchrss.com/api/v1/feed/list?auth="+resMatch, nil) if err != nil { continue } 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, "Not authorised") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fetchrss } trufflehog-3.60.0/pkg/detectors/fetchrss/fetchrss_test.go000066400000000000000000000055001451332102400235450ustar00rootroot00000000000000//go:build detectors // +build detectors package fetchrss import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFetchrss_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FETCHRSS_TOKEN") inactiveSecret := testSecrets.MustGetField("FETCHRSS_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 fetchrss secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fetchrss, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fetchrss 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_Fetchrss, 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("Fetchrss.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fetchrss.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fibery/000077500000000000000000000000001451332102400177755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fibery/fibery.go000066400000000000000000000051661451332102400216140ustar00rootroot00000000000000package fibery import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fibery"}) + `\b([0-9a-f]{8}.[0-9a-f]{35})\b`) domainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fibery", "domain"}) + `\b([0-9A-Za-z]{2,40})\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{"fibery"} } // FromData will find and optionally verify Fibery 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) domainMatches := domainPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, domainMatch := range domainMatches { if len(domainMatch) != 2 { continue } resDomainMatch := strings.TrimSpace(domainMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fibery, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("https://%s.fibery.io/api/commands", resDomainMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fibery } trufflehog-3.60.0/pkg/detectors/fibery/fibery_test.go000066400000000000000000000056161451332102400226530ustar00rootroot00000000000000//go:build detectors // +build detectors package fibery import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFibery_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FIBERY") domain := testSecrets.MustGetField("FIBERY_DOMAIN") inactiveSecret := testSecrets.MustGetField("FIBERY_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 fibery secret %s within fibery domain %s ", secret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fibery, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fibery secret %s within fibery domain %s but not valid", inactiveSecret, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fibery, 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("Fibery.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fibery.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) } } }) } } trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/000077500000000000000000000000001451332102400235675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/v1/000077500000000000000000000000001451332102400241155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken.go000066400000000000000000000044631451332102400315250ustar00rootroot00000000000000package figmapersonalaccesstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (Scanner) Version() int { return 1 } var ( defaultClient = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"figma"}) + `\b([0-9]{6}-[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"figma"} } // FromData will find and optionally verify FigmaPersonalAccessToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Raw: []byte(resMatch), } if verify { client := s.client if client == nil { client = defaultClient } req, err := http.NewRequestWithContext(ctx, "GET", "https://api.figma.com/v1/me", nil) if err != nil { continue } req.Header.Add("X-Figma-Token", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode != 403 { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FigmaPersonalAccessToken } trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/v1/figmapersonalaccesstoken_test.go000066400000000000000000000105331451332102400325570ustar00rootroot00000000000000//go:build detectors // +build detectors package figmapersonalaccesstoken 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 TestFigmaPersonalAccessToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FIGMAPERSONALACCESSTOKEN_TOKEN") inactiveSecret := testSecrets.MustGetField("FIGMAPERSONALACCESSTOKEN_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken 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_FigmaPersonalAccessToken, Verified: false, }, }, wantErr: false, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { 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("FigmaPersonalAccessToken.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("FigmaPersonalAccessToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/v2/000077500000000000000000000000001451332102400241165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2.go000066400000000000000000000044321451332102400321310ustar00rootroot00000000000000package figmapersonalaccesstoken import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (Scanner) Version() int { return 2 } var ( defaultClient = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"figma"}) + `\b(fig[d|((u|o)(r|h)?)]_[a-z0-9A-Z_-]{40})\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{"figma"} } // FromData will find and optionally verify FigmaPersonalAccessToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Raw: []byte(resMatch), } if verify { client := s.client if client == nil { client = defaultClient } req, err := http.NewRequestWithContext(ctx, "GET", "https://api.figma.com/v1/me", nil) if err != nil { continue } req.Header.Add("X-Figma-Token", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode != 403 { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FigmaPersonalAccessToken } trufflehog-3.60.0/pkg/detectors/figmapersonalaccesstoken/v2/figmapersonalaccesstoken_v2_test.go000066400000000000000000000105411451332102400331660ustar00rootroot00000000000000//go:build detectors // +build detectors package figmapersonalaccesstoken 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 TestFigmaPersonalAccessToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FIGMAPERSONALACCESSTOKEN_V2_TOKEN") inactiveSecret := testSecrets.MustGetField("FIGMAPERSONALACCESSTOKEN_V2_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken 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_FigmaPersonalAccessToken, Verified: false, }, }, wantErr: false, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a figmapersonalaccesstoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FigmaPersonalAccessToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { 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("FigmaPersonalAccessToken.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("FigmaPersonalAccessToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fileio/000077500000000000000000000000001451332102400177645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fileio/fileio.go000066400000000000000000000044531451332102400215700ustar00rootroot00000000000000package fileio import ( "context" "encoding/json" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fileio"}) + `\b([A-Z0-9.-]{39})\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{"fileio"} } // FromData will find and optionally verify FileIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FileIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://file.io/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { isJson := json.Valid(bodyBytes) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if isJson { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FileIO } trufflehog-3.60.0/pkg/detectors/fileio/fileio_test.go000066400000000000000000000054461451332102400226320ustar00rootroot00000000000000//go:build detectors // +build detectors package fileio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFileIO_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("FILEIO") inactiveSecret := testSecrets.MustGetField("FILEIO_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 fileio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FileIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fileio 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_FileIO, 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("FileIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FileIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/finage/000077500000000000000000000000001451332102400177465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/finage/finage.go000066400000000000000000000041401451332102400215250ustar00rootroot00000000000000package finage import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(API_KEY[0-9A-Z]{32})\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{"finage"} } // FromData will find and optionally verify Finage 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Finage, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.finage.co.uk/symbol-list/crypto?apikey=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Finage } trufflehog-3.60.0/pkg/detectors/finage/finage_test.go000066400000000000000000000054461451332102400225760ustar00rootroot00000000000000//go:build detectors // +build detectors package finage import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFinage_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("FINAGE") inactiveSecret := testSecrets.MustGetField("FINAGE_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 finage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Finage, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a finage 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_Finage, 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("Finage.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Finage.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) } } }) } } trufflehog-3.60.0/pkg/detectors/financialmodelingprep/000077500000000000000000000000001451332102400230475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/financialmodelingprep/financialmodelingprep.go000066400000000000000000000050431451332102400277320ustar00rootroot00000000000000package financialmodelingprep import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"financialmodelingprep"}) + `\b([a-zA-Z0-9]{32})\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{"financialmodelingprep"} } // FromData will find and optionally verify FinancialModelingPrep 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FinancialModelingPrep, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://financialmodelingprep.com/api/v3/financial-statement-symbol-lists?apikey=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) bodyString := string(bodyBytes) if err == nil { // valid response should be an array of currencies // error response is in json validResponse := strings.Contains(bodyString, `[ "`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FinancialModelingPrep } trufflehog-3.60.0/pkg/detectors/financialmodelingprep/financialmodelingprep_test.go000066400000000000000000000056741451332102400310030ustar00rootroot00000000000000//go:build detectors // +build detectors package financialmodelingprep import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFinancialModelingPrep_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("FINANCIALMODELINGPREP") inactiveSecret := testSecrets.MustGetField("FINANCIALMODELINGPREP_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 financialmodelingprep secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FinancialModelingPrep, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a financialmodelingprep 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_FinancialModelingPrep, 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("FinancialModelingPrep.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FinancialModelingPrep.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) } } }) } } trufflehog-3.60.0/pkg/detectors/findl/000077500000000000000000000000001451332102400176115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/findl/findl.go000066400000000000000000000042751451332102400212440ustar00rootroot00000000000000package findl import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"findl"}) + `\b([a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12})\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{"findl"} } // FromData will find and optionally verify Findl 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Findl, Raw: []byte(resMatch), } if verify { timeout := 5 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://api.findl.com/v1.0/query?limit=6", nil) if err != nil { continue } req.Header.Add("X-API-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Findl } trufflehog-3.60.0/pkg/detectors/findl/findl_test.go000066400000000000000000000054341451332102400223010ustar00rootroot00000000000000//go:build detectors // +build detectors package findl import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFindl_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FINDL") inactiveSecret := testSecrets.MustGetField("FINDL_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 findl secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Findl, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a findl 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_Findl, 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("Findl.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Findl.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) } } }) } } trufflehog-3.60.0/pkg/detectors/finnhub/000077500000000000000000000000001451332102400201465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/finnhub/finnhub.go000066400000000000000000000042121451332102400221250ustar00rootroot00000000000000package finnhub import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"finnhub"}) + `\b([0-9a-z]{20})\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{"finnhub"} } // FromData will find and optionally verify Finnhub 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Finnhub, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://finnhub.io/api/v1/calendar/economic?token=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Finnhub } trufflehog-3.60.0/pkg/detectors/finnhub/finnhub_test.go000066400000000000000000000054601451332102400231720ustar00rootroot00000000000000//go:build detectors // +build detectors package finnhub import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFinnhub_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("FINNHUB") inactiveSecret := testSecrets.MustGetField("FINNHUB_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 finnhub secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Finnhub, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a finnhub 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_Finnhub, 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("Finnhub.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Finnhub.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fixerio/000077500000000000000000000000001451332102400201625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fixerio/fixerio.go000066400000000000000000000047551451332102400221710ustar00rootroot00000000000000package fixerio import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fixer"}) + `\b([A-Za-z0-9]{32})\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{"fixer"} } // FromData will find and optionally verify FixerIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FixerIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://data.fixer.io/api/latest?access_key="+resMatch, nil) if err != nil { continue } 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 client_id and client_secret is valid -> 403 {"error":"invalid_grant","error_description":"Invalid authorization code"} // if invalid -> 401 {"error":"access_denied","error_description":"Unauthorized"} // ingenious! validResponse := strings.Contains(body, `"success": true`) || strings.Contains(body, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FixerIO } trufflehog-3.60.0/pkg/detectors/fixerio/fixerio_test.go000066400000000000000000000054661451332102400232300ustar00rootroot00000000000000//go:build detectors // +build detectors package fixerio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFixerIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FIXERIO_TOKEN") inactiveSecret := testSecrets.MustGetField("FIXERIO_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 fixerio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FixerIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fixerio 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_FixerIO, 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("FixerIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FixerIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flatio/000077500000000000000000000000001451332102400177735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flatio/flatio.go000066400000000000000000000042201451332102400215760ustar00rootroot00000000000000package flatio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flat"}) + `\b([0-9a-z]{128})\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{"flat"} } // FromData will find and optionally verify FlatIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FlatIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.flat.io/v2/me", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FlatIO } trufflehog-3.60.0/pkg/detectors/flatio/flatio_test.go000066400000000000000000000054461451332102400226500ustar00rootroot00000000000000//go:build detectors // +build detectors package flatio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlatIO_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("FLATIO") inactiveSecret := testSecrets.MustGetField("FLATIO_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 flatio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FlatIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flatio 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_FlatIO, 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("FlatIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FlatIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fleetbase/000077500000000000000000000000001451332102400204475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fleetbase/fleetbase.go000066400000000000000000000041361451332102400227340ustar00rootroot00000000000000package fleetbase import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(flb_live_[0-9a-zA-Z]{20})\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{"fleetbase"} } // FromData will find and optionally verify Fleetbase 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fleetbase, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.fleetbase.io/v1/contacts/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fleetbase } trufflehog-3.60.0/pkg/detectors/fleetbase/fleetbase_test.go000066400000000000000000000055041451332102400237730ustar00rootroot00000000000000//go:build detectors // +build detectors package fleetbase import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFleetbase_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("FLEETBASE") inactiveSecret := testSecrets.MustGetField("FLEETBASE_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 fleetbase secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fleetbase, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fleetbase 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_Fleetbase, 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("Fleetbase.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fleetbase.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flickr/000077500000000000000000000000001451332102400177675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flickr/flickr.go000066400000000000000000000044731451332102400216000ustar00rootroot00000000000000package flickr import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flickr"}) + `\b([0-9a-z]{32})\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{"flickr"} } // FromData will find and optionally verify Flickr 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Flickr, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://www.flickr.com/services/rest/?method=flickr.tags.getHotList&api_key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") 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 (res.StatusCode >= 200 && res.StatusCode < 300) && strings.Contains(body, "owner=") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Flickr } trufflehog-3.60.0/pkg/detectors/flickr/flickr_test.go000066400000000000000000000054461451332102400226400ustar00rootroot00000000000000//go:build detectors // +build detectors package flickr import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlickr_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("FLICKR") inactiveSecret := testSecrets.MustGetField("FLICKR_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 flickr secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Flickr, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flickr 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_Flickr, 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("Flickr.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Flickr.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flightapi/000077500000000000000000000000001451332102400204645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flightapi/flightapi.go000066400000000000000000000041331451332102400227630ustar00rootroot00000000000000package flightapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flightapi"}) + `\b([a-z0-9]{24})\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{"flightapi"} } // FromData will find and optionally verify FlightApi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FlightApi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.flightapi.io/iata/%s/london/airport", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FlightApi } trufflehog-3.60.0/pkg/detectors/flightapi/flightapi_test.go000066400000000000000000000055041451332102400240250ustar00rootroot00000000000000//go:build detectors // +build detectors package flightapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlightApi_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("FLIGHTAPI") inactiveSecret := testSecrets.MustGetField("FLIGHTAPI_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 flightapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FlightApi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flightapi 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_FlightApi, 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("FlightApi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FlightApi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flightlabs/000077500000000000000000000000001451332102400206345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flightlabs/flightlabs.go000066400000000000000000000045121451332102400233040ustar00rootroot00000000000000package flightlabs import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flightlabs"}) + `\b(ey[a-zA-Z0-9]{34}.ey[a-zA-Z0-9._-]{300,350})\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{"flightlabs"} } // FromData will find and optionally verify FlightLabs 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FlightLabs, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://app.goflightlabs.com/airports?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"id"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FlightLabs } trufflehog-3.60.0/pkg/detectors/flightlabs/flightlabs_test.go000066400000000000000000000054441451332102400243500ustar00rootroot00000000000000package flightlabs import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlightLabs_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FLIGHTLABS") inactiveSecret := testSecrets.MustGetField("FLIGHTLABS_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 flightlabs secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FlightLabs, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flightlabs 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_FlightLabs, 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("FlightLabs.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FlightLabs.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flightstats/000077500000000000000000000000001451332102400210515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flightstats/flightstats.go000066400000000000000000000054051451332102400237400ustar00rootroot00000000000000package flightstats import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = 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{"flightstats"}) + `\b([0-9a-z]{8})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flightstats"}) + `\b([0-9a-z]{32})\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{"flightstats"} } // FromData will find and optionally verify Flightstats 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resId := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Flightstats, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", fmt.Sprintf("https://api.flightstats.com/flex/aircraft/rest/v1/json/availableFields?appId=%s&appKey=%s", resId, resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") 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) validResponse := (res.StatusCode >= 200 && res.StatusCode < 300 && strings.Contains(body, "id")) || (res.StatusCode == 403 && strings.Contains(body, "application is not active")) if validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Flightstats } trufflehog-3.60.0/pkg/detectors/flightstats/flightstats_test.go000066400000000000000000000056041451332102400250000ustar00rootroot00000000000000//go:build detectors // +build detectors package flightstats import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlightstats_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } id := testSecrets.MustGetField("FLIGHTSTATS_ID") secret := testSecrets.MustGetField("FLIGHTSTATS_KEY") inactiveSecret := testSecrets.MustGetField("FLIGHTSTATS_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 flightstats secret %s within flightstats id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Flightstats, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flightstats secret %s within flightstats id %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Flightstats, 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("Flightstats.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Flightstats.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/float/000077500000000000000000000000001451332102400176225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/float/float.go000066400000000000000000000042731451332102400212640ustar00rootroot00000000000000package float import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"float"}) + `\b([a-f0-9]{16}[A-Za-z0-9+/]{42,43}=)`) ) // 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{"float"} } // FromData will find and optionally verify Float 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Float, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.float.com/v3/people", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) req.Header.Add("User-Agent", "TruffleHog3 (example@example.com)") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Float } trufflehog-3.60.0/pkg/detectors/float/float_test.go000066400000000000000000000054421451332102400223220ustar00rootroot00000000000000//go:build detectors // +build detectors package float import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFloat_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FLOAT_TOKEN") inactiveSecret := testSecrets.MustGetField("FLOAT_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 float secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Float, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a float 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_Float, 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("Float.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Float.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flowdash/000077500000000000000000000000001451332102400203245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flowdash/flowdash.go000066400000000000000000000042701451332102400224650ustar00rootroot00000000000000package flowdash import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flowdash"}) + `\b([0-9a-zA-Z]{24})\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{"flowdash"} } // FromData will find and optionally verify Flowdash 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Flowdash, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.flowdash.com/api/v1/workspace", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Flowdash } trufflehog-3.60.0/pkg/detectors/flowdash/flowdash_test.go000066400000000000000000000054721451332102400235310ustar00rootroot00000000000000//go:build detectors // +build detectors package flowdash import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlowdash_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FLOWDASH") inactiveSecret := testSecrets.MustGetField("FLOWDASH_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 flowdash secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Flowdash, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flowdash 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_Flowdash, 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("Flowdash.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Flowdash.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flowdock/000077500000000000000000000000001451332102400203255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flowdock/flowdock.go000066400000000000000000000041241451332102400224650ustar00rootroot00000000000000package flowdock import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flowdock"}) + `\b([0-9a-f]{32})\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{"flowdock"} } // FromData will find and optionally verify Flowdock 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Flowdock, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.flowdock.com/flows?users=false", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Flowdock } trufflehog-3.60.0/pkg/detectors/flowdock/flowdock_test.go000066400000000000000000000054721451332102400235330ustar00rootroot00000000000000//go:build detectors // +build detectors package flowdock import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlowdock_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FLOWDOCK") inactiveSecret := testSecrets.MustGetField("FLOWDOCK_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 flowdock secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Flowdock, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flowdock 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_Flowdock, 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("Flowdock.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Flowdock.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flowflu/000077500000000000000000000000001451332102400201735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flowflu/flowflu.go000066400000000000000000000053511451332102400222040ustar00rootroot00000000000000package flowflu import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flowflu"}) + `\b([a-zA-Z0-9]{51})\b`) accountPat = regexp.MustCompile(detectors.PrefixRegex([]string{"flowflu", "account"}) + `\b([a-zA-Z0-9]{4,30})\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{"flowflu"} } // FromData will find and optionally verify FlowFlu 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) accountMatches := accountPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, accountMatch := range accountMatches { if len(accountMatch) != 2 { continue } resAccount := strings.TrimSpace(accountMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FlowFlu, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.flowlu.com/api/v1/module/crm/lead/list?api_key=%s", resAccount, resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `total_result`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FlowFlu } trufflehog-3.60.0/pkg/detectors/flowflu/flowflu_test.go000066400000000000000000000056461451332102400232520ustar00rootroot00000000000000//go:build detectors // +build detectors package flowflu import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlowFlu_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } account := testSecrets.MustGetField("FLOWFLU_ACCOUNT") secret := testSecrets.MustGetField("FLOWFLU_TOKEN") inactiveSecret := testSecrets.MustGetField("FLOWFLU_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 flowflu secret %s within flowflu account %s", secret, account)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FlowFlu, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flowflu secret %s within flowflu account %s but not valid", inactiveSecret, account)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FlowFlu, 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("FlowFlu.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FlowFlu.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) } } }) } } trufflehog-3.60.0/pkg/detectors/flutterwave/000077500000000000000000000000001451332102400210655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/flutterwave/flutterwave.go000066400000000000000000000041511451332102400237650ustar00rootroot00000000000000package flutterwave import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(FLWSECK-[0-9a-z]{32}-X)\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{"FLWSECK-"} } // FromData will find and optionally verify Flutterwave 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Flutterwave, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.flutterwave.com/v3/subaccounts", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Flutterwave } trufflehog-3.60.0/pkg/detectors/flutterwave/flutterwave_test.go000066400000000000000000000055361451332102400250340ustar00rootroot00000000000000//go:build detectors // +build detectors package flutterwave import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFlutterwave_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FLUTTERWAVE_TOKEN") inactiveSecret := testSecrets.MustGetField("FLUTTERWAVE_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 flutterwave secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Flutterwave, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a flutterwave 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_Flutterwave, 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("Flutterwave.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Flutterwave.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fmfw/000077500000000000000000000000001451332102400174545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fmfw/fmfw.go000066400000000000000000000047311451332102400207470ustar00rootroot00000000000000package fmfw import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fmfw"}) + `\b([a-zA-Z0-9_-]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fmfw"}) + `\b([a-zA-Z0-9-]{32})\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{"fmfw"} } // FromData will find and optionally verify Fmfw 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fmfw, Raw: []byte(tokenPatMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://api.fmfw.io/api/3/spot/balance", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fmfw } trufflehog-3.60.0/pkg/detectors/fmfw/fmfw_test.go000066400000000000000000000055351451332102400220110ustar00rootroot00000000000000//go:build detectors // +build detectors package fmfw import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFmfw_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FMFW") user := testSecrets.MustGetField("FMFW_USER") inactiveSecret := testSecrets.MustGetField("FMFW_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 fmfw secret %s within fmfw %s", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fmfw, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fmfw secret %s within fmfw %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fmfw, 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("Fmfw.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fmfw.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) } } }) } } trufflehog-3.60.0/pkg/detectors/formbucket/000077500000000000000000000000001451332102400206565ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/formbucket/formbucket.go000066400000000000000000000050471451332102400233540ustar00rootroot00000000000000package formbucket import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"formbucket"}) + `\b([0-9A-Za-z]{1,}.[0-9A-Za-z]{1,}\.[0-9A-Z-a-z\-_]{1,})`) ) // 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{"formbucket"} } // FromData will find and optionally verify FormBucket 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FormBucket, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.formbucket.com/v1/profile", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() body, errBody := io.ReadAll(res.Body) if errBody != nil { continue } bodyString := string(body) validResponse := strings.Contains(bodyString, `created_on`) defer res.Body.Close() if errBody == nil { if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } type Response struct { Anonymous bool `json:"anonymous"` } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FormBucket } trufflehog-3.60.0/pkg/detectors/formbucket/formbucket_test.go000066400000000000000000000054301451332102400244070ustar00rootroot00000000000000//go:build detectors // +build detectors package formbucket import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFormBucket_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FORMBUCKET") inactiveSecret := testSecrets.MustGetField("FORMBUCKET_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 formbucket secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FormBucket, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a formbucket 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_FormBucket, 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("FormBucket.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FormBucket.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/formcraft/000077500000000000000000000000001451332102400205005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/formcraft/formcraft.go000066400000000000000000000043541451332102400230200ustar00rootroot00000000000000package formcraft import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"formcraft"}) + `\b([0-9a-z]{16})\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{"formcraft"} } // FromData will find and optionally verify Formcraft 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Formcraft, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://formcrafts.com/api/v1/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Formcraft } trufflehog-3.60.0/pkg/detectors/formcraft/formcraft_test.go000066400000000000000000000055041451332102400240550ustar00rootroot00000000000000//go:build detectors // +build detectors package formcraft import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFormcraft_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FORMCRAFT") inactiveSecret := testSecrets.MustGetField("FORMCRAFT_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 formcraft secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Formcraft, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a formcraft 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_Formcraft, 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("Formcraft.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Formcraft.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) } } }) } } trufflehog-3.60.0/pkg/detectors/formio/000077500000000000000000000000001451332102400200105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/formio/formio.go000066400000000000000000000043021451332102400216310ustar00rootroot00000000000000package formio import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"formio"}) + `\b(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[0-9A-Za-z]{310}\.[0-9A-Z-a-z\-_]{43}[ \r\n]{1})`) ) // 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{"formio"} } // FromData will find and optionally verify FormIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FormIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://formio.form.io/current", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-jwt-token", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FormIO } trufflehog-3.60.0/pkg/detectors/formio/formio_test.go000066400000000000000000000053601451332102400226750ustar00rootroot00000000000000//go:build detectors // +build detectors package formio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFormIO_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FORMIO") inactiveSecret := testSecrets.MustGetField("FORMIO_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 formio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FormIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a formio 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_FormIO, 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("FormIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("FormIO.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/formsite/000077500000000000000000000000001451332102400203455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/formsite/formsite.go000066400000000000000000000055161451332102400225330ustar00rootroot00000000000000package formsite import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"formsite"}) + `\b([a-zA-Z0-9]{32})\b`) serverPat = regexp.MustCompile(detectors.PrefixRegex([]string{"formsite"}) + `\b(fs[0-9]{1,4})\b`) userPat = regexp.MustCompile(detectors.PrefixRegex([]string{"formsite"}) + `\b([a-zA-Z0-9]{6})\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{"formsite"} } // FromData will find and optionally verify Formsite 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) serverMatches := serverPat.FindAllStringSubmatch(dataStr, -1) userMatches := userPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, serverMatch := range serverMatches { if len(serverMatch) != 2 { continue } resServerMatch := strings.TrimSpace(serverMatch[1]) for _, userMatch := range userMatches { if len(userMatch) != 2 { continue } resUserMatch := strings.TrimSpace(userMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Formsite, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.formsite.com/api/v2/%s/forms", resServerMatch, resUserMatch), nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Formsite } trufflehog-3.60.0/pkg/detectors/formsite/formsite_test.go000066400000000000000000000057361451332102400235760ustar00rootroot00000000000000package formsite import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFormsite_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FORMSITE") inactiveSecret := testSecrets.MustGetField("FORMSITE_INACTIVE") server := testSecrets.MustGetField("FORMSITE_SERVER") user := testSecrets.MustGetField("FORMSITE_USER") 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 formsite secret %s within formsite server %s formsite user %s", secret, server, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Formsite, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a formsite secret %s within but not valid formsite server %s formsite user %s", inactiveSecret, server, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Formsite, 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("Formsite.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Formsite.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) } } }) } } trufflehog-3.60.0/pkg/detectors/foursquare/000077500000000000000000000000001451332102400207115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/foursquare/foursquare.go000066400000000000000000000051321451332102400234350ustar00rootroot00000000000000package foursquare import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"foursquare"}) + `\b([0-9A-Z]{48})\b`) secretMatch = regexp.MustCompile(detectors.PrefixRegex([]string{"foursquare"}) + `\b([0-9A-Z]{48})\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{"foursquare"} } // FromData will find and optionally verify Foursquare 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) secretMatches := secretMatch.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecret := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FourSquare, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.foursquare.com/v2/venues/trending?client_id=%s&client_secret=%s&v=20211019&near=LA", resMatch, resSecret), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FourSquare } trufflehog-3.60.0/pkg/detectors/foursquare/foursquare_test.go000066400000000000000000000056511451332102400245020ustar00rootroot00000000000000//go:build detectors // +build detectors package foursquare import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFoursquare_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) } id := testSecrets.MustGetField("FOURSQUARE") secret := testSecrets.MustGetField("FOURSQUARE_SECRET") inactiveId := testSecrets.MustGetField("FOURSQUARE_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 foursquare secret %s within foursquare id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FourSquare, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a foursquare secret %s within foursquare id %s but not valid", secret, inactiveId)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FourSquare, 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("Foursquare.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Foursquare.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) } } }) } } trufflehog-3.60.0/pkg/detectors/frameio/000077500000000000000000000000001451332102400201375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/frameio/frameio.go000066400000000000000000000041751451332102400221170ustar00rootroot00000000000000package frameio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(fio-u-[0-9a-zA-Z_-]{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{"fio-u-"} } // FromData will find and optionally verify Frameio 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FrameIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.frame.io/v2/me", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FrameIO } trufflehog-3.60.0/pkg/detectors/frameio/frameio_test.go000066400000000000000000000054661451332102400231620ustar00rootroot00000000000000//go:build detectors // +build detectors package frameio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFrameio_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FRAMEIO_TOKEN") inactiveSecret := testSecrets.MustGetField("FRAMEIO_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 frameio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FrameIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a frameio 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_FrameIO, 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("Frameio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Frameio.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) } } }) } } trufflehog-3.60.0/pkg/detectors/freshbooks/000077500000000000000000000000001451332102400206625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/freshbooks/freshbooks.go000066400000000000000000000052201451332102400233550ustar00rootroot00000000000000package freshbooks import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"freshbooks"}) + `\b([0-9a-z]{64})\b`) uriPat = regexp.MustCompile(detectors.PrefixRegex([]string{"freshbooks"}) + `\b(https://www.[0-9A-Za-z_-]{1,}.com)\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{"freshbooks"} } // FromData will find and optionally verify Freshbooks 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) uriMatches := uriPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, uriMatch := range uriMatches { if len(uriMatch) != 2 { continue } resURI := strings.TrimSpace(uriMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Freshbooks, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf(`https://auth.freshbooks.com/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code`, resMatch, resURI), nil) if err != nil { continue } 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 res.StatusCode >= 200 && res.StatusCode < 300 && strings.Contains(body, "Log In to FreshBooks") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Freshbooks } trufflehog-3.60.0/pkg/detectors/freshbooks/freshbooks_test.go000066400000000000000000000056451451332102400244270ustar00rootroot00000000000000//go:build detectors // +build detectors package freshbooks import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFreshbooks_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FRESHBOOKS") uri := testSecrets.MustGetField("REDIRECT_URI") inactiveSecret := testSecrets.MustGetField("FRESHBOOKS_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 freshbooks secret %s within freshbooks %s", secret, uri)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Freshbooks, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a freshbooks secret %s within freshbooks %s but not valid", inactiveSecret, uri)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Freshbooks, 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("Freshbooks.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Freshbooks.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) } } }) } } trufflehog-3.60.0/pkg/detectors/freshdesk/000077500000000000000000000000001451332102400204735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/freshdesk/freshdesk.go000066400000000000000000000051231451332102400230010ustar00rootroot00000000000000package freshdesk import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. urlPat = regexp.MustCompile(`\b([0-9a-z-]{1,}\.freshdesk\.com)\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"freshdesk"}) + `\b([0-9A-Za-z]{20})\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{"freshdesk"} } // FromData will find and optionally verify Freshdesk 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, urlMatch := range urlMatches { if len(urlMatch) != 2 { continue } resURL := strings.TrimSpace(urlMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Freshdesk, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:X", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/api/v2/tickets", resURL), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Freshdesk } trufflehog-3.60.0/pkg/detectors/freshdesk/freshdesk_test.go000066400000000000000000000056061451332102400240460ustar00rootroot00000000000000//go:build detectors // +build detectors package freshdesk import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFreshdesk_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FRESHDESK") url := testSecrets.MustGetField("FRESHDESK_URL") inactiveSecret := testSecrets.MustGetField("FRESHDESK_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 freshdesk secret %s within %s", secret, url)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Freshdesk, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a freshdesk secret %s within %s but not valid", inactiveSecret, url)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Freshdesk, 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("Freshdesk.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Freshdesk.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) } } }) } } trufflehog-3.60.0/pkg/detectors/front/000077500000000000000000000000001451332102400176455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/front/front.go000066400000000000000000000042641451332102400213320ustar00rootroot00000000000000package front import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"front"}) + `\b([0-9a-zA-Z]{36}.[0-9a-zA-Z\.\-\_]{188,244})\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{"front"} } // FromData will find and optionally verify Front 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Front, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api2.frontapp.com/accounts", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Front } trufflehog-3.60.0/pkg/detectors/front/front_test.go000066400000000000000000000054341451332102400223710ustar00rootroot00000000000000//go:build detectors // +build detectors package front import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFront_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("FRONT") inactiveSecret := testSecrets.MustGetField("FRONT_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 front secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Front, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a front 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_Front, 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("Front.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Front.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ftp/000077500000000000000000000000001451332102400173065ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ftp/ftp.go000066400000000000000000000060221451332102400204260ustar00rootroot00000000000000package ftp import ( "context" "errors" "net/textproto" "net/url" "regexp" "strings" "time" "github.com/jlaffaye/ftp" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) const ( // https://datatracker.ietf.org/doc/html/rfc959 ftpNotLoggedIn = 530 defaultVerificationTimeout = 5 * time.Second ) type Scanner struct { // Verification timeout. Defaults to 5 seconds if unset. verificationTimeout time.Duration } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( keyPat = regexp.MustCompile(`\bftp://[\S]{3,50}:([\S]{3,50})@[-.%\w\/:]+\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{"ftp://"} } // FromData will find and optionally verify URI 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) for _, match := range matches { urlMatch := match[0] password := match[1] // Skip findings where the password only has "*" characters, this is a redacted password if strings.Trim(password, "*") == "" { continue } parsedURL, err := url.Parse(urlMatch) if err != nil { continue } if _, ok := parsedURL.User.Password(); !ok { continue } if parsedURL.User.Username() == "anonymous" { continue } rawURL, _ := url.Parse(urlMatch) rawURL.Path = "" redact := strings.TrimSpace(strings.Replace(rawURL.String(), password, "********", -1)) r := detectors.Result{ DetectorType: detectorspb.DetectorType_FTP, Raw: []byte(rawURL.String()), Redacted: redact, } if verify { timeout := s.verificationTimeout if timeout == 0 { timeout = defaultVerificationTimeout } verificationErr := verifyFTP(timeout, parsedURL) r.Verified = verificationErr == nil if !isErrDeterminate(verificationErr) { r.VerificationError = verificationErr } } if !r.Verified { // Skip unverified findings where the password starts with a `$` - it's almost certainly a variable. if strings.HasPrefix(password, "$") { continue } } if detectors.IsKnownFalsePositive(string(r.Raw), []detectors.FalsePositive{"@ftp.freebsd.org"}, false) { continue } results = append(results, r) } return results, nil } func isErrDeterminate(e error) bool { ftpErr := &textproto.Error{} return errors.As(e, &ftpErr) && ftpErr.Code == ftpNotLoggedIn } func verifyFTP(timeout time.Duration, u *url.URL) error { host := u.Host if !strings.Contains(host, ":") { host = host + ":21" } c, err := ftp.Dial(host, ftp.DialWithTimeout(timeout)) if err != nil { return err } password, _ := u.User.Password() return c.Login(u.User.Username(), password) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FTP } trufflehog-3.60.0/pkg/detectors/ftp/ftp_test.go000066400000000000000000000100211451332102400214570ustar00rootroot00000000000000//go:build detectors // +build detectors package ftp import ( "context" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFTP_FromChunk(t *testing.T) { type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "bad scheme", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte("file://user:pass@foo.com:123/wh/at/ever"), verify: true, }, wantErr: false, }, { name: "verified FTP", s: Scanner{}, args: args{ ctx: context.Background(), // https://dlptest.com/ftp-test/ data: []byte("ftp://dlpuser:rNrKYTX9g7z3RgJRmxWuGHbeu@ftp.dlptest.com"), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FTP, Verified: true, Redacted: "ftp://dlpuser:********@ftp.dlptest.com", }, }, wantErr: false, }, { name: "unverified FTP", s: Scanner{}, args: args{ ctx: context.Background(), // https://dlptest.com/ftp-test/ data: []byte("ftp://dlpuser:invalid@ftp.dlptest.com"), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FTP, Verified: false, Redacted: "ftp://dlpuser:********@ftp.dlptest.com", }, }, wantErr: false, }, { name: "bad host", s: Scanner{}, args: args{ ctx: context.Background(), // https://dlptest.com/ftp-test/ data: []byte("ftp://dlpuser:rNrKYTX9g7z3RgJRmxWuGHbeu@ftp.dlptest.com.badhost"), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FTP, Verified: false, Redacted: "ftp://dlpuser:********@ftp.dlptest.com.badhost", }, }, wantErr: false, wantVerificationErr: true, }, { name: "timeout", s: Scanner{verificationTimeout: 1 * time.Microsecond}, args: args{ ctx: context.Background(), // https://dlptest.com/ftp-test/ data: []byte("ftp://dlpuser:rNrKYTX9g7z3RgJRmxWuGHbeu@ftp.dlptest.com"), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FTP, Verified: false, Redacted: "ftp://dlpuser:********@ftp.dlptest.com", }, }, wantErr: false, wantVerificationErr: true, }, { name: "blocked FP", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte("ftp://abc:123@ftp.freebsd.org/pub/FreeBSD/doc/tr/articles/explaining-bsd/explaining-bsd_tr.pdf"), verify: true, }, 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("URI.FromData() error = %v, wantErr %v", err, tt.wantErr) return } // if os.Getenv("FORCE_PASS_DIFF") == "true" { // return // } for i := range got { got[i].Raw = nil if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } opts := cmpopts.IgnoreFields(detectors.Result{}, "VerificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("FTP.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fulcrum/000077500000000000000000000000001451332102400201725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fulcrum/fulcrum.go000066400000000000000000000042111451332102400221740ustar00rootroot00000000000000package fulcrum import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fulcrum"}) + `\b([a-z0-9]{80})\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{"fulcrum"} } // FromData will find and optionally verify fullcrum 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fulcrum, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.fulcrumapp.com/api/v2/forms.json", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("X-ApiToken", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fulcrum } trufflehog-3.60.0/pkg/detectors/fulcrum/fulcrum_test.go000066400000000000000000000054621451332102400232440ustar00rootroot00000000000000//go:build detectors // +build detectors package fulcrum import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFulcrum_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("FULLCRUM") inactiveSecret := testSecrets.MustGetField("FULLCRUM_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 fulcrum secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fulcrum, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fulcrum 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_Fulcrum, 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("fulcrum.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("fulcrum.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fullstory/000077500000000000000000000000001451332102400205605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fullstory/fullstory.go000066400000000000000000000042101451332102400231470ustar00rootroot00000000000000package fullstory import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fullstory"}) + `\b([a-zA-Z-0-9/+]{88})\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{"fullstory"} } // FromData will find and optionally verify Fullstory 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fullstory, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.fullstory.com/operations/v1", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fullstory } trufflehog-3.60.0/pkg/detectors/fullstory/fullstory_test.go000066400000000000000000000055041451332102400242150ustar00rootroot00000000000000//go:build detectors // +build detectors package fullstory import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFullstory_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("FULLSTORY") inactiveSecret := testSecrets.MustGetField("FULLSTORY_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 fullstory secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fullstory, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fullstory 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_Fullstory, 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("Fullstory.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fullstory.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fusebill/000077500000000000000000000000001451332102400203225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fusebill/fusebill.go000066400000000000000000000042331451332102400224600ustar00rootroot00000000000000package fusebill import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fusebill"}) + `\b([a-zA-Z0-9]{88})\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{"fusebill"} } // FromData will find and optionally verify Fusebill 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Fusebill, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://secure.fusebill.com/v1/customers?pageSize=100&pageNumber=0", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Fusebill } trufflehog-3.60.0/pkg/detectors/fusebill/fusebill_test.go000066400000000000000000000054721451332102400235250ustar00rootroot00000000000000//go:build detectors // +build detectors package fusebill import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFusebill_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("FUSEBILL") inactiveSecret := testSecrets.MustGetField("FUSEBILL_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 fusebill secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Fusebill, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fusebill 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_Fusebill, 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("Fusebill.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fusebill.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) } } }) } } trufflehog-3.60.0/pkg/detectors/fxmarket/000077500000000000000000000000001451332102400203365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/fxmarket/fxmarket.go000066400000000000000000000042151451332102400225100ustar00rootroot00000000000000package fxmarket import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"fxmarket"}) + `\b([0-9Aa-zA-Z-_=]{20})\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{"fxmarket"} } // FromData will find and optionally verify Fxmarket 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_FXMarket, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://fxmarketapi.com/apilive?api_key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_FXMarket } trufflehog-3.60.0/pkg/detectors/fxmarket/fxmarket_test.go000066400000000000000000000054721451332102400235550ustar00rootroot00000000000000//go:build detectors // +build detectors package fxmarket import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestFxmarket_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("FXMARKET") inactiveSecret := testSecrets.MustGetField("FXMARKET_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 fxmarket secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_FXMarket, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a fxmarket 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_FXMarket, 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("Fxmarket.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Fxmarket.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gcp/000077500000000000000000000000001451332102400172665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gcp/gcp.go000066400000000000000000000063611451332102400203740ustar00rootroot00000000000000package gcp import ( "context" "encoding/json" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "golang.org/x/oauth2/google" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( keyPat = regexp.MustCompile(`\{[^{]+auth_provider_x509_cert_url[^}]+\}`) ) type gcpKey struct { Type string `json:"type"` ProjectID string `json:"project_id"` PrivateKeyID string `json:"private_key_id"` PrivateKey string `json:"private_key"` ClientEmail string `json:"client_email"` ClientID string `json:"client_id"` AuthURI string `json:"auth_uri"` TokenURI string `json:"token_uri"` AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"` ClientX509CertURL string `json:"client_x509_cert_url"` } func trimCarrots(s string) string { s = strings.TrimPrefix(s, "<") s = strings.TrimSuffix(s, ">") return s } // 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{"provider_x509"} } // FromData will find and optionally verify GCP 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.FindAllString(dataStr, -1) for _, match := range matches { key := match key = strings.ReplaceAll(key, `,\\n`, `\n`) key = strings.ReplaceAll(key, `\"\\n`, `\n`) key = strings.ReplaceAll(key, `\\"`, `"`) creds := gcpKey{} err := json.Unmarshal([]byte(key), &creds) if err != nil { continue } // for Slack mangling (mailto scheme and hyperlinks) if strings.Contains(creds.ClientEmail, `= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Geckoboard } trufflehog-3.60.0/pkg/detectors/geckoboard/geckoboard_test.go000066400000000000000000000055161451332102400243120ustar00rootroot00000000000000//go:build detectors // +build detectors package geckoboard import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGeckoboard_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GECKOBOARD") inactiveSecret := testSecrets.MustGetField("GECKOBOARD_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 geckoboard secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Geckoboard, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a geckoboard 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_Geckoboard, 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("Geckoboard.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Geckoboard.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gemini/000077500000000000000000000000001451332102400177655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gemini/gemini.go000066400000000000000000000067511451332102400215750ustar00rootroot00000000000000package gemini import ( "bytes" "context" "crypto/hmac" "crypto/sha512" "encoding/base64" "encoding/hex" "encoding/json" "net/http" "regexp" "strings" "time" "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{} const ( baseURL = "https://api.gemini.com" accountDetail = "/v1/account" account = "primary" ) // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b((?:master-|account-)[0-9A-Za-z]{20})\b`) secretPat = regexp.MustCompile(`[A-Za-z0-9]{27,28}`) ) // 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{"master-", "account-"} } // FromData will find and optionally verify Gemini secrets in a given set of bytes. func (s Scanner) FromData(_ context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) idMatches := keyPat.FindAllStringSubmatch(dataStr, -1) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range idMatches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { resSecretMatch := strings.TrimSpace(secretMatch[0]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gemini, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecretMatch), } if verify { req, err := constructRequest(resSecretMatch, resMatch) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func constructRequest(secret, keyID string) (*http.Request, error) { req, err := http.NewRequest("POST", baseURL+accountDetail, &bytes.Buffer{}) if err != nil { return nil, err } params := map[string]any{ "request": accountDetail, "nonce": time.Now().UnixNano(), } acct := strings.Split(keyID, "-") // Not entirely sure how to handle master account keys where one of the accounts is named "primary". if len(acct) > 1 && acct[0] == "master" { params["account"] = account } reqStr, err := json.Marshal(¶ms) if err != nil { return nil, err } payload := base64.StdEncoding.EncodeToString(reqStr) signature := constructSignature(payload, secret) req.Header = http.Header{ "Content-Type": {"text/plain"}, "Content-Length": {"0"}, "X-GEMINI-APIKEY": {keyID}, "X-GEMINI-PAYLOAD": {payload}, "X-GEMINI-SIGNATURE": {signature}, "Cache-Control": {"no-cache"}, } return req, err } func constructSignature(payload string, resSecretMatch string) string { h := hmac.New(sha512.New384, []byte(resSecretMatch)) h.Write([]byte(payload)) signature := hex.EncodeToString(h.Sum(nil)) return signature } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gemini } trufflehog-3.60.0/pkg/detectors/gemini/gemini_test.go000066400000000000000000000070611451332102400226270ustar00rootroot00000000000000//go:build detectors // +build detectors package gemini 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 TestGemini_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secretMaster := testSecrets.MustGetField("GEMINI") keyMaster := testSecrets.MustGetField("GEMINI_KEY") secretAccount := testSecrets.MustGetField("GEMINI_ACCOUNT") keyAccount := testSecrets.MustGetField("GEMINI_KEY_ACCOUNT") inactiveSecret := testSecrets.MustGetField("GEMINI_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; master", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gemini %s and secretMaster %s within", keyMaster, secretMaster)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gemini, Verified: true, }, }, wantErr: false, }, { name: "found, verified; account", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gemini %s and secretAccount %s within", keyAccount, secretAccount)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gemini, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gemini secretMaster %s and secretMaster %s within but not valid", keyMaster, inactiveSecret)), // the secretMaster would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gemini, Verified: false, }, }, wantErr: false, }, { name: "not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte("You cannot find the secretMaster 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("Gemini.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatalf("no raw secretMaster present: \n %+v", got[i]) } got[i].Raw = nil } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2") if diff := cmp.Diff(tt.want, got, ignoreOpts); diff != "" { t.Errorf("Gemini.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) } } }) } } trufflehog-3.60.0/pkg/detectors/generic/000077500000000000000000000000001451332102400201315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/generic/generic.go000066400000000000000000000074051451332102400221020ustar00rootroot00000000000000package generic // cat scanner/pkg/secrets/generic/top-1000.txt | awk 'length($0)>5' > scanner/pkg/secrets/generic/words.txt import ( "context" "encoding/base64" "fmt" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func New() Scanner { excludePatterns := []string{ `[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}`, // UUID `[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}`, // UUIDv4 `[A-Z]{2,6}\-[0-9]{2,6}`, // issue tracker `#[a-fA-F0-9]{6}\b`, // hex color code `\b[A-Fa-f0-9]{64}\b`, // hex encoded hash `https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)`, // http `\b([/]{0,1}([\w]+[/])+[\w\.]*)\b`, // filepath `([0-9A-F]{2}[:-]){5}([0-9A-F]{2})`, // MAC addr `\d{4}[-/]{1}([0]\d|1[0-2])[-/]{1}([0-2]\d|3[01])`, // date `[v|\-]\d\.\d`, //version `\d\.\d\.\d-`, //version `[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}`, // IPs and OIDs `[A-Fa-f0-9x]{2}:[A-Fa-f0-9x]{2}:[A-Fa-f0-9x]{2}`, // hex encoding `[\w]+\([\w, ]+\)`, // function } excludeMatchers := []*regexp.Regexp{} for _, pat := range excludePatterns { excludeMatchers = append(excludeMatchers, regexp.MustCompile(pat)) } return Scanner{ excludeMatchers: excludeMatchers, } } type Scanner struct { excludeMatchers []*regexp.Regexp } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var keywords = []string{"pass", "token", "cred", "secret", "key"} var ( // \x21-\x7e == ASCII 33 (0x21) and 126 (0x7e) keyPat = regexp.MustCompile(detectors.PrefixRegex(keywords) + `(\b[\x21-\x7e]{16,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 keywords } // FromData will find and optionally verify Generic 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) for _, match := range matches { token := match[1] // Least expensive-> most expensive filters. // Substrings, then patterns. if detectors.IsKnownFalsePositive(token, detectors.DefaultFalsePositives, true) { continue } // toss any that match regexes if hasReMatch(s.excludeMatchers, token) { continue } // clean up containment chars token = strings.Trim(token, fmt.Sprintf(`%s" '.,)(][}{`, "`")) // toss any that b64 decode // TODO: run them through again? _, err := base64.StdEncoding.DecodeString(token) if err == nil { continue } s := detectors.Result{ DetectorType: detectorspb.DetectorType_Generic, Raw: []byte(token), } results = append(results, s) } return } func hasReMatch(matchers []*regexp.Regexp, token string) bool { for _, m := range matchers { if m.MatchString(token) { return true } } return false } // func hasDictWord(wordList []string, token string) bool { // lower := strings.ToLower(token) // for _, word := range wordList { // if strings.Contains(lower, word) { // return true // } // } // return false // } // func bytesToCleanWordList(data []byte) []string { // words := []string{} // for _, word := range strings.Split(string(data), "\n") { // if strings.TrimSpace(word) != "" { // words = append(words, strings.TrimSpace(strings.ToLower(word))) // } // } // return words // } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Generic } trufflehog-3.60.0/pkg/detectors/generic/generic_test.go000066400000000000000000000033731451332102400231410ustar00rootroot00000000000000//go:build detectors // +build detectors package generic import ( "context" "testing" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" ) func TestGeneric_FromChunk(t *testing.T) { ctx := context.Background() s := New() found := []string{ "export CONFIG_SERVICE_PASSWORD=34t98hofi2309pr230", "CONFIG_SERVICE_PASSWORD: 34t98hofi2309pr230", "the secret is 34t98hofi2309pr230", // keyword within distance of cred-looking token } notFound := []string{ "export MAGIC_VAR=piggymetrics201925", // key does not have keyword "export CONFIG_SERVICE_PASSWORD=testcredentials", // has test, cred "export CONFIG_SERVICE_PASSWORD=password123", // has pass "export CONFIG_SERVICE_PASSWORD=too-plain", // no digit "export CONFIG_SERVICE_PASSWORD=abcdefg123", // known FP "SECRET: mountain422", // excluded by word list "secret_guid=3fc0b7f7-da09-4ae7-a9c8-d69824b1819b", // excluded by matcher "secret_issue_key: BLAH-23490", // excluded by matcher } for _, data := range found { got, _ := s.FromData(ctx, false, []byte(data)) if len(got) == 0 { t.Errorf("Generic.FromData() expected secret for data: %s", data) return } } for _, data := range notFound { got, _ := s.FromData(ctx, false, []byte(data)) if len(got) != 0 { t.Errorf("Generic.FromData() expected no secret for data: %s", data) return } } } func BenchmarkFromData(benchmark *testing.B) { ctx := context.Background() s := New() 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) } } }) } } trufflehog-3.60.0/pkg/detectors/gengo/000077500000000000000000000000001451332102400176145ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gengo/gengo.go000066400000000000000000000070221451332102400212430ustar00rootroot00000000000000package gengo import ( "context" "crypto/hmac" "crypto/sha1" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // Removed bounds since there are some cases where the start and end of the token is a special character keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gengo"}) + `([ ]{0,1}[0-9a-zA-Z\[\]\-\(\)\{\}|_^@$=~]{64}[ \r\n]{1})`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gengo"}) + `([ ]{0,1}[0-9a-zA-Z\[\]\-\(\)\{\}|_^@$=~]{64}[ \r\n]{1})`) ) // 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{"gengo"} } // FromData will find and optionally verify Gengo 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gengo, Raw: []byte(resSecretMatch), RawV2: []byte(resMatch + resSecretMatch), } if verify { timestamp := strconv.FormatInt(time.Now().Unix(), 10) signature := getGengoSignature(timestamp, resSecretMatch) req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.gengo.com/v2/account/me?ts=%s&api_key=%s&api_sig=%s", timestamp, resMatch, signature), nil) if err != nil { continue } req.Header.Add("Accept", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() body, errBody := io.ReadAll(res.Body) if errBody == nil { var response Response if err := json.Unmarshal(body, &response); err != nil { continue } if res.StatusCode >= 200 && res.StatusCode < 300 && response.OpStat == "ok" { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } } return results, nil } type Response struct { OpStat string `json:"opstat"` } func getGengoSignature(timeStamp string, secret string) string { mac := hmac.New(sha1.New, []byte(secret)) mac.Write([]byte(timeStamp)) macsum := mac.Sum(nil) return hex.EncodeToString(macsum) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gengo } trufflehog-3.60.0/pkg/detectors/gengo/gengo_test.go000066400000000000000000000056771451332102400223200ustar00rootroot00000000000000//go:build detectors // +build detectors package gengo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGengo_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) } key := testSecrets.MustGetField("GENGO_KEY") inactiveKey := testSecrets.MustGetField("GENGO_KEY_INACTIVE") secret := testSecrets.MustGetField("GENGO") inactiveSecret := testSecrets.MustGetField("GENGO_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 gengo key %s with gengo secret %s within", key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gengo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gengo key %s with gengo secret %s within but not valid", inactiveKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gengo, 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("Gengo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Gengo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/geoapify/000077500000000000000000000000001451332102400203205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/geoapify/geoapify.go000066400000000000000000000041521451332102400224540ustar00rootroot00000000000000package geoapify import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"geoapify"}) + `\b([a-z0-9]{32})\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{"geoapify"} } // FromData will find and optionally verify Geoapify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Geoapify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.geoapify.com/v1/geocode/search?text=California&apiKey=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Geoapify } trufflehog-3.60.0/pkg/detectors/geoapify/geoapify_test.go000066400000000000000000000054721451332102400235210ustar00rootroot00000000000000//go:build detectors // +build detectors package geoapify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGeoapify_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("GEOAPIFY") inactiveSecret := testSecrets.MustGetField("GEOAPIFY_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 geoapify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Geoapify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a geoapify 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_Geoapify, 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("Geoapify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Geoapify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/geocode/000077500000000000000000000000001451332102400201225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/geocode/geocode.go000066400000000000000000000045411451332102400220620ustar00rootroot00000000000000package geocode import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"geocode"}) + `\b([a-z0-9]{28})\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{"geocode"} } // FromData will find and optionally verify Geocode 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Geocode, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://geocode.xyz/51.4647,0.0079?json=1&auth=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"remaining_credits" :`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Geocode } trufflehog-3.60.0/pkg/detectors/geocode/geocode_test.go000066400000000000000000000054601451332102400231220ustar00rootroot00000000000000//go:build detectors // +build detectors package geocode import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGeocode_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("GEOCODE") inactiveSecret := testSecrets.MustGetField("GEOCODE_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 geocode secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Geocode, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a geocode 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_Geocode, 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("Geocode.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Geocode.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) } } }) } } trufflehog-3.60.0/pkg/detectors/geocodify/000077500000000000000000000000001451332102400204655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/geocodify/geocodify.go000066400000000000000000000041041451332102400227630ustar00rootroot00000000000000package geocodify import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"geocodify"}) + `\b([0-9a-z]{40})\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{"geocodify"} } // FromData will find and optionally verify Geocodify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Geocodify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.geocodify.com/v2/geocode?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Geocodify } trufflehog-3.60.0/pkg/detectors/geocodify/geocodify_test.go000066400000000000000000000055041451332102400240270ustar00rootroot00000000000000//go:build detectors // +build detectors package geocodify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGeocodify_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("GEOCODIFY") inactiveSecret := testSecrets.MustGetField("GEOCODIFY_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 geocodify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Geocodify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a geocodify 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_Geocodify, 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("Geocodify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Geocodify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/geocodio/000077500000000000000000000000001451332102400203055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/geocodio/geocodio.go000066400000000000000000000047631451332102400224360ustar00rootroot00000000000000package geocodio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"geocod"}) + `\b([a-z0-9]{39})\b`) searchPat = regexp.MustCompile(detectors.PrefixRegex([]string{"geocod"}) + `\b([a-zA-Z0-9\S]{7,30})\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{"geocod"} } // FromData will find and optionally verify Geocodio 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) searchMatches := searchPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, searchMatch := range searchMatches { if len(searchMatch) != 2 { continue } resSearchMatch := strings.TrimSpace(searchMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Geocodio, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSearchMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.geocod.io/v1.6/geocode?q=%s&api_key=%s", resSearchMatch, resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Geocodio } trufflehog-3.60.0/pkg/detectors/geocodio/geocodio_test.go000066400000000000000000000056311451332102400234700ustar00rootroot00000000000000//go:build detectors // +build detectors package geocodio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGeocodio_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("GEOCODIO") search := testSecrets.MustGetField("GEOCODIO_SEARCH") inactiveSecret := testSecrets.MustGetField("GEOCODIO_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 geocodio secret %s within geocodio %s", secret, search)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Geocodio, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a geocodio secret %s within geocodio %s but not valid", inactiveSecret, search)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Geocodio, 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("Geocodio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Geocodio.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) } } }) } } trufflehog-3.60.0/pkg/detectors/geoipifi/000077500000000000000000000000001451332102400203105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/geoipifi/geoipifi.go000066400000000000000000000041211451332102400224300ustar00rootroot00000000000000package geoipifi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipifi"}) + `\b([a-z0-9A-Z_]{32})\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{"ipifi"} } // FromData will find and optionally verify GeoIpifi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GeoIpifi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://geo.ipify.org/api/v2/country?apiKey="+resMatch+"&ipAddress=8.8.8.8", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GeoIpifi } trufflehog-3.60.0/pkg/detectors/geoipifi/geoipifi_test.go000066400000000000000000000054721451332102400235010ustar00rootroot00000000000000//go:build detectors // +build detectors package geoipifi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGeoIpifi_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("GEOIPIFI") inactiveSecret := testSecrets.MustGetField("GEOIPIFI_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 geoipifi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GeoIpifi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a geoipifi 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_GeoIpifi, 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("GeoIpifi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GeoIpifi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/getemail/000077500000000000000000000000001451332102400203045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/getemail/getemail.go000066400000000000000000000046551451332102400224340ustar00rootroot00000000000000package getemail import ( "context" "fmt" "io" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClientTimeOut(5 * time.Second) // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getemail"}) + `\b([a-zA-Z0-9-]{20})\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{"getemail"} } // FromData will find and optionally verify GetEmail 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GetEmail, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.getemail.io/v1/find-mail?firstname=Larry&lastname=Page&domain=google.com&api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) errCode := strings.Contains(bodyString, `"code":"USER_NOT_EXIST"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if errCode { s1.Verified = false } else { s1.Verified = true } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GetEmail } trufflehog-3.60.0/pkg/detectors/getemail/getemail_test.go000066400000000000000000000054511451332102400234660ustar00rootroot00000000000000//go:build detectors // +build detectors package getemail import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGetEmail_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("GETEMAIL") inactiveSecret := testSecrets.MustGetField("GETEMAIL_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 getemail secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GetEmail, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a getemail 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_GetEmail, 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("GetEmail.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GetEmail.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) } } }) } } trufflehog-3.60.0/pkg/detectors/getemails/000077500000000000000000000000001451332102400204675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/getemails/getemails.go000066400000000000000000000047021451332102400227730ustar00rootroot00000000000000package getemails import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getemails"}) + `\b([a-z0-9-]{26})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getemails"}) + `\b([a-z0-9-]{18})\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{"getemails"} } // FromData will find and optionally verify GetEmails 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GetEmails, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getemails.com/api/v1/contacts", nil) if err != nil { continue } req.Header.Add("api-key", resMatch) req.Header.Add("api-id", resIdMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GetEmails } trufflehog-3.60.0/pkg/detectors/getemails/getemails_test.go000066400000000000000000000056051451332102400240350ustar00rootroot00000000000000//go:build detectors // +build detectors package getemails import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGetEmails_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("GETEMAILS") id := testSecrets.MustGetField("GETEMAILS_ID") inactiveSecret := testSecrets.MustGetField("GETEMAILS_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 getemails secret %s within getemails %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GetEmails, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a getemails secret %s within getemails %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GetEmails, 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("GetEmails.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GetEmails.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) } } }) } } trufflehog-3.60.0/pkg/detectors/getgeoapi/000077500000000000000000000000001451332102400204615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/getgeoapi/getgeoapi.go000066400000000000000000000042271451332102400227610ustar00rootroot00000000000000package getgeoapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getgeoapi"}) + `\b([0-9a-z]{40})\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{"getgeoapi"} } // FromData will find and optionally verify Getgeoapi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GetGeoAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.getgeoapi.com/v2/currency/list?api_key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GetGeoAPI } trufflehog-3.60.0/pkg/detectors/getgeoapi/getgeoapi_test.go000066400000000000000000000055041451332102400240170ustar00rootroot00000000000000//go:build detectors // +build detectors package getgeoapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGetgeoapi_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("GETGEOAPI") inactiveSecret := testSecrets.MustGetField("GETGEOAPI_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 getgeoapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GetGeoAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a getgeoapi 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_GetGeoAPI, 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("Getgeoapi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Getgeoapi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/getgist/000077500000000000000000000000001451332102400201635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/getgist/getgist.go000066400000000000000000000041641451332102400221650ustar00rootroot00000000000000package getgist import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getgist"}) + `\b([a-z0-9A-Z+=]{68})`) ) // 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{"getgist"} } // FromData will find and optionally verify Getgist 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Getgist, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getgist.com/contacts/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Getgist } trufflehog-3.60.0/pkg/detectors/getgist/getgist_test.go000066400000000000000000000054661451332102400232320ustar00rootroot00000000000000//go:build detectors // +build detectors package getgist import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGetgist_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GETGIST_TOKEN") inactiveSecret := testSecrets.MustGetField("GETGIST_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 getgist secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Getgist, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a getgist 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_Getgist, 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("Getgist.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Getgist.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) } } }) } } trufflehog-3.60.0/pkg/detectors/getresponse/000077500000000000000000000000001451332102400210535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/getresponse/getresponse.go000066400000000000000000000042141451332102400237410ustar00rootroot00000000000000package getresponse import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getresponse"}) + `\b([a-z0-9]{32})\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{"getresponse"} } // FromData will find and optionally verify Getresponse 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Getresponse, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getresponse.com/v3/accounts", nil) if err != nil { continue } req.Header.Add("X-Auth-Token", fmt.Sprintf("api-key %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Getresponse } trufflehog-3.60.0/pkg/detectors/getresponse/getresponse_test.go000066400000000000000000000054561451332102400250110ustar00rootroot00000000000000package getresponse import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGetresponse_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GETRESPONSE") inactiveSecret := testSecrets.MustGetField("GETRESPONSE_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 getresponse secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Getresponse, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a getresponse 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_Getresponse, 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("Getresponse.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Getresponse.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) } } }) } } trufflehog-3.60.0/pkg/detectors/getsandbox/000077500000000000000000000000001451332102400206535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/getsandbox/getsandbox.go000066400000000000000000000047011451332102400233420ustar00rootroot00000000000000package getsandbox import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getsandbox"}) + `\b([a-z0-9-]{40})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"getsandbox"}) + `\b([a-z0-9-]{15,30})\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{"getsandbox"} } // FromData will find and optionally verify GetSandbox 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GetSandbox, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://getsandbox.com/api/1/sandboxes/%s", resIdMatch), nil) if err != nil { continue } req.Header.Add("API-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GetSandbox } trufflehog-3.60.0/pkg/detectors/getsandbox/getsandbox_test.go000066400000000000000000000056431451332102400244070ustar00rootroot00000000000000//go:build detectors // +build detectors package getsandbox import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGetSandbox_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GETSANDBOX") id := testSecrets.MustGetField("GETSANDBOX_ID") inactiveSecret := testSecrets.MustGetField("GETSANDBOX_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 getsandbox secret %s within getsandbox %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GetSandbox, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a getsandbox secret %s within getsandbox %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GetSandbox, 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("GetSandbox.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GetSandbox.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) } } }) } } trufflehog-3.60.0/pkg/detectors/github/000077500000000000000000000000001451332102400177775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/github/github.go000066400000000000000000000071451451332102400216170ustar00rootroot00000000000000package github import ( "context" "encoding/json" "fmt" "net/http" "regexp" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/common" ) type Scanner struct{ detectors.EndpointSetter } // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) var _ detectors.EndpointCustomizer = (*Scanner)(nil) func (Scanner) Version() int { return 2 } func (Scanner) DefaultEndpoint() string { return "https://api.github.com" } var ( // Oauth token // https://developer.github.com/v3/#oauth2-token-sent-in-a-header // Token type list: // https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/ // https://github.blog/changelog/2022-10-18-introducing-fine-grained-personal-access-tokens/ keyPat = regexp.MustCompile(`\b((?:ghp|gho|ghu|ghs|ghr|github_pat)_[a-zA-Z0-9_]{36,255})\b`) // TODO: Oauth2 client_id and client_secret // https://developer.github.com/v3/#oauth2-keysecret ) // TODO: Add secret context?? Information about access, ownership etc type userRes struct { Login string `json:"login"` Type string `json:"type"` SiteAdmin bool `json:"site_admin"` Name string `json:"name"` Company string `json:"company"` UserURL string `json:"html_url"` } // 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{"ghp_", "gho_", "ghu_", "ghs_", "ghr_", "github_pat_"} } // FromData will find and optionally verify GitHub 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) for _, match := range matches { // First match is entire regex, second is the first group. if len(match) != 2 { continue } token := match[1] s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Github, Raw: []byte(token), ExtraData: map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/github/", }, } if verify { client := common.SaneHttpClient() // https://developer.github.com/v3/users/#get-the-authenticated-user for _, url := range s.Endpoints(s.DefaultEndpoint()) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/user", url), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json; charset=utf-8") req.Header.Add("Authorization", fmt.Sprintf("token %s", token)) res, err := client.Do(req) if err == nil { if res.StatusCode >= 200 && res.StatusCode < 300 { var userResponse userRes err = json.NewDecoder(res.Body).Decode(&userResponse) res.Body.Close() if err == nil { s1.Verified = true s1.ExtraData["username"] = userResponse.Login s1.ExtraData["url"] = userResponse.UserURL s1.ExtraData["account_type"] = userResponse.Type s1.ExtraData["site_admin"] = fmt.Sprintf("%t", userResponse.SiteAdmin) s1.ExtraData["name"] = userResponse.Name s1.ExtraData["company"] = userResponse.Company } } } } } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Github } trufflehog-3.60.0/pkg/detectors/github/github_test.go000066400000000000000000000110411451332102400226440ustar00rootroot00000000000000//go:build detectors // +build detectors package github import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGitHub_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } unverifiedGhp := testSecrets.MustGetField("GITHUB_UNVERIFIED_GHP") unverifiedGhpLong := testSecrets.MustGetField("GITHUB_UNVERIFIED_GHP_LONG") unverifiedGho := testSecrets.MustGetField("GITHUB_UNVERIFIED_GHO") unverifiedGhu := testSecrets.MustGetField("GITHUB_UNVERIFIED_GHU") unverifiedGhs := testSecrets.MustGetField("GITHUB_UNVERIFIED_GHS") unverifiedGhr := testSecrets.MustGetField("GITHUB_UNVERIFIED_GHR") 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, unverified ghp", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", unverifiedGhp)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "found, unverified gho", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", unverifiedGho)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "found, unverified ghu", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", unverifiedGhu)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "found, unverified ghs", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", unverifiedGhs)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "found, unverified ghr", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", unverifiedGhr)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "found, unverified ghp future length 255", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", unverifiedGhpLong)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte("https://raw.github.com/k/d890e8640f20fba3215ba7be8e0ff145aeb8c17c/include/base64.js"), 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("GitHub.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GitHub.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) } } }) } } trufflehog-3.60.0/pkg/detectors/github_oauth2/000077500000000000000000000000001451332102400212615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/github_oauth2/github_oauth2.go000066400000000000000000000046051451332102400243610ustar00rootroot00000000000000package github_oauth2 import ( "context" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "golang.org/x/oauth2/clientcredentials" "golang.org/x/oauth2/github" ) type Scanner struct{ detectors.EndpointSetter } // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // Oauth2 client ID and secret oauth2ClientIDPat = regexp.MustCompile(detectors.PrefixRegex([]string{"github"}) + `\b([a-f0-9]{20})\b`) oauth2ClientSecretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"github"}) + `\b([a-f0-9]{40})\b`) ) const ( githubBadVerificationCodeError = "bad_verification_code" ) // 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{"github"} } // FromData will find and optionally verify GitHub 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) // Oauth2 client ID and secret oauth2ClientIDMatches := oauth2ClientIDPat.FindAllStringSubmatch(dataStr, -1) oauth2ClientSecretMatches := oauth2ClientSecretPat.FindAllStringSubmatch(dataStr, -1) for _, idMatch := range oauth2ClientIDMatches { if len(idMatch) != 2 { continue } for _, secretMatch := range oauth2ClientSecretMatches { if len(secretMatch) != 2 { continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GitHubOauth2, Raw: []byte(idMatch[1]), RawV2: []byte(idMatch[1] + secretMatch[1]), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/github/", } config := &clientcredentials.Config{ ClientID: idMatch[1], ClientSecret: secretMatch[1], TokenURL: github.Endpoint.TokenURL, } _, err := config.Token(ctx) if err != nil && strings.Contains(err.Error(), githubBadVerificationCodeError) { s1.Verified = true } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GitHubOauth2 } trufflehog-3.60.0/pkg/detectors/github_old/000077500000000000000000000000001451332102400206355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/github_old/github_old.go000066400000000000000000000062301451332102400233050ustar00rootroot00000000000000package github_old import ( "context" "encoding/json" "fmt" "net/http" "regexp" "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{ detectors.EndpointSetter } // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) var _ detectors.EndpointCustomizer = (*Scanner)(nil) func (Scanner) Version() int { return 1 } func (Scanner) DefaultEndpoint() string { return "https://api.github.com" } var ( // Oauth token // https://developer.github.com/v3/#oauth2-token-sent-in-a-header keyPat = regexp.MustCompile(`(?i)(?:github|gh|pat)[^\.].{0,40}[ =:'"]+([a-f0-9]{40})\b`) // TODO: Oauth2 client_id and client_secret // https://developer.github.com/v3/#oauth2-keysecret ) // TODO: Add secret context?? Information about access, ownership etc type userRes struct { Login string `json:"login"` Type string `json:"type"` SiteAdmin bool `json:"site_admin"` Name string `json:"name"` Company string `json:"company"` } // 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{"github", "gh", "pat"} } // FromData will find and optionally verify GitHub 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) for _, match := range matches { // First match is entire regex, second is the first group. if len(match) != 2 { continue } token := match[1] specificFPs := []detectors.FalsePositive{"github commit"} if detectors.IsKnownFalsePositive(token, specificFPs, false) { continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Github, Raw: []byte(token), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/github/", } if verify { client := common.SaneHttpClient() // https://developer.github.com/v3/users/#get-the-authenticated-user for _, url := range s.Endpoints(s.DefaultEndpoint()) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/user", url), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json; charset=utf-8") req.Header.Add("Authorization", fmt.Sprintf("token %s", token)) res, err := client.Do(req) if err == nil { if res.StatusCode >= 200 && res.StatusCode < 300 { var userResponse userRes err = json.NewDecoder(res.Body).Decode(&userResponse) res.Body.Close() if err == nil { s1.Verified = true } } } } } if !s1.Verified && detectors.IsKnownFalsePositive(token, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Github } trufflehog-3.60.0/pkg/detectors/github_old/github_old_test.go000066400000000000000000000060121451332102400243420ustar00rootroot00000000000000//go:build detectors // +build detectors package github_old import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGitHub_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GITHUB_OLD") secretInactive := testSecrets.MustGetField("GITHUB_OLD_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 github secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a github secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Github, Verified: false, }, }, wantErr: false, }, { name: "not found url", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte("https://raw.github.com/k/d890e8640f20fba3215ba7be8e0ff145aeb8c17c/include/base64.js"), verify: true, }, want: nil, wantErr: false, }, { name: "not found ref", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte("https://github.com/lz4/lz4 @ dccf8826f1d76efcbdc655e63cc04cdbd1123619"), 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("GitHub.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GitHub.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) } } }) } } trufflehog-3.60.0/pkg/detectors/githubapp/000077500000000000000000000000001451332102400205005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/githubapp/githubapp.go000066400000000000000000000057351451332102400230240ustar00rootroot00000000000000package githubapp import ( "context" // b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "time" "github.com/golang-jwt/jwt" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. appPat = regexp.MustCompile(detectors.PrefixRegex([]string{"github"}) + `\b([0-9]{6})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"github"}) + `(-----BEGIN RSA PRIVATE KEY-----\s[A-Za-z0-9+\/\s]*\s-----END RSA PRIVATE KEY-----)`) ) // 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{"github"} } // FromData will find and optionally verify GitHubApp 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) appMatches := appPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, appMatch := range appMatches { if len(appMatch) != 2 { continue } appResMatch := strings.TrimSpace(appMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GitHubApp, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/github/", } if verify { signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(resMatch)) if err != nil { continue } // issued at time iat := time.Now().Add(-60 * time.Second) exp := time.Now().Add(9 * 60 * time.Second) iss := appResMatch token := jwt.New(jwt.SigningMethodRS256) claims := token.Claims.(jwt.MapClaims) claims["iat"] = iat.Unix() claims["exp"] = exp.Unix() claims["iss"] = iss tokenString, err := token.SignedString(signKey) if err != nil { continue } // end get token req, err := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/app", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.github.v3+json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tokenString)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GitHubApp } trufflehog-3.60.0/pkg/detectors/githubapp/githubapp_test.go000066400000000000000000000060771451332102400240630ustar00rootroot00000000000000//go:build detectors // +build detectors package githubapp import ( "context" b64 "encoding/base64" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGitHubApp_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GITHUBAPP_PRIVATEKEY") inactiveSecret := testSecrets.MustGetField("GITHUBAPP_PRIVATEKEY_INACTIVE") appKey := testSecrets.MustGetField("GITHUBAPP_APPKEY") sDec, _ := b64.StdEncoding.DecodeString(secret) inactiveSDec, _ := b64.StdEncoding.DecodeString(inactiveSecret) 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 githubapp secret %s within githubapp %s", sDec, appKey)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GitHubApp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a githubapp secret %s within githubapp %s but not valid", inactiveSDec, appKey)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GitHubApp, 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("GitHubApp.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GitHubApp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gitlab/000077500000000000000000000000001451332102400177575ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gitlab/gitlab.go000066400000000000000000000070461451332102400215570ustar00rootroot00000000000000package gitlab import ( "context" "fmt" "net/http" "regexp" "strings" "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.EndpointSetter } // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.EndpointCustomizer = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (Scanner) Version() int { return 1 } func (Scanner) DefaultEndpoint() string { return "https://gitlab.com" } var ( defaultClient = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gitlab"}) + `\b([a-zA-Z0-9\-=_]{20,22})\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{"gitlab"} } // FromData will find and optionally verify Gitlab 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) for _, match := range matches { if len(match) != 2 { continue } // ignore v2 detectors which have a prefix of `glpat-` if strings.Contains(match[0], "glpat-") { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gitlab, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/gitlab/", } if verify { isVerified, verificationErr := s.verifyGitlab(ctx, resMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error) { // there are 4 read 'scopes' for a gitlab token: api, read_user, read_repo, and read_registry // they all grant access to different parts of the API. I couldn't find an endpoint that every // one of these scopes has access to, so we just check an example endpoint for each scope. If any // of them contain data, we know we have a valid key, but if they all fail, we don't client := s.client if client == nil { client = defaultClient } for _, baseURL := range s.Endpoints(s.DefaultEndpoint()) { // test `read_user` scope req, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/api/v4/user", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() // The request body is unused. // 200 means good key and has `read_user` scope // 403 means good key but not the right scope // 401 is bad key switch res.StatusCode { case http.StatusOK: return true, nil case http.StatusForbidden: // Good key but not the right scope return true, nil case http.StatusUnauthorized: // Nothing to do; zero values are the ones we want return false, nil default: return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } return false, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gitlab } trufflehog-3.60.0/pkg/detectors/gitlab/gitlab_v1_test.go000066400000000000000000000113661451332102400232240ustar00rootroot00000000000000//go:build detectors // +build detectors package gitlab 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 TestGitlab_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GITLAB") secretInactive := testSecrets.MustGetField("GITLAB_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: true, }, }, wantErr: false, }, { name: "found only secret phrase", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("gitlab %s", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: false, }, }, wantErr: false, }, { name: "found, would be verified but for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found and valid but unexpected api response", s: Scanner{client: common.ConstantResponseHttpClient(500, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, good key but wrong scope", s: Scanner{client: common.ConstantResponseHttpClient(403, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: true, }, }, 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("Gitlab.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) } } opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gitlab/gitlab_v2_test.go000066400000000000000000000056141451332102400232240ustar00rootroot00000000000000//go:build detectors // +build detectors package gitlab 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" ) // This test ensures gitlab v1 detector does not work on gitlab v2 secrets func TestGitlab_FromChunk_WithV2Secrets(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GITLABV2") secretInactive := testSecrets.MustGetField("GITLABV2_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "verified secret, not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab secret %s within", secret)), verify: true, }, want: nil, wantErr: false, }, { name: "unverified secret, not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab secret %s within", secretInactive)), verify: true, }, want: nil, 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("Gitlab.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) } } opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } func BenchmarkV2FromData(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) } } }) } } trufflehog-3.60.0/pkg/detectors/gitlabv2/000077500000000000000000000000001451332102400202275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gitlabv2/gitlab_v1_test.go000066400000000000000000000062001451332102400234630ustar00rootroot00000000000000//go:build detectors // +build detectors package gitlabv2 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" ) // This test ensures gitlab v2 detector does not work on gitlab v1 secrets func TestGitlabV2_FromChunk_WithV1Secrets(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GITLAB") secretInactive := testSecrets.MustGetField("GITLAB_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "verified v1 secret, not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: nil, wantErr: false, }, { name: "verified v1 secret, not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("gitlab %s", secret)), verify: true, }, want: nil, wantErr: false, }, { name: "unverified v1 secret, not found", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab secret %s within", secretInactive)), verify: true, }, want: nil, 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("Gitlab.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) } } opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gitlabv2/gitlab_v2.go000066400000000000000000000066711451332102400224410ustar00rootroot00000000000000package gitlabv2 import ( "context" "fmt" "net/http" "regexp" "strings" "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.EndpointSetter } // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.EndpointCustomizer = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (Scanner) Version() int { return 2 } func (Scanner) DefaultEndpoint() string { return "https://gitlab.com" } var ( defaultClient = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gitlab"}) + `\b(glpat-[a-zA-Z0-9\-=_]{20,22})\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{"gitlab"} } // FromData will find and optionally verify Gitlab 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gitlab, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/gitlab/", } if verify { isVerified, verificationErr := s.verifyGitlab(ctx, resMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error) { // there are 4 read 'scopes' for a gitlab token: api, read_user, read_repo, and read_registry // they all grant access to different parts of the API. I couldn't find an endpoint that every // one of these scopes has access to, so we just check an example endpoint for each scope. If any // of them contain data, we know we have a valid key, but if they all fail, we don't client := s.client if client == nil { client = defaultClient } for _, baseURL := range s.Endpoints(s.DefaultEndpoint()) { // test `read_user` scope req, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/api/v4/user", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() // The request body is unused. // 200 means good key and has `read_user` scope // 403 means good key but not the right scope // 401 is bad key switch res.StatusCode { case http.StatusOK: return true, nil case http.StatusForbidden: // Good key but not the right scope return true, nil case http.StatusUnauthorized: // Nothing to do; zero values are the ones we want return false, nil default: return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } return false, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gitlab } trufflehog-3.60.0/pkg/detectors/gitlabv2/gitlab_v2_test.go000066400000000000000000000106621451332102400234730ustar00rootroot00000000000000//go:build detectors // +build detectors package gitlabv2 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 TestGitlabV2_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GITLABV2") secretInactive := testSecrets.MustGetField("GITLABV2_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: true, }, }, wantErr: false, }, { name: "found unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: false, }, }, wantErr: false, }, { name: "found, would be verified but for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found and valid but unexpected api response", s: Scanner{client: common.ConstantResponseHttpClient(500, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, good key but wrong scope", s: Scanner{client: common.ConstantResponseHttpClient(403, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitlab super secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitlab, Verified: true, }, }, 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("Gitlab.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf(" wantVerificationError = %v, verification error = %v,", tt.wantVerificationErr, got[i].VerificationError) } } opts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, opts); diff != "" { t.Errorf("Gitlab.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } func BenchmarkV2FromData(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) } } }) } } trufflehog-3.60.0/pkg/detectors/gitter/000077500000000000000000000000001451332102400200135ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gitter/gitter.go000066400000000000000000000041531451332102400216430ustar00rootroot00000000000000package gitter import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gitter"}) + `\b([a-z0-9-]{40})\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{"gitter"} } // FromData will find and optionally verify Gitter 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gitter, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.gitter.im/v1/user/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gitter } trufflehog-3.60.0/pkg/detectors/gitter/gitter_test.go000066400000000000000000000054461451332102400227100ustar00rootroot00000000000000//go:build detectors // +build detectors package gitter import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGitter_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("GITTER") inactiveSecret := testSecrets.MustGetField("GITTER_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 gitter secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gitter, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gitter 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_Gitter, 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("Gitter.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Gitter.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) } } }) } } trufflehog-3.60.0/pkg/detectors/glassnode/000077500000000000000000000000001451332102400204745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/glassnode/glassnode.go000066400000000000000000000042231451332102400230030ustar00rootroot00000000000000package glassnode import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"glassnode"}) + `\b([0-9A-Za-z]{27})\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{"glassnode"} } // FromData will find and optionally verify Glassnode 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Glassnode, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.glassnode.com/v1/metrics/indicators/sopr?a=btc&api_key="+resMatch, nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Glassnode } trufflehog-3.60.0/pkg/detectors/glassnode/glassnode_test.go000066400000000000000000000055041451332102400240450ustar00rootroot00000000000000//go:build detectors // +build detectors package glassnode import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGlassnode_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GLASSNODE") inactiveSecret := testSecrets.MustGetField("GLASSNODE_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 glassnode secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Glassnode, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a glassnode 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_Glassnode, 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("Glassnode.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Glassnode.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) } } }) } } trufflehog-3.60.0/pkg/detectors/glitterlyapi/000077500000000000000000000000001451332102400212265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/glitterlyapi/glitterlyapi.go000066400000000000000000000042411451332102400242670ustar00rootroot00000000000000package glitterlyapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"glitterlyapi"}) + `\b([0-9a-z-]{36})\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{"glitterlyapi"} } // FromData will find and optionally verify GlitterlyAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GlitterlyAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.glitterlyapi.com/auth", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GlitterlyAPI } trufflehog-3.60.0/pkg/detectors/glitterlyapi/glitterlyapi_test.go000066400000000000000000000055421451332102400253330ustar00rootroot00000000000000//go:build detectors // +build detectors package glitterlyapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGlitterlyAPI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GLITTERLYAPI") inactiveSecret := testSecrets.MustGetField("GLITTERLYAPI_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 glitterlyapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GlitterlyAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a glitterlyapi 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_GlitterlyAPI, 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("GlitterlyAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GlitterlyAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gocanvas/000077500000000000000000000000001451332102400203165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gocanvas/gocanvas.go000066400000000000000000000057551451332102400224620ustar00rootroot00000000000000package gocanvas import ( "context" "encoding/xml" "fmt" "io" "net/http" "net/url" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gocanvas"}) + `\b([0-9A-Za-z/+]{43}=[ \r\n]{1})`) emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gocanvas"}) + `\b([\w\.-]+@[\w-]+\.[\w\.-]{2,5})\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{"gocanvas"} } // FromData will find and optionally verify GoCanvas 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) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } resEmailMatch := strings.TrimSpace(emailMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GoCanvas, Raw: []byte(resMatch), } if verify { payload := url.Values{} payload.Add("username", resEmailMatch) req, err := http.NewRequest("GET", "https://www.gocanvas.com/apiv2/forms.xml", strings.NewReader(payload.Encode())) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() body, errBody := io.ReadAll(res.Body) if errBody == nil { response := Response{} if err := xml.Unmarshal(body, &response); err != nil { continue } if res.StatusCode >= 200 && res.StatusCode < 300 && response.Error == nil { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } } return results, nil } type Response struct { Error []struct { ErrorCode int `xml:"ErrorCode"` } `xml:"Error"` } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GoCanvas } trufflehog-3.60.0/pkg/detectors/gocanvas/gocanvas_test.go000066400000000000000000000056041451332102400235120ustar00rootroot00000000000000//go:build detectors // +build detectors package gocanvas import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGoCanvas_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } username := testSecrets.MustGetField("SCANNERS_EMAIL") secret := testSecrets.MustGetField("GOCANVAS") inactiveSecret := testSecrets.MustGetField("GOCANVAS_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 gocanvas username %s with gocanvas secret %s within", username, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GoCanvas, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gocanvas username %s with gocanvas secret %s within but not valid", username, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GoCanvas, 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("GoCanvas.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GoCanvas.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/gocardless/000077500000000000000000000000001451332102400206435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gocardless/gocardless.go000066400000000000000000000045301451332102400233220ustar00rootroot00000000000000package gocardless import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // Removed bounds at the end of the regex since there are some cases that the token ends with an underscore (_) or a dash (-) keyPat = regexp.MustCompile(`\b(live_[0-9A-Za-z\_\-]{40}[ "'\r\n]{1})`) ) // 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{"gocardless"} } // FromData will find and optionally verify GoCardless 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GoCardless, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.gocardless.com/customers/", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) req.Header.Add("GoCardless-Version", "2015-07-06") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GoCardless } trufflehog-3.60.0/pkg/detectors/gocardless/gocardless_test.go000066400000000000000000000055161451332102400243660ustar00rootroot00000000000000//go:build detectors // +build detectors package gocardless import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGoCardless_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("GOCARDLESS") inactiveSecret := testSecrets.MustGetField("GOCARDLESS_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 gocardless secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GoCardless, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gocardless 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_GoCardless, 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("GoCardless.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GoCardless.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) } } }) } } trufflehog-3.60.0/pkg/detectors/goodday/000077500000000000000000000000001451332102400201435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/goodday/goodday.go000066400000000000000000000041201451332102400221150ustar00rootroot00000000000000package goodday import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"goodday"}) + `\b([a-z0-9]{32})\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{"goodday"} } // FromData will find and optionally verify GoodDay 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GoodDay, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.goodday.work/2.0/users", nil) if err != nil { continue } req.Header.Add("gd-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 } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GoodDay } trufflehog-3.60.0/pkg/detectors/goodday/goodday_test.go000066400000000000000000000054601451332102400231640ustar00rootroot00000000000000//go:build detectors // +build detectors package goodday import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGoodDay_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GOODDAY") inactiveSecret := testSecrets.MustGetField("GOODDAY_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 goodday secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GoodDay, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a goodday 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_GoodDay, 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("GoodDay.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GoodDay.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) } } }) } } trufflehog-3.60.0/pkg/detectors/graphcms/000077500000000000000000000000001451332102400203215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/graphcms/graphcms.go000066400000000000000000000046641451332102400224660ustar00rootroot00000000000000package graphcms import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(ey[a-zA-Z0-9]{73}.ey[a-zA-Z0-9]{365}.[a-zA-Z0-9_-]{683})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"graph"}) + `\b([a-z0-9]{25})\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{"graphcms"} } // FromData will find and optionally verify GraphCMS 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GraphCMS, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{users {id name}}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api-ap-northeast-1.graphcms.com/v2/"+resIdMatch+"/master", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/graphql") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } if !s1.Verified { if detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GraphCMS } trufflehog-3.60.0/pkg/detectors/graphcms/graphcms_test.go000066400000000000000000000056431451332102400235230ustar00rootroot00000000000000//go:build detectors // +build detectors package graphcms import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGraphCMS_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GRAPHCMS_TOKEN") inactiveSecret := testSecrets.MustGetField("GRAPHCMS_INACTIVE") id := testSecrets.MustGetField("GRAPHCMS_ID") 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 graphcms secret %s within graphcms %s but verified", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GraphCMS, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a graphcms secret Bearer %s within graphcms %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GraphCMS, 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("GraphCMS.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GraphCMS.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) } } }) } } trufflehog-3.60.0/pkg/detectors/graphhopper/000077500000000000000000000000001451332102400210345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/graphhopper/graphhopper.go000066400000000000000000000041711451332102400237050ustar00rootroot00000000000000package graphhopper import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"graphhopper"}) + `\b([a-z0-9-]{36})\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{"graphhopper"} } // FromData will find and optionally verify Graphhopper 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Graphhopper, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://graphhopper.com/api/1/geocode?q=Philippines&key=%s&type=json", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Graphhopper } trufflehog-3.60.0/pkg/detectors/graphhopper/graphhopper_test.go000066400000000000000000000055301451332102400247440ustar00rootroot00000000000000//go:build detectors // +build detectors package graphhopper import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGraphhopper_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("GRAPHHOPPER") inactiveSecret := testSecrets.MustGetField("GRAPHHOPPER_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 graphhopper secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Graphhopper, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a graphhopper 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_Graphhopper, 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("Graphhopper.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Graphhopper.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) } } }) } } trufflehog-3.60.0/pkg/detectors/groovehq/000077500000000000000000000000001451332102400203475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/groovehq/groovehq.go000066400000000000000000000041631451332102400225340ustar00rootroot00000000000000package groovehq import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"groove"}) + `\b([a-z0-9A-Z]{64})`) ) // 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{"groovehq"} } // FromData will find and optionally verify Groovehq 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Groovehq, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.groovehq.com/v1/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Groovehq } trufflehog-3.60.0/pkg/detectors/groovehq/groovehq_test.go000066400000000000000000000055001451332102400235670ustar00rootroot00000000000000//go:build detectors // +build detectors package groovehq import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGroovehq_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GROOVEHQ_TOKEN") inactiveSecret := testSecrets.MustGetField("GROOVEHQ_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 groovehq secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Groovehq, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a groovehq 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_Groovehq, 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("Groovehq.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Groovehq.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gtmetrix/000077500000000000000000000000001451332102400203605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gtmetrix/gtmetrix.go000066400000000000000000000041121451332102400225500ustar00rootroot00000000000000package gtmetrix import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gtmetrix"}) + `\b([0-9a-f]{32})\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{"gtmetrix"} } // FromData will find and optionally verify GTMetrix 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_GTMetrix, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://gtmetrix.com/api/2.0/status", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_GTMetrix } trufflehog-3.60.0/pkg/detectors/gtmetrix/gtmetrix_test.go000066400000000000000000000054201451332102400236120ustar00rootroot00000000000000package gtmetrix import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGTMetrix_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GTMETRIX") inactiveSecret := testSecrets.MustGetField("GTMETRIX_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 gtmetrix secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_GTMetrix, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gtmetrix 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_GTMetrix, 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("GTMetrix.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("GTMetrix.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) } } }) } } trufflehog-3.60.0/pkg/detectors/guardianapi/000077500000000000000000000000001451332102400210015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/guardianapi/guardianapi.go000066400000000000000000000041241451332102400236150ustar00rootroot00000000000000package guardianapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b([0-9Aa-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"guardianapi"} } // FromData will find and optionally verify Guardianapi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Guardianapi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://content.guardianapis.com/search?api-key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Guardianapi } trufflehog-3.60.0/pkg/detectors/guardianapi/guardianapi_test.go000066400000000000000000000055361451332102400246640ustar00rootroot00000000000000//go:build detectors // +build detectors package guardianapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGuardianapi_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GUARDIANAPI_TOKEN") inactiveSecret := testSecrets.MustGetField("GUARDIANAPI_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 guardianapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Guardianapi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a guardianapi 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_Guardianapi, 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("Guardianapi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Guardianapi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gumroad/000077500000000000000000000000001451332102400201535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gumroad/gumroad.go000066400000000000000000000042561451332102400221470ustar00rootroot00000000000000package gumroad import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gumroad"}) + `\b([a-z0-9A-Z-]{43})\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{"gumroad"} } // FromData will find and optionally verify Gumroad 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gumroad, Raw: []byte(resMatch), } payload := strings.NewReader("access_token=" + resMatch) if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.gumroad.com/v2/products", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gumroad } trufflehog-3.60.0/pkg/detectors/gumroad/gumroad_test.go000066400000000000000000000054601451332102400232040ustar00rootroot00000000000000//go:build detectors // +build detectors package gumroad import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGumroad_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("GUMROAD") inactiveSecret := testSecrets.MustGetField("GUMROAD_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 gumroad secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gumroad, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gumroad 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_Gumroad, 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("Gumroad.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Gumroad.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) } } }) } } trufflehog-3.60.0/pkg/detectors/guru/000077500000000000000000000000001451332102400174775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/guru/guru.go000066400000000000000000000052261451332102400210150ustar00rootroot00000000000000package guru import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. unamePat = regexp.MustCompile(detectors.PrefixRegex([]string{"guru"}) + `\b([a-zA-Z0-9]{3,20}@[a-zA-Z0-9]{2,12}.[a-zA-Z0-9]{2,5})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"guru"}) + `\b([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})\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{"guru"} } // FromData will find and optionally verify Guru 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) unameMatches := unamePat.FindAllStringSubmatch(dataStr, -1) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range unameMatches { if len(match) != 2 { continue } unameMatch := strings.TrimSpace(match[1]) for _, secret := range keyMatches { if len(secret) != 2 { continue } keyMatch := strings.TrimSpace(secret[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Guru, Raw: []byte(unameMatch), } if verify { data := fmt.Sprintf("%s:%s", unameMatch, keyMatch) encoded := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getguru.com/api/v1/teams/teamId/stats", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", encoded)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(keyMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Guru } trufflehog-3.60.0/pkg/detectors/guru/guru_test.go000066400000000000000000000056211451332102400220530ustar00rootroot00000000000000//go:build detectors // +build detectors package guru import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGuru_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } username := testSecrets.MustGetField("GURU") secret := testSecrets.MustGetField("GURU_SECRET") inactiveSecret := testSecrets.MustGetField("GURU_SECRET_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 guru secret %s within guru username %s but verified", secret, username)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Guru, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a guru secret %s within guru username %s but not valid", inactiveSecret, username)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Guru, 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("Guru.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Guru.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) } } }) } } trufflehog-3.60.0/pkg/detectors/gyazo/000077500000000000000000000000001451332102400176465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/gyazo/gyazo.go000066400000000000000000000040611451332102400213270ustar00rootroot00000000000000package gyazo import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gyazo"}) + `\b([0-9A-Za-z-]{43})\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{"gyazo"} } // FromData will find and optionally verify Gyazo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Gyazo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.gyazo.com/api/images?access_token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Gyazo } trufflehog-3.60.0/pkg/detectors/gyazo/gyazo_test.go000066400000000000000000000054341451332102400223730ustar00rootroot00000000000000//go:build detectors // +build detectors package gyazo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestGyazo_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("GYAZO") inactiveSecret := testSecrets.MustGetField("GYAZO_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 gyazo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Gyazo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a gyazo 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_Gyazo, 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("Gyazo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Gyazo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/happi/000077500000000000000000000000001451332102400176165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/happi/happi.go000066400000000000000000000041031451332102400212440ustar00rootroot00000000000000package happi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"happi"}) + `\b([a-zA-Z0-9]{56})`) ) // 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{"happi"} } // FromData will find and optionally verify Happi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Happi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.happi.dev/v1/exchange", nil) if err != nil { continue } req.Header.Add("x-happi-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Happi } trufflehog-3.60.0/pkg/detectors/happi/happi_test.go000066400000000000000000000054341451332102400223130ustar00rootroot00000000000000//go:build detectors // +build detectors package happi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHappi_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HAPPI") inactiveSecret := testSecrets.MustGetField("HAPPI_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 happi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Happi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a happi 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_Happi, 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("Happi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Happi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/happyscribe/000077500000000000000000000000001451332102400210265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/happyscribe/happyscribe.go000066400000000000000000000042331451332102400236700ustar00rootroot00000000000000package happyscribe import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"happyscribe"}) + `\b([0-9a-zA-Z]{24})\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{"happyscribe"} } // FromData will find and optionally verify Happyscribe 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Happyscribe, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.happyscribe.com/api/v1/transcriptions", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Happyscribe } trufflehog-3.60.0/pkg/detectors/happyscribe/happyscribe_test.go000066400000000000000000000055301451332102400247300ustar00rootroot00000000000000//go:build detectors // +build detectors package happyscribe import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHappyscribe_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("HAPPYSCRIBE") inactiveSecret := testSecrets.MustGetField("HAPPYSCRIBE_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 happyscribe secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Happyscribe, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a happyscribe 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_Happyscribe, 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("Happyscribe.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Happyscribe.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) } } }) } } trufflehog-3.60.0/pkg/detectors/harvest/000077500000000000000000000000001451332102400201715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/harvest/harvest.go000066400000000000000000000046201451332102400221760ustar00rootroot00000000000000package harvest import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"harvest"}) + `\b([a-z0-9A-Z._]{97})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"harvest"}) + `\b([0-9]{4,9})\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{"harvest"} } // FromData will find and optionally verify Harvest 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Harvest, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.harvestapp.com/v2/users/me?access_token="+resMatch+"&account_id="+resIdMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Harvest } trufflehog-3.60.0/pkg/detectors/harvest/harvest_test.go000066400000000000000000000056061451332102400232420ustar00rootroot00000000000000//go:build detectors // +build detectors package harvest import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHarvest_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HARVEST_TOKEN") inactiveSecret := testSecrets.MustGetField("HARVEST_INACTIVE") id := testSecrets.MustGetField("HARVEST_ID") 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 harvest secret %s within harvestid %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Harvest, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a harvest secret %s within harvestid %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Harvest, 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("Harvest.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Harvest.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) } } }) } } trufflehog-3.60.0/pkg/detectors/heatmapapi/000077500000000000000000000000001451332102400206265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/heatmapapi/heatmapapi.go000066400000000000000000000041611451332102400232700ustar00rootroot00000000000000package heatmapapi import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"heatmapapi"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"heatmapapi"} } // FromData will find and optionally verify Heatmapapi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Heatmapapi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://www.heatmapapi.com/javascript/HeatmapAPI2.aspx?k=%s", resMatch), nil) if err != nil { continue } 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, "poststr") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Heatmapapi } trufflehog-3.60.0/pkg/detectors/heatmapapi/heatmapapi_test.go000066400000000000000000000054441451332102400243340ustar00rootroot00000000000000package heatmapapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHeatmapapi_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HEATMAPAPI") inactiveSecret := testSecrets.MustGetField("HEATMAPAPI_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 heatmapapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Heatmapapi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a heatmapapi 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_Heatmapapi, 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("Heatmapapi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Heatmapapi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hellosign/000077500000000000000000000000001451332102400205015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hellosign/hellosign.go000066400000000000000000000041321451332102400230140ustar00rootroot00000000000000package hellosign import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hellosign"}) + `\b([a-zA-Z-0-9/+]{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{"hellosign"} } // FromData will find and optionally verify HelloSign 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HelloSign, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.hellosign.com/v3/account", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_HelloSign } trufflehog-3.60.0/pkg/detectors/hellosign/hellosign_test.go000066400000000000000000000055041451332102400240570ustar00rootroot00000000000000//go:build detectors // +build detectors package hellosign import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHelloSign_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("HELLOSIGN") inactiveSecret := testSecrets.MustGetField("HELLOSIGN_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 hellosign secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HelloSign, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hellosign 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_HelloSign, 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("HelloSign.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("HelloSign.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) } } }) } } trufflehog-3.60.0/pkg/detectors/helpcrunch/000077500000000000000000000000001451332102400206505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/helpcrunch/helpcrunch.go000066400000000000000000000042211451332102400233310ustar00rootroot00000000000000package helpcrunch import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"helpcrunch"}) + `\b([a-zA-Z-0-9+/=]{328})`) ) // 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{"helpcrunch"} } // FromData will find and optionally verify HelpCrunch 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HelpCrunch, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.helpcrunch.com/v1/departments", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_HelpCrunch } trufflehog-3.60.0/pkg/detectors/helpcrunch/helpcrunch_test.go000066400000000000000000000055241451332102400243770ustar00rootroot00000000000000//go:build detectors // +build detectors package helpcrunch import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHelpCrunch_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HELPCRUNCH_TOKEN") inactiveSecret := testSecrets.MustGetField("HELPCRUNCH_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 helpcrunch secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HelpCrunch, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a helpcrunch 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_HelpCrunch, 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("HelpCrunch.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("HelpCrunch.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) } } }) } } trufflehog-3.60.0/pkg/detectors/helpscout/000077500000000000000000000000001451332102400205235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/helpscout/helpscout.go000066400000000000000000000042121451332102400230570ustar00rootroot00000000000000package helpscout import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"helpscout"}) + `\b([A-Za-z0-9]{56})\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{"helpscout"} } // FromData will find and optionally verify Helpscout 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Helpscout, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://docsapi.helpscout.net/v1/collections", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Helpscout } trufflehog-3.60.0/pkg/detectors/helpscout/helpscout_test.go000066400000000000000000000055121451332102400241220ustar00rootroot00000000000000//go:build detectors // +build detectors package helpscout import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHelpscout_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HELPSCOUT_TOKEN") inactiveSecret := testSecrets.MustGetField("HELPSCOUT_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 helpscout secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Helpscout, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a helpscout 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_Helpscout, 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("Helpscout.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Helpscout.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hereapi/000077500000000000000000000000001451332102400201325ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hereapi/hereapi.go000066400000000000000000000042001451332102400220720ustar00rootroot00000000000000package hereapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hereapi"}) + `\b([a-zA-Z0-9\S]{43})\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{"hereapi"} } // FromData will find and optionally verify HereAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HereAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://weather.ls.hereapi.com/weather/1.0/report.json?product=alerts&name=Argentina&apiKey=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_HereAPI } trufflehog-3.60.0/pkg/detectors/hereapi/hereapi_test.go000066400000000000000000000054601451332102400231420ustar00rootroot00000000000000//go:build detectors // +build detectors package hereapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHereAPI_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("HEREAPI") inactiveSecret := testSecrets.MustGetField("HEREAPI_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 hereapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HereAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hereapi 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_HereAPI, 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("HereAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("HereAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/heroku/000077500000000000000000000000001451332102400200125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/heroku/heroku.go000066400000000000000000000043341451332102400216420ustar00rootroot00000000000000package heroku import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"heroku"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"heroku"} } // FromData will find and optionally verify Heroku 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Heroku, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.heroku.com/apps", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.heroku+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Heroku } trufflehog-3.60.0/pkg/detectors/heroku/heroku_test.go000066400000000000000000000054461451332102400227060ustar00rootroot00000000000000//go:build detectors // +build detectors package heroku import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHeroku_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HEROKU") inactiveSecret := testSecrets.MustGetField("HEROKU_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 heroku secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Heroku, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a heroku 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_Heroku, 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("Heroku.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Heroku.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hive/000077500000000000000000000000001451332102400174505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hive/hive.go000066400000000000000000000045621451332102400207410ustar00rootroot00000000000000package hive import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = 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{"hive"}) + `\b([0-9A-Za-z]{17})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hive"}) + `\b([0-9a-z]{32})\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{"hive"} } // FromData will find and optionally verify Hive 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range idMatches { if len(match) != 2 { continue } idMatch := strings.TrimSpace(match[1]) for _, match := range keyMatches { if len(match) != 2 { continue } keyMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Hive, Raw: []byte(idMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.hive.com/api/v1/testcredentials?user_id="+idMatch+"&api_key="+keyMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(keyMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Hive } trufflehog-3.60.0/pkg/detectors/hive/hive_test.go000066400000000000000000000055631451332102400220020ustar00rootroot00000000000000//go:build detectors // +build detectors package hive import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHive_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } id := testSecrets.MustGetField("HIVE") secret := testSecrets.MustGetField("HIVE_SECRET") inactiveSecret := testSecrets.MustGetField("HIVE_SECRET_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 hive secret %s within hive id %s but verified", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hive, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hive secret %s within hive id %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hive, 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("Hive.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Hive.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hiveage/000077500000000000000000000000001451332102400201255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hiveage/hiveage.go000066400000000000000000000044471451332102400220750ustar00rootroot00000000000000package hiveage import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hiveage"}) + `\b([0-9A-Za-z\_\-]{20})\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{"hiveage"} } // FromData will find and optionally verify Hiveage 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Hiveage, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) encoded := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://mltb8350.hiveage.com/api/network", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", encoded)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Hiveage } trufflehog-3.60.0/pkg/detectors/hiveage/hiveage_test.go000066400000000000000000000054601451332102400231300ustar00rootroot00000000000000//go:build detectors // +build detectors package hiveage import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHiveage_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HIVEAGE") inactiveSecret := testSecrets.MustGetField("HIVEAGE_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 hiveage secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hiveage, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hiveage 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_Hiveage, 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("Hiveage.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Hiveage.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) } } }) } } trufflehog-3.60.0/pkg/detectors/holidayapi/000077500000000000000000000000001451332102400206405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/holidayapi/holidayapi.go000066400000000000000000000041611451332102400233140ustar00rootroot00000000000000package holidayapi import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"holidayapi"}) + `\b([a-z0-9-]{36})\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{"holidayapi"} } // FromData will find and optionally verify HolidayAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HolidayAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://holidayapi.com/v1/holidays?key=%s&country=PH&year=2020", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_HolidayAPI } trufflehog-3.60.0/pkg/detectors/holidayapi/holidayapi_test.go000066400000000000000000000055161451332102400243600ustar00rootroot00000000000000//go:build detectors // +build detectors package holidayapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHolidayAPI_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("HOLIDAYAPI") inactiveSecret := testSecrets.MustGetField("HOLIDAYAPI_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 holidayapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HolidayAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a holidayapi 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_HolidayAPI, 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("HolidayAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("HolidayAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/holistic/000077500000000000000000000000001451332102400203335ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/holistic/holistic.go000066400000000000000000000044451451332102400225070ustar00rootroot00000000000000package holistic import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"holistic"}) + `\b([0-9a-f]{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{"holistic"} } // FromData will find and optionally verify Holistic 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Holistic, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.holistic.dev/api/v1/project", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err != nil { continue } bodyString := string(bodyBytes) errorResponse := strings.Contains(bodyString, `"data":[]`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 && !errorResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Holistic } trufflehog-3.60.0/pkg/detectors/holistic/holistic_test.go000066400000000000000000000054201451332102400235400ustar00rootroot00000000000000package holistic import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHolistic_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HOLISTIC") inactiveSecret := testSecrets.MustGetField("HOLISTIC_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 holistic secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Holistic, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a holistic 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_Holistic, 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("Holistic.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Holistic.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) } } }) } } trufflehog-3.60.0/pkg/detectors/honeycomb/000077500000000000000000000000001451332102400205005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/honeycomb/honeycomb.go000066400000000000000000000042531451332102400230160ustar00rootroot00000000000000package honeycomb import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // Older + Newer API key format. See pull#687 for discussion keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"Honeycomb"}) + `\b([0-9a-f]{32}|[0-9a-zA-Z]{22})\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{"Honeycomb"} } // FromData will find and optionally verify Honeycomb 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Honeycomb, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.honeycomb.io/1/auth", nil) if err != nil { continue } req.Header.Add("X-Honeycomb-Team", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Honeycomb } trufflehog-3.60.0/pkg/detectors/honeycomb/honeycomb_test.go000066400000000000000000000055041451332102400240550ustar00rootroot00000000000000//go:build detectors // +build detectors package honeycomb import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHoneycomb_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HONEYCOMB") inactiveSecret := testSecrets.MustGetField("HONEYCOMB_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 honeycomb secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Honeycomb, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a honeycomb 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_Honeycomb, 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("Honeycomb.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Honeycomb.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) } } }) } } trufflehog-3.60.0/pkg/detectors/host/000077500000000000000000000000001451332102400174725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/host/host.go000066400000000000000000000041001451332102400207710ustar00rootroot00000000000000package host import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"host"}) + `\b([a-z0-9]{14})\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{"host.io"} } // FromData will find and optionally verify Host 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Host, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://host.io/api/domains/ip/8.8.8.8?token=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Host } trufflehog-3.60.0/pkg/detectors/host/host_test.go000066400000000000000000000054221451332102400220400ustar00rootroot00000000000000//go:build detectors // +build detectors package host import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHost_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("HOST") inactiveSecret := testSecrets.MustGetField("HOST_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 host secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Host, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a host 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_Host, 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("Host.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Host.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) } } }) } } trufflehog-3.60.0/pkg/detectors/html2pdf/000077500000000000000000000000001451332102400202355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/html2pdf/html2pdf.go000066400000000000000000000042361451332102400223110ustar00rootroot00000000000000package html2pdf import ( "bytes" "context" "encoding/json" "net/http" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} type html2pdfRequest struct { HTML string `json:"html"` ApiKey string `json:"apiKey"` } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"html2pdf"}) + `\b([a-zA-Z0-9]{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{"html2pdf"} } // FromData will find and optionally verify Html2Pdf 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Html2Pdf, Raw: []byte(resMatch), } if verify { req := html2pdfRequest{ HTML: "Helloworld", ApiKey: resMatch, } reqJson, _ := json.Marshal(&req) reqBuf := bytes.NewReader(reqJson) res, err := http.Post("https://api.html2pdf.app/v1/generate", "application/json", reqBuf) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Html2Pdf } trufflehog-3.60.0/pkg/detectors/html2pdf/html2pdf_test.go000066400000000000000000000054721451332102400233530ustar00rootroot00000000000000//go:build detectors // +build detectors package html2pdf import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHtml2Pdf_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("HTML2PDF") inactiveSecret := testSecrets.MustGetField("HTML2PDF_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 html2pdf secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Html2Pdf, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a html2pdf 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_Html2Pdf, 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("Html2Pdf.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Html2Pdf.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hubspotapikey/000077500000000000000000000000001451332102400214045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hubspotapikey/hubspotapikey.go000066400000000000000000000036371451332102400246330ustar00rootroot00000000000000package hubspotapikey import ( "context" // "log" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hubspot"}) + `\b([A-Za-z0-9]{8}\-[A-Za-z0-9]{4}\-[A-Za-z0-9]{4}\-[A-Za-z0-9]{4}\-[A-Za-z0-9]{12})\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{"hubspot"} } // FromData will find and optionally verify HubSpotApiKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HubSpotApiKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.hubapi.com/contacts/v1/lists?hapikey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_HubSpotApiKey } trufflehog-3.60.0/pkg/detectors/hubspotapikey/hubspotapikey_test.go000066400000000000000000000054651451332102400256730ustar00rootroot00000000000000//go:build detectors // +build detectors package hubspotapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHubSpotApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HUBSPOTAPIKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("HUBSPOTAPIKEY_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 hubspotapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HubSpotApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hubspotapikey secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HubSpotApiKey, 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("HubSpotApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("HubSpotApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/huggingface/000077500000000000000000000000001451332102400207645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/huggingface/huggingface.go000066400000000000000000000050041451332102400235610ustar00rootroot00000000000000package huggingface import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // 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. keyPat = regexp.MustCompile(`\bhf_[a-zA-Z]{34}\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{"huggingface", "hugging_face", "hf"} // Huggingface docs occasionally use "hf" instead of "huggingface" } // FromData will find and optionally verify Huggingface 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) for _, match := range matches { if len(match) != 1 { continue } resMatch := strings.TrimSpace(match[0]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_HuggingFace, Raw: []byte(resMatch), } if verify { client := s.client if client == nil { client = defaultClient } req, err := http.NewRequestWithContext(ctx, "GET", "https://huggingface.co/api/whoami-v2", nil) if err != nil { continue } req.Header.Set("Authorization", "Bearer "+resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_HuggingFace } trufflehog-3.60.0/pkg/detectors/huggingface/huggingface_test.go000066400000000000000000000104561451332102400246270ustar00rootroot00000000000000//go:build detectors // +build detectors package huggingface import ( "context" "fmt" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHuggingface_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HUGGINGFACE") inactiveSecret := testSecrets.MustGetField("HUGGINGFACE_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a huggingface secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HuggingFace, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a huggingface 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_HuggingFace, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a huggingface secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HuggingFace, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a huggingface secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_HuggingFace, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("Huggingface.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf(" wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Huggingface.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) } } }) } } trufflehog-3.60.0/pkg/detectors/humanity/000077500000000000000000000000001451332102400203535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/humanity/humanity.go000066400000000000000000000040721451332102400225430ustar00rootroot00000000000000package humanity import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"humanity"}) + `\b([0-9a-z]{40})\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{"humanity"} } // FromData will find and optionally verify Humanity 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Humanity, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.humanity.com/api/v2/me?access_token="+resMatch, nil) if err != nil { continue } 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) validResponse := strings.Contains(body, "name") if validResponse { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Humanity } trufflehog-3.60.0/pkg/detectors/humanity/humanity_test.go000066400000000000000000000054721451332102400236070ustar00rootroot00000000000000//go:build detectors // +build detectors package humanity import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHumanity_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("HUMANITY") inactiveSecret := testSecrets.MustGetField("HUMANITY_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 humanity secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Humanity, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a humanity 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_Humanity, 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("Humanity.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Humanity.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hunter/000077500000000000000000000000001451332102400200225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hunter/hunter.go000066400000000000000000000035071451332102400216630ustar00rootroot00000000000000package hunter import ( "context" // "fmt" // "log" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hunter"}) + `\b([a-z0-9_-]{40})\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{"hunter"} } // FromData will find and optionally verify Hunter 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Hunter, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.hunter.io/v2/leads_lists?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Hunter } trufflehog-3.60.0/pkg/detectors/hunter/hunter_test.go000066400000000000000000000054741451332102400227270ustar00rootroot00000000000000//go:build detectors // +build detectors package hunter import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHunter_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HUNTER_IO_API_KEY") secretInactive := testSecrets.MustGetField("HUNTER_IO_API_KEY_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 hunter secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hunter, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hunter secret %s within but not valid", secretInactive)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hunter, 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("Hunter.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Hunter.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hybiscus/000077500000000000000000000000001451332102400203465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hybiscus/hybiscus.go000066400000000000000000000042571451332102400225360ustar00rootroot00000000000000package hybiscus import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hybiscus"}) + `\b([0-9a-zA-Z]{43})\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{"hybiscus"} } // FromData will find and optionally verify Hybiscus 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Hybiscus, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.hybiscus.dev/api/v1/get-remaining-quota", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.hybiscus+json; version=3") req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Hybiscus } trufflehog-3.60.0/pkg/detectors/hybiscus/hybiscus_test.go000066400000000000000000000054721451332102400235750ustar00rootroot00000000000000//go:build detectors // +build detectors package hybiscus import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHybiscus_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("HYBISCUS") inactiveSecret := testSecrets.MustGetField("HYBISCUS_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 hybiscus secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hybiscus, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hybiscus 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_Hybiscus, 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("Hybiscus.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Hybiscus.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) } } }) } } trufflehog-3.60.0/pkg/detectors/hypertrack/000077500000000000000000000000001451332102400206715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/hypertrack/hypertrack.go000066400000000000000000000051401451332102400233740ustar00rootroot00000000000000package hypertrack import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives.' accPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hypertrack"}) + `\b([0-9a-zA-Z\_\-]{27})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"hypertrack"}) + `\b([0-9a-zA-Z\_\-]{54})\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{"hypertrack"} } // FromData will find and optionally verify Hypertrack 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) accMatches := accPat.FindAllStringSubmatch(dataStr, -1) matches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, accMatch := range accMatches { if len(accMatch) != 2 { continue } resAccMatch := strings.TrimSpace(accMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Hypertrack, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://v3.api.hypertrack.com/trips/", nil) if err != nil { continue } req.SetBasicAuth(resAccMatch, resMatch) req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resAccMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Hypertrack } trufflehog-3.60.0/pkg/detectors/hypertrack/hypertrack_test.go000066400000000000000000000060451451332102400244400ustar00rootroot00000000000000//go:build detectors // +build detectors package hypertrack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestHypertrack_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("HYPERTRACK") inactiveSecret := testSecrets.MustGetField("HYPERTRACK_INACTIVE") account := testSecrets.MustGetField("HYPERTRACK_ACCOUNT") inactiveAccount := testSecrets.MustGetField("HYPERTRACK_ACCOUNT_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 hypertrack account %s with hypertrack secret %s within", account, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hypertrack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a hypertrack account %s with hypertrack secret %s within but not valid", inactiveAccount, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Hypertrack, 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("Hypertrack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Hypertrack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ibmclouduserkey/000077500000000000000000000000001451332102400217235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ibmclouduserkey/ibmclouduserkey.go000066400000000000000000000045001451332102400254570ustar00rootroot00000000000000package ibmclouduserkey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ibm"}) + `\b([A-Za-z0-9_-]{44})\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{"ibm"} } // FromData will find and optionally verify IbmCloudUserKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_IbmCloudUserKey, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`apikey=` + resMatch + `&grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey`) req, err := http.NewRequestWithContext(ctx, "POST", "https://iam.cloud.ibm.com/identity/token", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", "Basic Yng6Yng=") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_IbmCloudUserKey } trufflehog-3.60.0/pkg/detectors/ibmclouduserkey/ibmclouduserkey_test.go000066400000000000000000000054531451332102400265260ustar00rootroot00000000000000//go:build detectors // +build detectors package ibmclouduserkey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIbmCloudUserKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("IBMCLOUDUSERKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("IBMCLOUDUSERKEY_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 ibmclouduserkey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_IbmCloudUserKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ibmclouduserkey 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_IbmCloudUserKey, 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("IbmCloudUserKey.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("IbmCloudUserKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/iconfinder/000077500000000000000000000000001451332102400206355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/iconfinder/iconfinder.go000066400000000000000000000042131451332102400233040ustar00rootroot00000000000000package iconfinder import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"iconfinder"}) + `\b([a-zA-Z0-9]{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{"iconfinder"} } // FromData will find and optionally verify IconFinder 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_IconFinder, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.iconfinder.com/v4/iconsets", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_IconFinder } trufflehog-3.60.0/pkg/detectors/iconfinder/iconfinder_test.go000066400000000000000000000055161451332102400243520ustar00rootroot00000000000000//go:build detectors // +build detectors package iconfinder import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIconFinder_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("ICONFINDER") inactiveSecret := testSecrets.MustGetField("ICONFINDER_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 iconfinder secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_IconFinder, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a iconfinder 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_IconFinder, 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("IconFinder.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("IconFinder.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) } } }) } } trufflehog-3.60.0/pkg/detectors/iexapis/000077500000000000000000000000001451332102400201575ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/iexapis/iexapis.go000066400000000000000000000041061451332102400221510ustar00rootroot00000000000000package iexapis import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"iexapis"}) + `\b(sk_[a-z0-9]{32})\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{"iexapis"} } // FromData will find and optionally verify Iexapis 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Iexapis, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.iexapis.com/stable/stock/aapl/quote?token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Iexapis } trufflehog-3.60.0/pkg/detectors/iexapis/iexapis_test.go000066400000000000000000000054601451332102400232140ustar00rootroot00000000000000//go:build detectors // +build detectors package iexapis import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIexapis_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("IEXAPIS") inactiveSecret := testSecrets.MustGetField("IEXAPIS_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 iexapis secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Iexapis, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a iexapis 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_Iexapis, 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("Iexapis.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Iexapis.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) } } }) } } trufflehog-3.60.0/pkg/detectors/iexcloud/000077500000000000000000000000001451332102400203315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/iexcloud/iexcloud.go000066400000000000000000000041351451332102400224770ustar00rootroot00000000000000package iexcloud import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"iexcloud"}) + `\b([a-z0-9_]{35})\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{"iexcloud"} } // FromData will find and optionally verify Iexcloud 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Iexcloud, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://cloud.iexapis.com/v1/stock/aapl/quote?token=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Iexcloud } trufflehog-3.60.0/pkg/detectors/iexcloud/iexcloud_test.go000066400000000000000000000054721451332102400235430ustar00rootroot00000000000000//go:build detectors // +build detectors package iexcloud import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIexcloud_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("IEXCLOUD") inactiveSecret := testSecrets.MustGetField("IEXCLOUD_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 iexcloud secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Iexcloud, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a iexcloud 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_Iexcloud, 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("Iexcloud.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Iexcloud.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) } } }) } } trufflehog-3.60.0/pkg/detectors/imagekit/000077500000000000000000000000001451332102400203075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/imagekit/imagekit.go000066400000000000000000000041151451332102400224310ustar00rootroot00000000000000package imagekit import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"imagekit"}) + `\b([a-zA-Z0-9_=]{36})`) ) // 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{"imagekit"} } // FromData will find and optionally verify Imagekit 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Imagekit, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.imagekit.io/v1/files", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Imagekit } trufflehog-3.60.0/pkg/detectors/imagekit/imagekit_test.go000066400000000000000000000055001451332102400234670ustar00rootroot00000000000000//go:build detectors // +build detectors package imagekit import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestImagekit_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("IMAGEKIT_TOKEN") inactiveSecret := testSecrets.MustGetField("IMAGEKIT_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 imagekit secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Imagekit, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a imagekit 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_Imagekit, 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("Imagekit.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Imagekit.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) } } }) } } trufflehog-3.60.0/pkg/detectors/imagga/000077500000000000000000000000001451332102400177425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/imagga/imagga.go000066400000000000000000000042421451332102400215200ustar00rootroot00000000000000package imagga import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"imagga"}) + `\b([a-z0-9A-Z=]{72})`) ) // 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{"imagga"} } // FromData will find and optionally verify Imagga secrets in a given set of bytes.ddd func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) matches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Imagga, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.imagga.com/v2/usage", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Imagga } trufflehog-3.60.0/pkg/detectors/imagga/imagga_test.go000066400000000000000000000054461451332102400225660ustar00rootroot00000000000000//go:build detectors // +build detectors package imagga import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestImagga_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("IMAGGA") inactiveSecret := testSecrets.MustGetField("IMAGGA_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 imagga secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Imagga, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a imagga 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_Imagga, 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("Imagga.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Imagga.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) } } }) } } trufflehog-3.60.0/pkg/detectors/impala/000077500000000000000000000000001451332102400177605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/impala/impala.go000066400000000000000000000041221451332102400215510ustar00rootroot00000000000000package impala import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"impala"}) + `\b([0-9A-Za-z_]{46})\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{"impala"} } // FromData will find and optionally verify Impala 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Impala, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://sandbox.impala.travel/v1/bookings", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Impala } trufflehog-3.60.0/pkg/detectors/impala/impala_test.go000066400000000000000000000054461451332102400226220ustar00rootroot00000000000000//go:build detectors // +build detectors package impala import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestImpala_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("IMPALA") inactiveSecret := testSecrets.MustGetField("IMPALA_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 impala secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Impala, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a impala 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_Impala, 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("Impala.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Impala.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) } } }) } } trufflehog-3.60.0/pkg/detectors/infura/000077500000000000000000000000001451332102400200015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/infura/infura.go000066400000000000000000000044541451332102400216230ustar00rootroot00000000000000package infura import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"infura"}) + `\b([0-9a-z]{32})\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{"infura"} } // FromData will find and optionally verify Infura 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Infura, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://mainnet.infura.io/v3/"+resMatch, payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") 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, `"result"`) { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Infura } trufflehog-3.60.0/pkg/detectors/infura/infura_test.go000066400000000000000000000054461451332102400226640ustar00rootroot00000000000000//go:build detectors // +build detectors package infura import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestInfura_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("INFURA") inactiveSecret := testSecrets.MustGetField("INFURA_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 infura secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Infura, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a infura 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_Infura, 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("Infura.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Infura.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) } } }) } } trufflehog-3.60.0/pkg/detectors/insightly/000077500000000000000000000000001451332102400205275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/insightly/insightly.go000066400000000000000000000041421451332102400230710ustar00rootroot00000000000000package insightly import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"insightly"}) + `\b([a-z0-9-]{36})\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{"insightly"} } // FromData will find and optionally verify Insightly 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Insightly, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.na1.insightly.com/v3.1/Contacts?top=2", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Insightly } trufflehog-3.60.0/pkg/detectors/insightly/insightly_test.go000066400000000000000000000055041451332102400241330ustar00rootroot00000000000000//go:build detectors // +build detectors package insightly import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestInsightly_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("INSIGHTLY") inactiveSecret := testSecrets.MustGetField("INSIGHTLY_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 insightly secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Insightly, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a insightly 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_Insightly, 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("Insightly.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Insightly.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) } } }) } } trufflehog-3.60.0/pkg/detectors/instabot/000077500000000000000000000000001451332102400203405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/instabot/instabot.go000066400000000000000000000041471451332102400225200ustar00rootroot00000000000000package instabot import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"instabot"}) + `\b([0-9a-zA-Z=+\/]{43}[0-9a-zA-Z+\/=]{1})`) ) // 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{"instabot"} } // FromData will find and optionally verify Instabot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Instabot, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.instabot.io/v1", nil) if err != nil { continue } req.Header.Add("X-Instabot-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Instabot } trufflehog-3.60.0/pkg/detectors/instabot/instabot_test.go000066400000000000000000000054721451332102400235610ustar00rootroot00000000000000//go:build detectors // +build detectors package instabot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestInstabot_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("INSTABOT") inactiveSecret := testSecrets.MustGetField("INSTABOT_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 instabot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Instabot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a instabot 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_Instabot, 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("Instabot.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Instabot.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) } } }) } } trufflehog-3.60.0/pkg/detectors/integromat/000077500000000000000000000000001451332102400206665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/integromat/integromat.go000066400000000000000000000042061451332102400233700ustar00rootroot00000000000000package integromat import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"integromat"}) + `\b([a-z0-9-]{36})\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{"integromat"} } // FromData will find and optionally verify Integromat 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Integromat, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.integromat.com/v1/whoami", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Integromat } trufflehog-3.60.0/pkg/detectors/integromat/integromat_test.go000066400000000000000000000055241451332102400244330ustar00rootroot00000000000000//go:build detectors // +build detectors package integromat import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIntegromat_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("INTEGROMAT_TOKEN") inactiveSecret := testSecrets.MustGetField("INTEGROMAT_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 integromat secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Integromat, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a integromat 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_Integromat, 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("Integromat.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Integromat.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) } } }) } } trufflehog-3.60.0/pkg/detectors/intercom/000077500000000000000000000000001451332102400203355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/intercom/intercom.go000066400000000000000000000045171451332102400225130ustar00rootroot00000000000000package intercom import ( "context" "encoding/base64" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"intercom"}) + `\b([a-zA-Z0-9\W\S]{59}\=)`) ) // 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{"intercom"} } // FromData will find and optionally verify InterCom 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) dec, err := base64.StdEncoding.DecodeString(resMatch) if err != nil { continue } if !strings.HasPrefix(string(dec), "tok:") { continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Intercom, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.intercom.io/contacts?per_page=5", nil) if err != nil { continue } req.Header.Add("Authorization", "Bearer "+resMatch) req.Header.Add("Accept", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Intercom } trufflehog-3.60.0/pkg/detectors/intercom/intercom_test.go000066400000000000000000000055001451332102400235430ustar00rootroot00000000000000//go:build detectors // +build detectors package intercom import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestInterCom_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("INTERCOM_TOKEN") inactiveSecret := testSecrets.MustGetField("INTERCOM_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 intercom secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Intercom, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a intercom 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_Intercom, 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("InterCom.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("InterCom.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) } } }) } } trufflehog-3.60.0/pkg/detectors/interseller/000077500000000000000000000000001451332102400210455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/interseller/interseller.go000066400000000000000000000036671451332102400237400ustar00rootroot00000000000000package interseller import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"interseller"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"interseller"} } // FromData will find and optionally verify Interseller 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Interseller, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://interseller.io/api/campaigns/list", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("X-API-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Interseller } trufflehog-3.60.0/pkg/detectors/interseller/interseller_test.go000066400000000000000000000055301451332102400247660ustar00rootroot00000000000000//go:build detectors // +build detectors package interseller import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestInterseller_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("INTERSELLER") inactiveSecret := testSecrets.MustGetField("INTERSELLER_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 interseller secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Interseller, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a interseller 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_Interseller, 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("Interseller.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Interseller.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) } } }) } } trufflehog-3.60.0/pkg/detectors/intrinio/000077500000000000000000000000001451332102400203505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/intrinio/intrinio.go000066400000000000000000000041321451332102400225320ustar00rootroot00000000000000package intrinio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"intrinio"}) + `\b([a-zA-Z0-9]{44})\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{"intrinio"} } // FromData will find and optionally verify Intrinio 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Intrinio, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api-v2.intrinio.com/securities?api_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Intrinio } trufflehog-3.60.0/pkg/detectors/intrinio/intrinio_test.go000066400000000000000000000054721451332102400236010ustar00rootroot00000000000000//go:build detectors // +build detectors package intrinio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIntrinio_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("INTRINIO") inactiveSecret := testSecrets.MustGetField("INTRINIO_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 intrinio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Intrinio, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a intrinio 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_Intrinio, 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("Intrinio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Intrinio.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) } } }) } } trufflehog-3.60.0/pkg/detectors/invoiceocean/000077500000000000000000000000001451332102400211575ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/invoiceocean/invoiceocean.go000066400000000000000000000047231451332102400241560ustar00rootroot00000000000000package invoiceocean import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"invoiceocean"}) + `\b([0-9A-Za-z]{20})\b`) urlPat = regexp.MustCompile(`\b([0-9a-z]{1,}\.invoiceocean\.com)\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{"invoiceocean"} } // FromData will find and optionally verify Invoiceocean 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, urlMatch := range urlMatches { if len(urlMatch) != 2 { continue } resURL := strings.TrimSpace(urlMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_InvoiceOcean, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/invoices.json?period=this_month&api_token=%s", resURL, resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_InvoiceOcean } trufflehog-3.60.0/pkg/detectors/invoiceocean/invoiceocean_test.go000066400000000000000000000056471451332102400252230ustar00rootroot00000000000000//go:build detectors // +build detectors package invoiceocean import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestInvoiceocean_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("INVOICEOCEAN") url := testSecrets.MustGetField("INVOICEOCEAN_URL") inactiveSecret := testSecrets.MustGetField("INVOICEOCEAN_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 invoiceocean secret %s within %s", secret, url)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_InvoiceOcean, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a invoiceocean secret %s within %s but not valid", inactiveSecret, url)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_InvoiceOcean, 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("Invoiceocean.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Invoiceocean.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ipapi/000077500000000000000000000000001451332102400176175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ipapi/ipapi.go000066400000000000000000000047041451332102400212550ustar00rootroot00000000000000package ipapi import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipapi"}) + `\b([a-z0-9]{32})\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{"ipapi"} } // FromData will find and optionally verify Ipapi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Ipapi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.ipapi.com/49.146.239.251?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) valid := strings.Contains(bodyString, "continent_code") || strings.Contains(bodyString, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if valid { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ipapi } trufflehog-3.60.0/pkg/detectors/ipapi/ipapi_test.go000066400000000000000000000054341451332102400223150ustar00rootroot00000000000000//go:build detectors // +build detectors package ipapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIpapi_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("IPAPI") inactiveSecret := testSecrets.MustGetField("IPAPI_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 ipapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Ipapi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ipapi 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_Ipapi, 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("Ipapi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ipapi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ipgeolocation/000077500000000000000000000000001451332102400213515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ipgeolocation/ipgeolocation.go000066400000000000000000000041601451332102400245350ustar00rootroot00000000000000package ipgeolocation import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipgeolocation"}) + `\b([a-z0-9]{32})\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{"ipgeolocation"} } // FromData will find and optionally verify IPGeolocation 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_IPGeolocation, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.ipgeolocation.io/ipgeo?apiKey=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_IPGeolocation } trufflehog-3.60.0/pkg/detectors/ipgeolocation/ipgeolocation_test.go000066400000000000000000000055331451332102400256010ustar00rootroot00000000000000//go:build detectors // +build detectors package ipgeolocation import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIPGeolocation_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("IPGEOLOCATION") inactiveSecret := testSecrets.MustGetField("IPGEOLOCATION_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 ipgeolocation secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_IPGeolocation, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ipgeolocation 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_IPGeolocation, 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("IPGeolocation.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("IPGeolocation.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ipify/000077500000000000000000000000001451332102400176355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ipify/ipify.go000066400000000000000000000042341451332102400213070ustar00rootroot00000000000000package ipify import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipify"}) + `\b([a-zA-Z0-9_-]{32})\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{"ipify"} } // FromData will find and optionally verify Ipify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Ipify, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://geo.ipify.org/api/v2/country?apiKey=%s&ipAddress=8.8.8.8", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Ipify } trufflehog-3.60.0/pkg/detectors/ipify/ipify_test.go000066400000000000000000000054341451332102400223510ustar00rootroot00000000000000//go:build detectors // +build detectors package ipify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIpify_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("IPIFY") inactiveSecret := testSecrets.MustGetField("IPIFY_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 ipify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Ipify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ipify 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_Ipify, 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("Ipify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ipify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ipinfodb/000077500000000000000000000000001451332102400203075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ipinfodb/ipinfodb.go000066400000000000000000000045711451332102400224370ustar00rootroot00000000000000package ipinfodb import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipinfodb"}) + `\b([a-z0-9]{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{"ipinfodb"} } // FromData will find and optionally verify IPinfoDB 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_IPinfoDB, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.ipinfodb.com/v3/ip-country/?key=%s&ip=8.8.8.8&format=json", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"statusCode": "OK"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_IPinfoDB } trufflehog-3.60.0/pkg/detectors/ipinfodb/ipinfodb_test.go000066400000000000000000000054721451332102400234770ustar00rootroot00000000000000//go:build detectors // +build detectors package ipinfodb import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIPinfoDB_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("IPINFODB") inactiveSecret := testSecrets.MustGetField("IPINFODB_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 ipinfodb secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_IPinfoDB, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ipinfodb 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_IPinfoDB, 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("IPinfoDB.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("IPinfoDB.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ipquality/000077500000000000000000000000001451332102400205365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ipquality/ipquality.go000066400000000000000000000046161451332102400231150ustar00rootroot00000000000000package ipquality import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipquality"}) + `\b([0-9a-z]{32})\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{"ipquality"} } // FromData will find and optionally verify Ipquality 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_IPQuality, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://www.ipqualityscore.com/api/json/account/%s", resMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") 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) validResponse := strings.Contains(body, "insufficient credits") || strings.Contains(body, `"success":true`) if (res.StatusCode >= 200 && res.StatusCode < 300) && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_IPQuality } trufflehog-3.60.0/pkg/detectors/ipquality/ipquality_test.go000066400000000000000000000055041451332102400241510ustar00rootroot00000000000000//go:build detectors // +build detectors package ipquality import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIpquality_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("IPQUALITY") inactiveSecret := testSecrets.MustGetField("IPQUALITY_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 ipquality secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_IPQuality, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ipquality 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_IPQuality, 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("Ipquality.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ipquality.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ipstack/000077500000000000000000000000001451332102400201535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ipstack/ipstack.go000066400000000000000000000042761451332102400221510ustar00rootroot00000000000000package ipstack import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ipstack"}) + `\b([a-fA-F0-9]{32})\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{"ipstack"} } // FromData will find and optionally verify IpStack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_IpStack, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ipstack.com/134.201.250.155?access_key="+resMatch, nil) if err != nil { continue } 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) validResponse := strings.Contains(body, "continent_code") || strings.Contains(body, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) if validResponse { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_IpStack } trufflehog-3.60.0/pkg/detectors/ipstack/ipstack_test.go000066400000000000000000000054661451332102400232120ustar00rootroot00000000000000//go:build detectors // +build detectors package ipstack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestIpStack_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("IPSTACK_TOKEN") inactiveSecret := testSecrets.MustGetField("IPSTACK_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 IPSTACK secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_IpStack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a IPSTACK 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_IpStack, 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("IpStack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("IPSTACK.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) } } }) } } trufflehog-3.60.0/pkg/detectors/jdbc/000077500000000000000000000000001451332102400174175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/jdbc/jdbc.go000066400000000000000000000150631451332102400206550ustar00rootroot00000000000000package jdbc import ( "context" "database/sql" "errors" "fmt" "regexp" "strings" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct { ignorePatterns []regexp.Regexp } func New(opts ...func(*Scanner)) *Scanner { scanner := &Scanner{ ignorePatterns: []regexp.Regexp{}, } for _, opt := range opts { opt(scanner) } return scanner } func WithIgnorePattern(ignoreStrings []string) func(*Scanner) { return func(s *Scanner) { ignorePatterns := []regexp.Regexp{} for _, ignoreString := range ignoreStrings { ignorePattern, err := regexp.Compile(ignoreString) if err != nil { panic(fmt.Sprintf("%s is not a valid regex, error received: %v", ignoreString, err)) } ignorePatterns = append(ignorePatterns, *ignorePattern) } s.ignorePatterns = ignorePatterns } } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( keyPat = regexp.MustCompile(`(?i)jdbc:[\w]{3,10}:[^\s"']{0,512}`) ) // 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{"jdbc"} } // FromData will find and optionally verify Jdbc 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) matchLoop: for _, match := range matches { if len(s.ignorePatterns) != 0 { for _, ignore := range s.ignorePatterns { if ignore.MatchString(match[0]) { continue matchLoop } } } jdbcConn := match[0] s := detectors.Result{ DetectorType: detectorspb.DetectorType_JDBC, Raw: []byte(jdbcConn), Redacted: tryRedactAnonymousJDBC(jdbcConn), } if verify { s.Verified = false j, err := newJDBC(jdbcConn) if err != nil { continue } ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() pingRes := j.ping(ctx) s.Verified = pingRes.err == nil // If there's a ping error that is marked as "determinate" we throw it away. We do this because this was the // behavior before tri-state verification was introduced and preserving it allows us to gradually migrate // detectors to use tri-state verification. if pingRes.err != nil && !pingRes.determinate { s.VerificationError = pingRes.err } // TODO: specialized redaction } if !s.Verified && detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, false) { continue } results = append(results, s) } return } func tryRedactAnonymousJDBC(conn string) string { if s, ok := tryRedactURLParams(conn); ok { return s } if s, ok := tryRedactODBC(conn); ok { return s } if s, ok := tryRedactBasicAuth(conn); ok { return s } if s, ok := tryRedactRegex(conn); ok { return s } return conn } // Basic authentication "username:password@host" style func tryRedactBasicAuth(conn string) (string, bool) { userPass, postfix, found := strings.Cut(conn, "@") if !found { return "", false } index := strings.LastIndex(userPass, ":") if index == -1 { return "", false } prefix, pass := userPass[:index], userPass[index+1:] return prefix + ":" + strings.Repeat("*", len(pass)) + "@" + postfix, true } // URL param "?password=password" style func tryRedactURLParams(conn string) (string, bool) { prefix, paramString, found := strings.Cut(conn, "?") if !found { return "", false } var newParams []string found = false for _, param := range strings.Split(paramString, "&") { key, val, _ := strings.Cut(param, "=") if strings.Contains(strings.ToLower(key), "pass") { newParams = append(newParams, key+"="+strings.Repeat("*", len(val))) found = true continue } newParams = append(newParams, param) } if !found { return "", false } return prefix + "?" + strings.Join(newParams, "&"), true } // ODBC params ";password=password" style func tryRedactODBC(conn string) (string, bool) { var found bool var newParams []string for _, param := range strings.Split(conn, ";") { key, val, isKvp := strings.Cut(param, "=") if isKvp && strings.Contains(strings.ToLower(key), "pass") { newParams = append(newParams, key+"="+strings.Repeat("*", len(val))) found = true continue } newParams = append(newParams, param) } if !found { return "", false } return strings.Join(newParams, ";"), true } // Naively search the string for "pass=" func tryRedactRegex(conn string) (string, bool) { pattern := regexp.MustCompile(`(?i)pass.*?=(.+?)\b`) var found bool newConn := pattern.ReplaceAllStringFunc(conn, func(s string) string { index := strings.Index(s, "=") if index == -1 { // unreachable due to regex containing '=' return s } found = true return s[:index+1] + strings.Repeat("*", len(s[index+1:])) }) if !found { return "", false } return newConn, true } var supportedSubprotocols = map[string]func(string) (jdbc, error){ "sqlite": parseSqlite, "mysql": parseMySQL, "postgresql": parsePostgres, "sqlserver": parseSqlServer, } type pingResult struct { err error determinate bool } type jdbc interface { ping(context.Context) pingResult } func newJDBC(conn string) (jdbc, error) { // expected format: "jdbc:{subprotocol}:{subname}" if !strings.HasPrefix(strings.ToLower(conn), "jdbc:") { return nil, errors.New("expected jdbc prefix") } conn = conn[len("jdbc:"):] subprotocol, subname, found := strings.Cut(conn, ":") if !found { return nil, errors.New("expected a colon separated subprotocol and subname") } // get the subprotocol parser parser, ok := supportedSubprotocols[strings.ToLower(subprotocol)] if !ok { return nil, errors.New("unsupported subprotocol") } return parser(subname) } func ping(ctx context.Context, driverName string, isDeterminate func(error) bool, candidateConns ...string) pingResult { var indeterminateErrors []error for _, c := range candidateConns { err := pingErr(ctx, driverName, c) if err == nil || isDeterminate(err) { return pingResult{err, true} } indeterminateErrors = append(indeterminateErrors, err) } return pingResult{errors.Join(indeterminateErrors...), false} } func pingErr(ctx context.Context, driverName, conn string) error { db, err := sql.Open(driverName, conn) if err != nil { return err } defer db.Close() if err := db.PingContext(ctx); err != nil { return err } return nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_JDBC } trufflehog-3.60.0/pkg/detectors/jdbc/jdbc_integration_test.go000066400000000000000000000065661451332102400243270ustar00rootroot00000000000000//go:build detectors && integration // +build detectors,integration package jdbc import ( "context" "fmt" "os" "os/exec" "strings" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/stretchr/testify/assert" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMain(m *testing.M) { code, err := runMain(m) if err != nil { panic(err) } os.Exit(code) } func runMain(m *testing.M) (int, error) { for _, ctrl := range []struct { start func() error stop func() }{ {startPostgres, stopPostgres}, {startMySQL, stopMySQL}, {startSqlServer, stopSqlServer}, } { if err := ctrl.start(); err != nil { return 0, err } defer ctrl.stop() } return m.Run(), nil } func dockerLogLine(hash string, needle string) chan struct{} { ch := make(chan struct{}, 1) go func() { for { out, err := exec.Command("docker", "logs", hash).CombinedOutput() if err != nil { panic(err) } if strings.Contains(string(out), needle) { ch <- struct{}{} return } time.Sleep(1 * time.Second) } }() return ch } func TestJdbcVerified(t *testing.T) { type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string args args want []detectors.Result wantErr bool }{ { name: "postgres verified", args: args{ ctx: context.Background(), data: []byte(`jdbc connection string: jdbc:postgresql://localhost:5432/foo?sslmode=disable&password=` + postgresPass), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: true, Redacted: "jdbc:postgresql://localhost:5432/foo?sslmode=disable&password=" + strings.Repeat("*", len(postgresPass)), }, }, wantErr: false, }, { name: "mysql verified", args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf(`CONN="jdbc:mysql://%s:%s@tcp(127.0.0.1:3306)/%s"`, mysqlUser, mysqlPass, mysqlDatabase)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: true, Redacted: fmt.Sprintf(`jdbc:mysql://%s:%s@tcp(127.0.0.1:3306)/%s`, mysqlUser, strings.Repeat("*", len(mysqlPass)), mysqlDatabase), }, }, wantErr: false, }, { name: "sql server verified", args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("jdbc:sqlserver://odbc:server=localhost;database=%s;password=%s", sqlServerDatabase, sqlServerPass)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: true, Redacted: "jdbc:sqlserver://odbc:server=localhost;database=master;password=**************", }, }, 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 tt.wantErr { assert.Error(t, err) return } assert.NoError(t, err) if os.Getenv("FORCE_PASS_DIFF") == "true" { return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Jdbc.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } trufflehog-3.60.0/pkg/detectors/jdbc/jdbc_test.go000066400000000000000000000157311451332102400217160ustar00rootroot00000000000000//go:build detectors // +build detectors package jdbc import ( "context" "github.com/stretchr/testify/assert" "os" "testing" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestJdbc_FromChunk(t *testing.T) { type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string args args want []detectors.Result wantErr bool }{ { name: "found, unverified", args: args{ ctx: context.Background(), data: []byte(`jdbc connection string: jdbc:mysql://hello.test.us-east-1.rds.amazonaws.com:3306/testdb?password=testpassword <-`), verify: false, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: false, Redacted: "jdbc:mysql://hello.test.us-east-1.rds.amazonaws.com:3306/testdb?password=************", }, }, wantErr: false, }, { name: "found, unverified numeric password", args: args{ ctx: context.Background(), data: []byte(`jdbc connection string: jdbc:postgresql://host:5342/testdb?password=123456 <-`), verify: false, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: false, Redacted: "jdbc:postgresql://host:5342/testdb?password=******", }, }, wantErr: false, }, { name: "not found", args: args{ ctx: context.Background(), data: []byte("You cannot find the secret within"), verify: false, }, want: nil, wantErr: false, }, { name: "sqlite unverified", args: args{ ctx: context.Background(), data: []byte("jdbc:sqlite::memory:"), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: false, Redacted: "jdbc:sqlite::memory:", }, }, wantErr: false, }, { name: "found double quoted string, unverified", args: args{ ctx: context.Background(), data: []byte(`CONN="jdbc:postgres://hello.test.us-east-1.rds.amazonaws.com:3306/testdb?password=testpassword"`), verify: false, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: false, Redacted: "jdbc:postgres://hello.test.us-east-1.rds.amazonaws.com:3306/testdb?password=************", }, }, wantErr: false, }, { name: "found single quoted string, unverified", args: args{ ctx: context.Background(), data: []byte(`CONN='jdbc:postgres://hello.test.us-east-1.rds.amazonaws.com:3306/testdb?password=testpassword'`), verify: false, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: false, Redacted: "jdbc:postgres://hello.test.us-east-1.rds.amazonaws.com:3306/testdb?password=************", }, }, wantErr: false, }, { name: "sqlserver, unverified", args: args{ ctx: context.Background(), data: []byte(`jdbc:sqlserver://a.b.c.net;database=database-name;spring.datasource.password=super-secret-password`), verify: false, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JDBC, Verified: false, Redacted: "jdbc:sqlserver://a.b.c.net;database=database-name;spring.datasource.password=*********************", }, }, 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("Jdbc.FromData() error = %v, wantErr %v", err, tt.wantErr) return } if os.Getenv("FORCE_PASS_DIFF") == "true" { return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Jdbc.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } func TestJdbc_FromDataWithIgnorePattern(t *testing.T) { type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string args args want []detectors.Result ignorePatterns []string wantErr bool }{ { name: "not found", args: args{ ctx: context.Background(), data: []byte("jdbc:sqlite::secretpattern:"), verify: false, }, want: nil, ignorePatterns: []string{ ".*secretpattern.*", }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := New(WithIgnorePattern(tt.ignorePatterns)) got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("Jdbc.FromDataWithConfig() error = %v, wantErr %v", err, tt.wantErr) return } if os.Getenv("FORCE_PASS_DIFF") == "true" { return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Jdbc.FromDataWithConfig() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } func TestJdbc_Redact(t *testing.T) { tests := []struct { name string conn string want string }{ { name: "basic auth'", conn: "//user:secret@tcp(127.0.0.1:3306)/", want: "//user:******@tcp(127.0.0.1:3306)/", }, { name: "basic auth including raw string 'pass'", conn: "//wrongUser:wrongPass@tcp(127.0.0.1:3306)/", want: "//wrongUser:*********@tcp(127.0.0.1:3306)/", }, { name: "basic auth including raw string 'pass' with unfortunate db name", conn: "//wrongUser:wrongPass@tcp(127.0.0.1:3306)/passwords", want: "//wrongUser:*********@tcp(127.0.0.1:3306)/passwords", }, { name: "url param-style", conn: "jdbc:postgresql://localhost:5432/foo?sslmode=disable&password=p@ssw04d", want: "jdbc:postgresql://localhost:5432/foo?sslmode=disable&password=********", }, { name: "odbc-style without server", conn: "//odbc:server=localhost;user id=sa;database=master;password=/p?s=sw&rd", want: "//odbc:server=localhost;user id=sa;database=master;password=**********", }, { name: "odbc-style with server", conn: "jdbc:sqlserver://a.b.c.net;database=database-name;spring.datasource.password=super-secret-password", want: "jdbc:sqlserver://a.b.c.net;database=database-name;spring.datasource.password=*********************", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tryRedactAnonymousJDBC(tt.conn) assert.Equal(t, tt.want, got) }) } } 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) } } }) } } trufflehog-3.60.0/pkg/detectors/jdbc/mysql.go000066400000000000000000000036011451332102400211130ustar00rootroot00000000000000package jdbc import ( "context" "errors" "github.com/go-sql-driver/mysql" "strings" ) type mysqlJDBC struct { conn string userPass string host string database string params string } func (s *mysqlJDBC) ping(ctx context.Context) pingResult { return ping(ctx, "mysql", isMySQLErrorDeterminate, s.conn, buildMySQLConnectionString(s.host, s.database, s.userPass, s.params), buildMySQLConnectionString(s.host, "", s.userPass, s.params)) } func buildMySQLConnectionString(host, database, userPass, params string) string { conn := host + "/" + database if userPass != "" { conn = userPass + "@" + conn } if params != "" { conn = conn + "?" + params } return conn } func isMySQLErrorDeterminate(err error) bool { // MySQL error numbers from https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html if mySQLErr, isMySQLErr := err.(*mysql.MySQLError); isMySQLErr { switch mySQLErr.Number { case 1044: // User access denied to a particular database return false // "Indeterminate" so that other connection variations will be tried case 1045: // User access denied return true } } return false } func parseMySQL(subname string) (jdbc, error) { // expected form: [subprotocol:]//[user:password@]HOST[/DB][?key=val[&key=val]] hostAndDB, params, _ := strings.Cut(subname, "?") if !strings.HasPrefix(hostAndDB, "//") { return nil, errors.New("expected host to start with //") } userPassAndHostAndDB := strings.TrimPrefix(hostAndDB, "//") userPass, hostAndDB, found := strings.Cut(userPassAndHostAndDB, "@") if !found { hostAndDB = userPass userPass = "" } host, database, found := strings.Cut(hostAndDB, "/") if !found { return nil, errors.New("expected host and database to be separated by /") } return &mysqlJDBC{ conn: subname[2:], userPass: userPass, host: host, database: database, params: params, }, nil } trufflehog-3.60.0/pkg/detectors/jdbc/mysql_integration_test.go000066400000000000000000000045351451332102400245640ustar00rootroot00000000000000//go:build detectors && integration // +build detectors,integration package jdbc import ( "bytes" "context" "errors" "os/exec" "testing" "time" "github.com/stretchr/testify/assert" ) const ( mysqlUser = "coolGuy" mysqlPass = "23201dabb56ca236f3dc6736c0f9afad" mysqlDatabase = "stuff" ) func TestMySQL(t *testing.T) { type result struct { parseErr bool pingOk bool pingDeterminate bool } tests := []struct { input string want result }{ { input: "", want: result{parseErr: true}, }, { input: "//" + mysqlUser + ":" + mysqlPass + "@tcp(127.0.0.1:3306)/" + mysqlDatabase, want: result{pingOk: true, pingDeterminate: true}, }, { input: "//wrongUser:wrongPass@tcp(127.0.0.1:3306)/" + mysqlDatabase, want: result{pingOk: false, pingDeterminate: true}, }, { input: "//" + mysqlUser + ":wrongPass@tcp(127.0.0.1:3306)/" + mysqlDatabase, want: result{pingOk: false, pingDeterminate: true}, }, { input: "//" + mysqlUser + ":" + mysqlPass + "@tcp(127.0.0.1:3306)/", want: result{pingOk: true, pingDeterminate: true}, }, { input: "//" + mysqlUser + ":" + mysqlPass + "@tcp(127.0.0.1:3306)/wrongDB", want: result{pingOk: true, pingDeterminate: true}, }, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { j, err := parseMySQL(tt.input) if err != nil { got := result{parseErr: true} assert.Equal(t, tt.want, got) return } pr := j.ping(context.Background()) got := result{pingOk: pr.err == nil, pingDeterminate: pr.determinate} assert.Equal(t, tt.want, got) }) } } var mysqlDockerHash string func startMySQL() error { cmd := exec.Command( "docker", "run", "--rm", "-p", "3306:3306", "-e", "MYSQL_ROOT_PASSWORD=403a96cff2a323f74bfb1c16992895be", "-e", "MYSQL_USER="+mysqlUser, "-e", "MYSQL_PASSWORD="+mysqlPass, "-e", "MYSQL_DATABASE="+mysqlDatabase, "-e", "MYSQL_ROOT_HOST=%", "-d", "mysql", ) out, err := cmd.Output() if err != nil { return err } mysqlDockerHash = string(bytes.TrimSpace(out)) select { case <-dockerLogLine(mysqlDockerHash, "socket: '/var/run/mysqld/mysqld.sock' port: 3306"): return nil case <-time.After(30 * time.Second): stopMySQL() return errors.New("timeout waiting for mysql database to be ready") } } func stopMySQL() { exec.Command("docker", "kill", mysqlDockerHash).Run() } trufflehog-3.60.0/pkg/detectors/jdbc/postgres.go000066400000000000000000000052711451332102400216210ustar00rootroot00000000000000package jdbc import ( "context" "errors" "fmt" "github.com/lib/pq" "strings" ) type postgresJDBC struct { conn string params map[string]string } func (s *postgresJDBC) ping(ctx context.Context) pingResult { // It is crucial that we try to build a connection string ourselves before using the one we found. This is because // if the found connection string doesn't include a username, the driver will attempt to connect using the current // user's name, which will fail in a way that looks like a determinate failure, thus terminating the waterfall. In // contrast, when we build a connection string ourselves, if there's no username, we try 'postgres' instead, which // actually has a chance of working. return ping(ctx, "postgres", isPostgresErrorDeterminate, buildPostgresConnectionString(s.params, true), buildPostgresConnectionString(s.params, false), s.conn, "postgres://"+s.conn) } func isPostgresErrorDeterminate(err error) bool { // Postgres codes from https://www.postgresql.org/docs/current/errcodes-appendix.html if pqErr, isPostgresError := err.(*pq.Error); isPostgresError { switch pqErr.Code { case "28P01": // Invalid username/password return true case "3D000": // Unknown database return false // "Indeterminate" so that other connection variations will be tried case "3F000": // Unknown schema return false // "Indeterminate" so that other connection variations will be tried } } return false } func joinKeyValues(m map[string]string, sep string) string { var data []string for k, v := range m { if v == "" { continue } data = append(data, fmt.Sprintf("%s=%s", k, v)) } return strings.Join(data, sep) } func parsePostgres(subname string) (jdbc, error) { // expected form: //HOST/DB?key=value&key=value hostAndDB, paramString, _ := strings.Cut(subname, "?") if !strings.HasPrefix(hostAndDB, "//") { return nil, errors.New("expected host to start with //") } hostAndDB = strings.TrimPrefix(hostAndDB, "//") host, database, _ := strings.Cut(hostAndDB, "/") params := map[string]string{ "host": host, "dbname": database, } for _, param := range strings.Split(paramString, "&") { key, val, _ := strings.Cut(param, "=") params[key] = val } return &postgresJDBC{subname[2:], params}, nil } func buildPostgresConnectionString(params map[string]string, includeDbName bool) string { data := map[string]string{ // default user "user": "postgres", } for key, val := range params { if key == "host" { if h, p, found := strings.Cut(val, ":"); found { data["host"] = h data["port"] = p continue } } if key == "dbname" && !includeDbName { continue } data[key] = val } return joinKeyValues(data, " ") } trufflehog-3.60.0/pkg/detectors/jdbc/postgres_integration_test.go000066400000000000000000000050221451332102400252550ustar00rootroot00000000000000//go:build detectors && integration // +build detectors,integration package jdbc import ( "bytes" "context" "errors" "os/exec" "testing" "time" "github.com/stretchr/testify/assert" ) const ( postgresUser = "postgres" postgresPass = "23201dabb56ca236f3dc6736c0f9afad" ) func TestPostgres(t *testing.T) { type result struct { parseErr bool pingOk bool pingDeterminate bool } tests := []struct { input string want result }{ { input: "//localhost:5432/foo?sslmode=disable&password=" + postgresPass, want: result{pingOk: true, pingDeterminate: true}, }, { input: "//localhost:5432/foo?sslmode=disable&user=" + postgresUser + "&password=" + postgresPass, want: result{pingOk: true, pingDeterminate: true}, }, { input: "//localhost/foo?sslmode=disable&port=5432&password=" + postgresPass, want: result{pingOk: true, pingDeterminate: true}, }, { input: "//localhost:5432/foo?password=" + postgresPass, want: result{pingOk: false, pingDeterminate: false}, }, { input: "//localhost:5432/foo?sslmode=disable&password=foo", want: result{pingOk: false, pingDeterminate: true}, }, { input: "//localhost:5432/foo?sslmode=disable&user=foo&password=" + postgresPass, want: result{pingOk: false, pingDeterminate: true}, }, { input: "//badhost:5432/foo?sslmode=disable&user=foo&password=" + postgresPass, want: result{pingOk: false, pingDeterminate: false}, }, { input: "invalid", want: result{parseErr: true}, }, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { j, err := parsePostgres(tt.input) if err != nil { got := result{parseErr: true} assert.Equal(t, tt.want, got) return } pr := j.ping(context.Background()) got := result{pingOk: pr.err == nil, pingDeterminate: pr.determinate} assert.Equal(t, tt.want, got) }) } } var postgresDockerHash string func startPostgres() error { cmd := exec.Command( "docker", "run", "--rm", "-p", "5432:5432", "-e", "POSTGRES_PASSWORD="+postgresPass, "-e", "POSTGRES_USER="+postgresUser, "-d", "postgres", ) out, err := cmd.Output() if err != nil { return err } postgresDockerHash = string(bytes.TrimSpace(out)) select { case <-dockerLogLine(postgresDockerHash, "PostgreSQL init process complete; ready for start up."): return nil case <-time.After(30 * time.Second): stopPostgres() return errors.New("timeout waiting for postgres database to be ready") } } func stopPostgres() { exec.Command("docker", "kill", postgresDockerHash).Run() } trufflehog-3.60.0/pkg/detectors/jdbc/sqlite.go000066400000000000000000000017321451332102400212520ustar00rootroot00000000000000package jdbc import ( "context" "errors" "strings" _ "github.com/mattn/go-sqlite3" ) type sqliteJDBC struct { filename string params map[string]string testing bool } var cannotVerifySqliteError error = errors.New("sqlite credentials cannot be verified") func (s *sqliteJDBC) ping(ctx context.Context) pingResult { if !s.testing { // sqlite is not a networked database, so we cannot verify return pingResult{cannotVerifySqliteError, true} } return ping(ctx, "sqlite3", isSqliteErrorDeterminate, s.filename) } func isSqliteErrorDeterminate(err error) bool { return true } func parseSqlite(subname string) (jdbc, error) { filename, params, _ := strings.Cut(subname, "?") if filename == "" { return nil, errors.New("empty filename") } j := &sqliteJDBC{filename: filename, params: map[string]string{}} for _, keyVal := range strings.Split(params, "&") { if key, val, found := strings.Cut(keyVal, "="); found { j.params[key] = val } } return j, nil } trufflehog-3.60.0/pkg/detectors/jdbc/sqlite_test.go000066400000000000000000000014201451332102400223030ustar00rootroot00000000000000//go:build detectors // +build detectors package jdbc import ( "context" "testing" "github.com/stretchr/testify/assert" ) func parseSqliteTest(subname string) (jdbc, error) { j, err := parseSqlite(subname) if err != nil { return j, err } j.(*sqliteJDBC).testing = true return j, err } func TestParseSqlite(t *testing.T) { type testStruct struct { input string wantErr bool } tests := []struct { input string wantErr bool }{ {input: "", wantErr: true}, {input: ":memory:"}, } for _, test := range tests { t.Run(test.input, func(t *testing.T) { j, err := parseSqliteTest(test.input) if test.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.True(t, j.ping(context.Background()).err == nil) } }) } } trufflehog-3.60.0/pkg/detectors/jdbc/sqlserver.go000066400000000000000000000027341451332102400220020ustar00rootroot00000000000000package jdbc import ( "context" "errors" mssql "github.com/denisenkom/go-mssqldb" "strings" ) type sqlServerJDBC struct { conn string params map[string]string } func (s *sqlServerJDBC) ping(ctx context.Context) pingResult { return ping(ctx, "mssql", isSqlServerErrorDeterminate, joinKeyValues(s.params, ";"), s.conn, "sqlserver://"+s.conn) } func isSqlServerErrorDeterminate(err error) bool { // Error numbers from https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver16 if mssqlError, isMssqlError := err.(mssql.Error); isMssqlError { switch mssqlError.Number { case 18456: // Login failed // This is a determinate failure iff we tried to use a real user return mssqlError.Message != "login error: Login failed for user ''." } } return false } func parseSqlServer(subname string) (jdbc, error) { if !strings.HasPrefix(subname, "//") { return nil, errors.New("expected connection to start with //") } conn := strings.TrimPrefix(subname, "//") params := map[string]string{ "user id": "sa", "database": "master", } for _, param := range strings.Split(conn, ";") { key, value, found := strings.Cut(param, "=") if !found { continue } params[key] = value if key != "password" && strings.Contains(strings.ToLower(key), "password") { params["password"] = value } } return &sqlServerJDBC{ conn: strings.TrimPrefix(subname, "//"), params: params, }, nil } trufflehog-3.60.0/pkg/detectors/jdbc/sqlserver_integration_test.go000066400000000000000000000041771451332102400254470ustar00rootroot00000000000000//go:build detectors && integration // +build detectors,integration package jdbc import ( "bytes" "context" "errors" "os/exec" "testing" "time" "github.com/stretchr/testify/assert" ) const ( sqlServerPass = "Secr3tP@s5w0rd" sqlServerUser = "sa" sqlServerDatabase = "master" ) func TestSqlServer(t *testing.T) { type result struct { parseErr bool pingOk bool pingDeterminate bool } tests := []struct { input string want result }{ { input: "", want: result{parseErr: true}, }, { input: "//server=localhost;user id=sa;database=master;password=" + sqlServerPass, want: result{pingOk: true, pingDeterminate: true}, }, { input: "//server=badhost;user id=sa;database=master;password=" + sqlServerPass, want: result{pingOk: false, pingDeterminate: false}, }, { input: "//localhost;database=master;spring.datasource.password=" + sqlServerPass, want: result{pingOk: true, pingDeterminate: true}, }, { input: "//localhost;database=master;spring.datasource.password=badpassword", want: result{pingOk: false, pingDeterminate: true}, }, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { j, err := parseSqlServer(tt.input) if err != nil { got := result{parseErr: true} assert.Equal(t, tt.want, got) return } pr := j.ping(context.Background()) got := result{pingOk: pr.err == nil, pingDeterminate: pr.determinate} assert.Equal(t, tt.want, got) }) } } var sqlServerDockerHash string func startSqlServer() error { cmd := exec.Command( "docker", "run", "--rm", "-p", "1433:1433", "-e", "ACCEPT_EULA=1", "-e", "MSSQL_SA_PASSWORD="+sqlServerPass, "-d", "mcr.microsoft.com/azure-sql-edge", ) out, err := cmd.Output() if err != nil { return err } sqlServerDockerHash = string(bytes.TrimSpace(out)) select { case <-dockerLogLine(sqlServerDockerHash, "EdgeTelemetry starting up"): return nil case <-time.After(30 * time.Second): stopSqlServer() return errors.New("timeout waiting for sql server database to be ready") } } func stopSqlServer() { exec.Command("docker", "kill", sqlServerDockerHash).Run() } trufflehog-3.60.0/pkg/detectors/jiratoken/000077500000000000000000000000001451332102400205035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/jiratoken/jiratoken.go000066400000000000000000000100441451332102400230170ustar00rootroot00000000000000package jiratoken import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (Scanner) Version() int { return 1 } var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jira"}) + `\b([a-zA-Z-0-9]{24})\b`) domainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jira"}) + `\b([a-zA-Z-0-9]{5,24}\.[a-zA-Z-0-9]{3,16}\.[a-zA-Z-0-9]{3,16})\b`) emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jira"}) + `\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`) ) const ( failedAuth = "AUTHENTICATED_FAILED" loginReasonHeaderKey = "X-Seraph-LoginReason" ) // 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{"jira"} } // FromData will find and optionally verify JiraToken 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) tokens := tokenPat.FindAllStringSubmatch(dataStr, -1) domains := domainPat.FindAllStringSubmatch(dataStr, -1) emails := emailPat.FindAllStringSubmatch(dataStr, -1) for _, email := range emails { email = strings.Split(email[0], " ") if len(email) != 2 { continue } resEmail := strings.TrimSpace(email[1]) for _, token := range tokens { if len(token) != 2 { continue } resToken := strings.TrimSpace(token[1]) for _, domain := range domains { if len(domain) != 2 { continue } resDomain := strings.TrimSpace(domain[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_JiraToken, Raw: []byte(resToken), RawV2: []byte(fmt.Sprintf("%s:%s:%s", resEmail, resToken, resDomain)), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/atlassian/", } if verify { client := s.getClient() isVerified, verificationErr := verifyJiratoken(ctx, client, resEmail, resDomain, resToken) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } } } return results, nil } func (s Scanner) getClient() *http.Client { if s.client != nil { return s.client } return defaultClient } func verifyJiratoken(ctx context.Context, client *http.Client, email, domain, token string) (bool, error) { data := fmt.Sprintf("%s:%s", email, token) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://"+domain+"/rest/api/3/dashboard", nil) if err != nil { return false, err } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() // If the request is successful and the login reason is not failed authentication, then the token is valid. // This is because Jira returns a 200 status code even if the token is invalid. // Jira returns a default dashboard page. if !(res.StatusCode >= 200 && res.StatusCode < 300) { return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } if res.Header.Get(loginReasonHeaderKey) != failedAuth { return true, nil } return false, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_JiraToken } trufflehog-3.60.0/pkg/detectors/jiratoken/jiratoken_test.go000066400000000000000000000107611451332102400240640ustar00rootroot00000000000000//go:build detectors // +build detectors package jiratoken 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 TestJiraToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("JIRA_TOKEN") inactiveSecret := testSecrets.MustGetField("JIRA_INACTIVE") email := testSecrets.MustGetField("JIRA_EMAIL") domain := testSecrets.MustGetField("JIRA_DOMAIN") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s with jira %s", secret, email, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s but not jira %s valid", inactiveSecret, email, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s with jira %s", secret, email, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s with jira %s", secret, email, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("JiraToken.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("JiraToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/jiratoken_v2/000077500000000000000000000000001451332102400211125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/jiratoken_v2/jiratoken_v2.go000066400000000000000000000077671451332102400240570ustar00rootroot00000000000000package jiratoken_v2 import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (Scanner) Version() int { return 2 } var ( defaultClient = common.SaneHttpClient() // https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/ // Tokens created after Jan 18 2023 use a variable length tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jira"}) + `\b([A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8})\b`) domainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jira"}) + `\b([a-zA-Z-0-9]{5,24}\.[a-zA-Z-0-9]{3,16}\.[a-zA-Z-0-9]{3,16})\b`) emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jira"}) + `\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`) ) const ( failedAuth = "AUTHENTICATED_FAILED" loginReasonHeaderKey = "X-Seraph-LoginReason" ) // 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{"jira"} } // FromData will find and optionally verify JiraToken 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) tokens := tokenPat.FindAllStringSubmatch(dataStr, -1) domains := domainPat.FindAllStringSubmatch(dataStr, -1) emails := emailPat.FindAllStringSubmatch(dataStr, -1) for _, email := range emails { email = strings.Split(email[0], " ") if len(email) != 2 { continue } resEmail := strings.TrimSpace(email[1]) for _, token := range tokens { if len(token) != 2 { continue } resToken := strings.TrimSpace(token[1]) for _, domain := range domains { if len(domain) != 2 { continue } resDomain := strings.TrimSpace(domain[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_JiraToken, Raw: []byte(resToken), RawV2: []byte(fmt.Sprintf("%s:%s:%s", resEmail, resToken, resDomain)), } if verify { client := s.getClient() isVerified, verificationErr := verifyJiratoken(ctx, client, resEmail, resDomain, resToken) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } } } return results, nil } func (s Scanner) getClient() *http.Client { if s.client != nil { return s.client } return defaultClient } func verifyJiratoken(ctx context.Context, client *http.Client, email, domain, token string) (bool, error) { data := fmt.Sprintf("%s:%s", email, token) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://"+domain+"/rest/api/3/dashboard", nil) if err != nil { return false, err } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() // If the request is successful and the login reason is not failed authentication, then the token is valid. // This is because Jira returns a 200 status code even if the token is invalid. // Jira returns a default dashboard page. if !(res.StatusCode >= 200 && res.StatusCode < 300) { return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } if res.Header.Get(loginReasonHeaderKey) != failedAuth { return true, nil } return false, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_JiraToken } trufflehog-3.60.0/pkg/detectors/jiratoken_v2/jiratoken_v2_test.go000066400000000000000000000110001451332102400250650ustar00rootroot00000000000000//go:build detectors // +build detectors package jiratoken_v2 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 TestJiraToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("JIRA_V2_TOKEN") inactiveSecret := testSecrets.MustGetField("JIRA_V2_INACTIVE") email := testSecrets.MustGetField("JIRA_V2_EMAIL") domain := testSecrets.MustGetField("JIRA_V2_DOMAIN") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s with jira %s", secret, email, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s but not jira %s valid", inactiveSecret, email, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s with jira %s", secret, email, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jira secret %s within jira %s with jira %s", secret, email, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_JiraToken, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("JiraToken.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("JiraToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/jotform/000077500000000000000000000000001451332102400201755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/jotform/jotform.go000066400000000000000000000040611451332102400222050ustar00rootroot00000000000000package jotform import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jotform"}) + `\b([0-9Aa-z]{32})\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{"jotform"} } // FromData will find and optionally verify Jotform 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Jotform, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.jotform.com/user?apiKey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Jotform } trufflehog-3.60.0/pkg/detectors/jotform/jotform_test.go000066400000000000000000000054601451332102400232500ustar00rootroot00000000000000//go:build detectors // +build detectors package jotform import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestJotform_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("JOTFORM") inactiveSecret := testSecrets.MustGetField("JOTFORM_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 jotform secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Jotform, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jotform 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_Jotform, 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("Jotform.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Jotform.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) } } }) } } trufflehog-3.60.0/pkg/detectors/jumpcloud/000077500000000000000000000000001451332102400205175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/jumpcloud/jumpcloud.go000066400000000000000000000041531451332102400230530ustar00rootroot00000000000000package jumpcloud import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"jumpcloud"}) + `\b([a-zA-Z0-9]{40})\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{"jumpcloud"} } // FromData will find and optionally verify Jumpcloud 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Jumpcloud, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://console.jumpcloud.com/api/v2/systemgroups", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Jumpcloud } trufflehog-3.60.0/pkg/detectors/jumpcloud/jumpcloud_test.go000066400000000000000000000055121451332102400241120ustar00rootroot00000000000000//go:build detectors // +build detectors package jumpcloud import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestJumpcloud_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("JUMPCLOUD_TOKEN") inactiveSecret := testSecrets.MustGetField("JUMPCLOUD_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 jumpcloud secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Jumpcloud, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a jumpcloud 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_Jumpcloud, 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("Jumpcloud.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Jumpcloud.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) } } }) } } trufflehog-3.60.0/pkg/detectors/juro/000077500000000000000000000000001451332102400174745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/juro/juro.go000066400000000000000000000040751451332102400210100ustar00rootroot00000000000000package juro import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"juro"}) + `\b([a-zA-Z0-9]{40})\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{"juro"} } // FromData will find and optionally verify Juro 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Juro, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.juro.com/v3/templates", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Juro } trufflehog-3.60.0/pkg/detectors/juro/juro_test.go000066400000000000000000000054221451332102400220440ustar00rootroot00000000000000//go:build detectors // +build detectors package juro import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestJuro_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("JURO") inactiveSecret := testSecrets.MustGetField("JURO_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 juro secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Juro, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a juro 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_Juro, 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("Juro.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Juro.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) } } }) } } trufflehog-3.60.0/pkg/detectors/kanban/000077500000000000000000000000001451332102400177475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kanban/kanban.go000066400000000000000000000047441451332102400215410ustar00rootroot00000000000000package kanban import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kanban"}) + `\b([0-9A-Z]{12})\b`) urlPat = regexp.MustCompile(`\b([0-9a-z]{1,}\.kanbantool\.com)\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{"kanban"} } // FromData will find and optionally verify Kanban 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, urlMatch := range urlMatches { if len(urlMatch) != 2 { continue } resURL := strings.TrimSpace(urlMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Kanban, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/api/v3/users/current.json", resURL), nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.kanban+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Kanban } trufflehog-3.60.0/pkg/detectors/kanban/kanban_test.go000066400000000000000000000055451451332102400226000ustar00rootroot00000000000000//go:build detectors // +build detectors package kanban import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKanban_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KANBAN") url := testSecrets.MustGetField("KANBAN_URL") inactiveSecret := testSecrets.MustGetField("KANBAN_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 kanban secret %s within %s", secret, url)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kanban, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kanban secret %s within %s but not valid", inactiveSecret, url)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kanban, 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("Kanban.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Kanban.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) } } }) } } trufflehog-3.60.0/pkg/detectors/kanbantool/000077500000000000000000000000001451332102400206455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kanbantool/kanbantool.go000066400000000000000000000050001451332102400233170ustar00rootroot00000000000000package kanbantool import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kanbantool"}) + `\b([0-9A-Z]{12})\b`) domainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kanbantool"}) + `\b([a-z0-9A-Z]{2,22})\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{"kanbantool"} } // FromData will find and optionally verify Kanbantool 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) idMatches := domainPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Kanbantool, Raw: []byte(resMatch), } if verify { url := fmt.Sprintf("https://%s.kanbantool.com/api/v3/users/current.json", resIdMatch) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Kanbantool } trufflehog-3.60.0/pkg/detectors/kanbantool/kanbantool_test.go000066400000000000000000000056241451332102400243720ustar00rootroot00000000000000package kanbantool import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKanbantool_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KANBANTOOL") inactiveSecret := testSecrets.MustGetField("KANBANTOOL_INACTIVE") domain := testSecrets.MustGetField("KANBANTOOL_DOMAIN") 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 kanbantool secret %s within kanbantool %s", secret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kanbantool, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kanbantool secret %s within but not valid kanbantool %s", inactiveSecret, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kanbantool, 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("Kanbantool.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]) } got[i].Raw = nil if diff := pretty.Compare(got[i], tt.want[0]); diff != "" { t.Errorf("Kanbantool.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) } } }) } } trufflehog-3.60.0/pkg/detectors/karmacrm/000077500000000000000000000000001451332102400203125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/karmacrm/karmacrm.go000066400000000000000000000043661451332102400224470ustar00rootroot00000000000000package karmacrm import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"karma"}) + `\b([a-zA-Z0-9]{20})\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{"karmacrm"} } // FromData will find and optionally verify KarmaCRM 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_KarmaCRM, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://app.karmacrm.com/api/v3/contacts.json?api_token=%s&page=1", resMatch), nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.karmacrm+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_KarmaCRM } trufflehog-3.60.0/pkg/detectors/karmacrm/karmacrm_test.go000066400000000000000000000054721451332102400235050ustar00rootroot00000000000000//go:build detectors // +build detectors package karmacrm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKarmaCRM_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("KARMACRM") inactiveSecret := testSecrets.MustGetField("KARMACRM_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 karmacrm secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_KarmaCRM, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a karmacrm 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_KarmaCRM, 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("KarmaCRM.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("KarmaCRM.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) } } }) } } trufflehog-3.60.0/pkg/detectors/keenio/000077500000000000000000000000001451332102400177675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/keenio/keenio.go000066400000000000000000000047141451332102400215760ustar00rootroot00000000000000package keenio import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"keen"}) + `\b([0-9A-Z]{64})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"keen"}) + `\b([0-9a-z]{24})\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{"keen"} } // FromData will find and optionally verify KeenIO 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_KeenIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.keen.io/3.0/organizations/"+resIdMatch+"/projects", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_KeenIO } trufflehog-3.60.0/pkg/detectors/keenio/keenio_test.go000066400000000000000000000055701451332102400226360ustar00rootroot00000000000000//go:build detectors // +build detectors package keenio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKeenIO_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("KEENIO") inactiveSecret := testSecrets.MustGetField("KEENIO_INACTIVE") orgId := testSecrets.MustGetField("KEENIO_ID") 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 keenio secret %s within keenid %s", secret, orgId)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_KeenIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a keenio secret %s within keenid %s but not valid", inactiveSecret, orgId)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_KeenIO, 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("KeenIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("KeenIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/kickbox/000077500000000000000000000000001451332102400201475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kickbox/kickbox.go000066400000000000000000000035141451332102400221330ustar00rootroot00000000000000package kickbox import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kickbox"}) + `\b([a-zA-Z0-9_]+[a-zA-Z0-9]{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{"kickbox"} } // FromData will find and optionally verify Kickbox 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Kickbox, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.kickbox.com/v2/verify?email=kickbox@example.com&apikey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Kickbox } trufflehog-3.60.0/pkg/detectors/kickbox/kickbox_test.go000066400000000000000000000054601451332102400231740ustar00rootroot00000000000000//go:build detectors // +build detectors package kickbox import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKickbox_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KICKBOX") inactiveSecret := testSecrets.MustGetField("KICKBOX_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 kickbox secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kickbox, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kickbox 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_Kickbox, 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("Kickbox.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Kickbox.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) } } }) } } trufflehog-3.60.0/pkg/detectors/klaviyo/000077500000000000000000000000001451332102400201735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/klaviyo/klaviyo.go000066400000000000000000000067071451332102400222120ustar00rootroot00000000000000package klaviyo import ( "context" "encoding/json" "fmt" "net/http" "regexp" "strings" "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 } // 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. keyPat = regexp.MustCompile(`\b(pk_[[:alnum:]]{34})\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{"pk_"} } type response struct { Errors []struct { Id string `json:"id"` Status int `json:"status"` Code string `json:"code"` Title string `json:"title"` Detail string `json:"detail"` Source struct { Pointer string `json:"pointer"` } `json:"source"` } `json:"errors"` } // FromData will find and optionally verify Klaviyo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Klaviyo, Raw: []byte(resMatch), } if verify { client := s.client if client == nil { client = defaultClient } req, err := http.NewRequestWithContext(ctx, "GET", "https://a.klaviyo.com/api/profiles", nil) if err != nil { continue } req.Header.Set("Revision", "2023-02-22") req.Header.Set("Authorization", "Klaviyo-API-Key "+resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 403 { var apiResp response // Klaviyo responds with 403 when the API-key does not have permissions to hit /api/profiles. // Ensure that the 403 is from Klaviyo: https://developers.klaviyo.com/en/docs/rate_limits_and_error_handling if err = json.NewDecoder(res.Body).Decode(&apiResp); err == nil { // valid JSON response if len(apiResp.Errors) > 0 { // Thus, the key is verified, but it is up to the user to determine what scopes the key has. s1.Verified = true } else { s1.VerificationError = fmt.Errorf("errors expected") } } else { s1.VerificationError = fmt.Errorf("unexpected API JSON response") } } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) } else { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Klaviyo } trufflehog-3.60.0/pkg/detectors/klaviyo/klaviyo_test.go000066400000000000000000000103651451332102400232440ustar00rootroot00000000000000//go:build detectors // +build detectors package klaviyo import ( "context" "fmt" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKlaviyo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KLAVIYO") inactiveSecret := testSecrets.MustGetField("KLAVIYO_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a klaviyo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Klaviyo, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a klaviyo 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_Klaviyo, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a klaviyo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Klaviyo, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a klaviyo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Klaviyo, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("Klaviyo.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Klaviyo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/klipfolio/000077500000000000000000000000001451332102400205055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/klipfolio/klipfolio.go000066400000000000000000000041371451332102400230310ustar00rootroot00000000000000package klipfolio import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"klipfolio"}) + `\b([0-9a-f]{40})\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{"klipfolio"} } // FromData will find and optionally verify Klipfolio 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Klipfolio, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.klipfolio.com/api/1.0/users", nil) if err != nil { continue } req.Header.Add("kf-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Klipfolio } trufflehog-3.60.0/pkg/detectors/klipfolio/klipfolio_test.go000066400000000000000000000054161451332102400240710ustar00rootroot00000000000000//go:build detectors // +build detectors package klipfolio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKlipfolio_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KLIPFOLIO") inactiveSecret := testSecrets.MustGetField("KLIPFOLIO_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 klipfolio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Klipfolio, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a klipfolio 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_Klipfolio, 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("Klipfolio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Klipfolio.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/knapsackpro/000077500000000000000000000000001451332102400210315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/knapsackpro/knapsackpro.go000066400000000000000000000042721451332102400237010ustar00rootroot00000000000000package knapsackpro import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"knapsackpro"}) + `\b([0-9a-z]{32})\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{"knapsackpro"} } // FromData will find and optionally verify KnapsackPro 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_KnapsackPro, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.knapsackpro.com/v1/builds?page=1", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("KNAPSACK-PRO-TEST-SUITE-TOKEN", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_KnapsackPro } trufflehog-3.60.0/pkg/detectors/knapsackpro/knapsackpro_test.go000066400000000000000000000055301451332102400247360ustar00rootroot00000000000000//go:build detectors // +build detectors package knapsackpro import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKnapsackPro_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KNAPSACKPRO") inactiveSecret := testSecrets.MustGetField("KNAPSACKPRO_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 knapsackpro secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_KnapsackPro, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a knapsackpro 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_KnapsackPro, 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("KnapsackPro.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("KnapsackPro.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) } } }) } } trufflehog-3.60.0/pkg/detectors/kontent/000077500000000000000000000000001451332102400201775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kontent/kontent.go000066400000000000000000000041041451332102400222070ustar00rootroot00000000000000package kontent import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kontent"}) + `\b([a-z0-9-]{36})\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{"kontent"} } // FromData will find and optionally verify Kontent 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Kontent, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://deliver.kontent.ai/%s/items", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Kontent } trufflehog-3.60.0/pkg/detectors/kontent/kontent_test.go000066400000000000000000000054371451332102400232600ustar00rootroot00000000000000//go:build detectors // +build detectors package kontent import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKontent_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("KONTENT") inactiveSecret := testSecrets.MustGetField("KONTENT_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 kontent secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kontent, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kontent 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_Kontent, 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("Kontent.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Kontent.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) } } }) } } trufflehog-3.60.0/pkg/detectors/kraken/000077500000000000000000000000001451332102400177705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kraken/kraken.go000066400000000000000000000073111451332102400215740ustar00rootroot00000000000000package kraken import ( "context" "crypto/hmac" "crypto/sha256" "crypto/sha512" "encoding/base64" "io" "net/http" "net/url" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // Bounds have been removed because there are some cases that tokens have trailing frontslash(/) or plus sign (+) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kraken"}) + `\b([0-9A-Za-z\/\+=]{56}[ "'\r\n]{1})`) privKeyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kraken"}) + `\b([0-9A-Za-z\/\+=]{86,88}[ "'\r\n]{1})`) ) // 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{"kraken"} } // FromData will find and optionally verify Kraken 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) privKeyMatches := privKeyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, privKeyMatch := range privKeyMatches { if len(privKeyMatch) != 2 { continue } resPrivKeyMatch := strings.TrimSpace(privKeyMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Kraken, Raw: []byte(resMatch), RawV2: []byte(resMatch + resPrivKeyMatch), } if verify { // Increasing 64-bit integer, for each request that is made with a particular API key. apiNonce := strconv.FormatInt(time.Now().Unix(), 10) payload := url.Values{} payload.Add("nonce", apiNonce) b64DecodedSecret, _ := base64.StdEncoding.DecodeString(resPrivKeyMatch) signature := getKrakenSignature("/0/private/Balance", payload, b64DecodedSecret) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.kraken.com/0/private/Balance", strings.NewReader(payload.Encode())) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("API-Key", resMatch) req.Header.Add("API-Sign", signature) res, err := client.Do(req) if err == nil { defer res.Body.Close() out, _ := io.ReadAll(res.Body) if !strings.Contains(string(out), "Invalid key") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } // Code from https://docs.kraken.com/rest/#section/Authentication/Headers-and-Signature func getKrakenSignature(url_path string, values url.Values, secret []byte) string { sha := sha256.New() sha.Write([]byte(values.Get("nonce") + values.Encode())) shasum := sha.Sum(nil) mac := hmac.New(sha512.New, secret) mac.Write(append([]byte(url_path), shasum...)) macsum := mac.Sum(nil) return base64.StdEncoding.EncodeToString(macsum) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Kraken } trufflehog-3.60.0/pkg/detectors/kraken/kraken_test.go000066400000000000000000000057651451332102400226460ustar00rootroot00000000000000//go:build detectors // +build detectors package kraken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKraken_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("KRAKEN") inactiveSecret := testSecrets.MustGetField("KRAKEN_INACTIVE") private := testSecrets.MustGetField("KRAKEN_PRIVATE") inactivePrivate := testSecrets.MustGetField("KRAKEN_PRIVATE_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 kraken secret %s with kraken private key %s within", secret, private)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kraken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kraken secret %s with kraken private key %s within but not valid", inactiveSecret, inactivePrivate)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kraken, 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("Kraken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Kraken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/kucoin/000077500000000000000000000000001451332102400200055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kucoin/kucoin.go000066400000000000000000000102631451332102400216260ustar00rootroot00000000000000package kucoin import ( "context" "crypto/hmac" "crypto/sha256" "encoding/base64" "net/http" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kucoin"}) + `\b([0-9a-f]{24})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kucoin"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`) passphrasePat = regexp.MustCompile(detectors.PrefixRegex([]string{"kucoin"}) + `([ \r\n]{1}[!-~]{7,32}[ \r\n]{1})`) ) // 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{"kucoin"} } // FromData will find and optionally verify KuCoin 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) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) passphraseMatches := passphrasePat.FindAllStringSubmatch(dataStr, -1) for _, keyMatch := range keyMatches { if len(keyMatch) != 2 { continue } resKeyMatch := strings.TrimSpace(keyMatch[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) for _, passphraseMatch := range passphraseMatches { if len(passphraseMatch) != 2 { continue } resPassphraseMatch := strings.TrimSpace(passphraseMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_KuCoin, Raw: []byte(resKeyMatch), RawV2: []byte(resKeyMatch + resPassphraseMatch), } if verify { timestamp := strconv.FormatInt(time.Now().Unix()*1000, 10) method := "GET" endpoint := "/api/v1/accounts" bodyStr := "" apiVersion := "2" signature := getKucoinSignature(resSecretMatch, timestamp, method, endpoint, bodyStr) passPhrase := getKucoinPassphrase(resSecretMatch, resPassphraseMatch) req, err := http.NewRequest(method, "https://api.kucoin.com"+endpoint, nil) if err != nil { continue } req.Header.Add("KC-API-KEY", resKeyMatch) req.Header.Add("KC-API-SIGN", signature) req.Header.Add("KC-API-TIMESTAMP", timestamp) req.Header.Add("KC-API-PASSPHRASE", passPhrase) req.Header.Add("KC-API-KEY-VERSION", apiVersion) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resKeyMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func getKucoinPassphrase(apiSecret string, apiPassphrase string) string { mac := hmac.New(sha256.New, []byte(apiSecret)) mac.Write([]byte(apiPassphrase)) macsum := mac.Sum(nil) return base64.StdEncoding.EncodeToString(macsum) } func getKucoinSignature(apiSecret string, timestamp string, method string, endpoint string, bodyStr string) string { preHashStr := timestamp + method + endpoint + bodyStr mac := hmac.New(sha256.New, []byte(apiSecret)) mac.Write([]byte(preHashStr)) macsum := mac.Sum(nil) return base64.StdEncoding.EncodeToString(macsum) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_KuCoin } trufflehog-3.60.0/pkg/detectors/kucoin/kucoin_test.go000066400000000000000000000061641451332102400226720ustar00rootroot00000000000000//go:build detectors // +build detectors package kucoin import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKuCoin_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("KUCOIN") inactiveSecret := testSecrets.MustGetField("KUCOIN_INACTIVE") key := testSecrets.MustGetField("KUCOIN_KEY") inactiveKey := testSecrets.MustGetField("KUCOIN_KEY_INACTIVE") passphrase := testSecrets.MustGetField("KUCOIN_PASSPHRASE") inactivePassphrase := testSecrets.MustGetField("KUCOIN_PASSPHRASE_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 kucoin key %s with kucoin secret %s and kucoin passphrase %s within", key, secret, passphrase)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_KuCoin, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kucoin key %s with kucoin secret %s and kucoin passphrase %s within but not valid", inactiveKey, inactiveSecret, inactivePassphrase)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_KuCoin, 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("KuCoin.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("KuCoin.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/kylas/000077500000000000000000000000001451332102400176405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/kylas/kylas.go000066400000000000000000000041641451332102400213170ustar00rootroot00000000000000package kylas import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"kylas"}) + `\b([a-z0-9-]{36})\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{"kylas"} } // FromData will find and optionally verify Kylas 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Kylas, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.kylas.io/v1/contacts", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Kylas } trufflehog-3.60.0/pkg/detectors/kylas/kylas_test.go000066400000000000000000000054341451332102400223570ustar00rootroot00000000000000//go:build detectors // +build detectors package kylas import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestKylas_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("KYLAS") inactiveSecret := testSecrets.MustGetField("KYLAS_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 kylas secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Kylas, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a kylas 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_Kylas, 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("Kylas.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Kylas.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) } } }) } } trufflehog-3.60.0/pkg/detectors/languagelayer/000077500000000000000000000000001451332102400213355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/languagelayer/languagelayer.go000066400000000000000000000047761451332102400245220ustar00rootroot00000000000000package languagelayer import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"languagelayer"}) + `\b([a-z0-9]{32})\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{"languagelayer"} } // FromData will find and optionally verify LanguageLayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LanguageLayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.languagelayer.com/languages?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `results`) || strings.Contains(bodyString, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LanguageLayer } trufflehog-3.60.0/pkg/detectors/languagelayer/languagelayer_test.go000066400000000000000000000055541451332102400255540ustar00rootroot00000000000000//go:build detectors // +build detectors package languagelayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLanguageLayer_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("LANGUAGELAYER") inactiveSecret := testSecrets.MustGetField("LANGUAGELAYER_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 languagelayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LanguageLayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a languagelayer 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_LanguageLayer, 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("LanguageLayer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LanguageLayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lastfm/000077500000000000000000000000001451332102400200035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lastfm/lastfm.go000066400000000000000000000041111451332102400216150ustar00rootroot00000000000000package lastfm import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lastfm"}) + `\b([0-9a-z]{32})\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{"lastfm"} } // FromData will find and optionally verify Lastfm 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Lastfm, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://ws.audioscrobbler.com/2.0/?method=artist.getSimilar&api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Lastfm } trufflehog-3.60.0/pkg/detectors/lastfm/lastfm_test.go000066400000000000000000000054461451332102400226700ustar00rootroot00000000000000//go:build detectors // +build detectors package lastfm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLastfm_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("LASTFM") inactiveSecret := testSecrets.MustGetField("LASTFM_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 lastfm secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Lastfm, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lastfm 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_Lastfm, 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("Lastfm.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Lastfm.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) } } }) } } trufflehog-3.60.0/pkg/detectors/launchdarkly/000077500000000000000000000000001451332102400211765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/launchdarkly/launchdarkly.go000066400000000000000000000067121451332102400242140ustar00rootroot00000000000000package launchdarkly import ( "context" "fmt" "net/http" "regexp" "strings" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ldclient "github.com/launchdarkly/go-server-sdk/v6" "github.com/launchdarkly/go-server-sdk/v6/ldcomponents" ) type Scanner struct { client *http.Client } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClient() defaultSDKConfig = ldclient.Config{ Logging: ldcomponents.NoLogging(), } defaultSDKTimeout = 10 * time.Second invalidSDKKeyError = "SDK key contains invalid characters" // Launchdarkly keys are UUIDv4s with either api- or sdk- prefixes. // mob- keys are possible, but are not sensitive credentials. keyPat = regexp.MustCompile(`\b((?:api|sdk)-[a-z0-9]{8}-[a-z0-9]{4}-4[a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12})\b`) ) // We are not including "mob-" because client keys are not sensitive. // They are expected to be public. func (s Scanner) Keywords() []string { return []string{"api-", "sdk-"} } // FromData will find and optionally verify LaunchDarkly 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LaunchDarkly, Raw: []byte(resMatch), } if verify { if strings.HasPrefix(resMatch, "api-") { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.launchdarkly.com/api/v2/tokens", nil) if err != nil { continue } client := s.client if client == nil { client = defaultClient } req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 401 { // 401 is expected for an invalid token, so there is nothing to do here. } else { s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } else { s1.VerificationError = err } } else { // This is a server SDK key. Try to initialize using the SDK. _, err := ldclient.MakeCustomClient(resMatch, defaultSDKConfig, defaultSDKTimeout) if err == nil { s1.Verified = true } else if err == ldclient.ErrInitializationFailed || err.Error() == invalidSDKKeyError { // If initialization fails, the key is not valid, so do nothing. } else { // If the error isn't nil or known, then this is likely a timeout error: ldclient.ErrInitializationTimeout // But any other error here means we don't know if this key is valid. s1.VerificationError = err } } } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LaunchDarkly } trufflehog-3.60.0/pkg/detectors/launchdarkly/launchdarkly_test.go000066400000000000000000000075611451332102400252560ustar00rootroot00000000000000//go:build detectors // +build detectors package launchdarkly import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLaunchDarkly_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LAUNCHDARKLY_TOKEN") inactiveSecret := testSecrets.MustGetField("LAUNCHDARKLY_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a launchdarkly secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LaunchDarkly, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a launchdarkly 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_LaunchDarkly, 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: "found, valid but unexpected api response", s: Scanner{client: common.ConstantResponseHttpClient(500, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a launchdarkly secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LaunchDarkly, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, // TODO: enable this test and add the sdk token /* { name: "found, valid sdk token", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a launchdarkly sdk secret %s within", sdkSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LaunchDarkly, Verified: true, }, }, 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("LaunchDarkly.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LaunchDarkly.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ldap/000077500000000000000000000000001451332102400174355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ldap/ldap.go000066400000000000000000000106631451332102400207120ustar00rootroot00000000000000package ldap import ( "context" "crypto/tls" "fmt" "net" "net/url" "regexp" "strings" "time" "github.com/go-ldap/ldap/v3" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. uriPat = regexp.MustCompile(`\b(?i)ldaps?://[\S]+\b`) // ldap://127.0.0.1:389 // ldap://127.0.0.1 // ldap://mydomain.test // ldaps://[fe80:4049:92ff:fe44:4bd1]:5060 // ldap://[fe80::4bd1]:5060 // ldap://ds.example.com:389/dc=example,dc=com?givenName,sn,cn?sub?(uid=john.doe) usernamePat = regexp.MustCompile(detectors.PrefixRegex([]string{"user", "bind"}) + `["']([a-zA-Z=,]{4,150})["']`) passwordPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pass"}) + `["']([\S]{4,48})["']`) // https://learn.microsoft.com/en-us/windows/win32/api/iads/nf-iads-iadsopendsobject-opendsobject?redirectedfrom=MSDN // I.E. Set ou = dso.OpenDSObject("LDAP://DC.business.com/OU=IT,DC=Business,DC=com", "Business\administrator", "Pa$$word01", 1) iadPat = regexp.MustCompile(`OpenDSObject\(\"(?i)(ldaps?://[\S]+)\", ?\"([\S]+)\", ?\"([\S]+)\",[ \d]+\)`) ) // 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{"ldaps://", "ldap://"} } // FromData will find and optionally verify Ldap 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) // Check for matches in the URI + username + password format uriMatches := uriPat.FindAllString(dataStr, -1) for _, uri := range uriMatches { ldapURL, err := url.Parse(uri) if err != nil { continue } usernameMatches := usernamePat.FindAllStringSubmatch(dataStr, -1) for _, username := range usernameMatches { passwordMatches := passwordPat.FindAllStringSubmatch(dataStr, -1) for _, password := range passwordMatches { s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LDAP, Raw: []byte(strings.Join([]string{ldapURL.String(), username[1], password[1]}, "\t")), } if verify { verificationErr := verifyLDAP(username[1], password[1], ldapURL) s1.Verified = verificationErr == nil if !isErrDeterminate(verificationErr) { s1.VerificationError = verificationErr } } results = append(results, s1) } } } // Check for matches for the IAD library format iadMatches := iadPat.FindAllStringSubmatch(dataStr, -1) for _, iad := range iadMatches { uri := iad[1] username := iad[2] password := iad[3] ldapURL, err := url.Parse(uri) if err != nil { continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LDAP, Raw: []byte(strings.Join([]string{ldapURL.String(), username, password}, "\t")), } if verify { verificationError := verifyLDAP(username, password, ldapURL) s1.Verified = verificationError == nil if !isErrDeterminate(verificationError) { s1.VerificationError = verificationError } } results = append(results, s1) } return results, nil } func isErrDeterminate(err error) bool { switch e := err.(type) { case *ldap.Error: switch e.Err.(type) { case *net.OpError: return false } } return true } func verifyLDAP(username, password string, ldapURL *url.URL) error { // Tests with non-TLS, TLS, and STARTTLS ldap.DefaultTimeout = 5 * time.Second uri := ldapURL.String() switch ldapURL.Scheme { case "ldap": // Non-TLS dial l, err := ldap.DialURL(uri) if err != nil { return err } defer l.Close() // Non-TLS verify err = l.Bind(username, password) if err == nil { return nil } // STARTTLS err = l.StartTLS(&tls.Config{InsecureSkipVerify: true}) if err != nil { return err } // STARTTLS verify return l.Bind(username, password) case "ldaps": // TLS dial l, err := ldap.DialTLS("tcp", uri, &tls.Config{InsecureSkipVerify: true}) if err != nil { return err } defer l.Close() // TLS verify return l.Bind(username, password) } return fmt.Errorf("unknown ldap scheme %q", ldapURL.Scheme) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LDAP } trufflehog-3.60.0/pkg/detectors/ldap/ldap_integration_test.go000066400000000000000000000125751451332102400243600ustar00rootroot00000000000000//go:build detectors && integration // +build detectors,integration package ldap import ( "bytes" "context" "errors" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "os" "os/exec" "strings" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMain(m *testing.M) { code, err := runMain(m) if err != nil { panic(err) } os.Exit(code) } func runMain(m *testing.M) (int, error) { if err := startOpenLDAP(); err != nil { return 0, err } defer stopOpenLDAP() return m.Run(), nil } func dockerLogLine(hash string, needle string) chan struct{} { ch := make(chan struct{}, 1) go func() { for { out, err := exec.Command("docker", "logs", hash).CombinedOutput() if err != nil { panic(err) } if strings.Contains(string(out), needle) { ch <- struct{}{} return } time.Sleep(1 * time.Second) } }() return ch } func TestLdap_Integration_FromChunk(t *testing.T) { type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found with URI and separate user+password usage, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(` ldap://localhost:1389 binddn="cn=admin,dc=example,dc=org" pass="P@55w0rd"`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, Verified: true, }, }, wantErr: false, }, { name: "found with URI and separate user+password usage, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(` ldap://localhost:1389 binddn="cn=someuser,dc=example,dc=org" pass="P@55w0rd"`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, Verified: false, }, }, wantErr: false, }, { name: "found with IAD lib usage, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(`Set ou = dso.OpenDSObject("LDAP://localhost:1389", "cn=admin,dc=example,dc=org", "P@55w0rd", 1)`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, Verified: true, }, }, wantErr: false, }, { name: "found with IAD lib usage, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(`Set ou = dso.OpenDSObject("LDAP://localhost:1389", "cn=admin,dc=example,dc=org", "P@55w0rd", 1)`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, Verified: true, }, }, wantErr: false, }, { name: "found with IAD lib usage, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(`Set ou = dso.OpenDSObject("LDAP://localhost:1389", "cn=admin,dc=example,dc=org", "invalid", 1)`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, Verified: false, }, }, wantErr: false, }, { name: "inaccessible host", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(` ldap://badhost:1389 binddn="cn=admin,dc=example,dc=org" pass="P@55w0rd"`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { 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("Ldap.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Ldap.FromData() %s diff: (-got +want)\n%s", tt.name, diff) } }) } } var containerID string func startOpenLDAP() error { cmd := exec.Command( "docker", "run", "--rm", "-p", "1389:1389", "-e", "LDAP_ROOT=dc=example,dc=org", "-e", "LDAP_ADMIN_USERNAME=admin", "-e", "LDAP_ADMIN_PASSWORD=P@55w0rd", "-d", "bitnami/openldap:latest", ) out, err := cmd.Output() if err != nil { return err } containerID = string(bytes.TrimSpace(out)) select { case <-dockerLogLine(containerID, "slapd starting"): return nil case <-time.After(30 * time.Second): stopOpenLDAP() return errors.New("timeout waiting for ldap service to be ready") } } func stopOpenLDAP() { exec.Command("docker", "kill", containerID).Run() } trufflehog-3.60.0/pkg/detectors/ldap/ldap_test.go000066400000000000000000000040071451332102400217440ustar00rootroot00000000000000//go:build detectors // +build detectors package ldap import ( "context" "testing" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLdap_FromChunk(t *testing.T) { 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 with IAD lib usage, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(`Set ou = dso.OpenDSObject("LDAP://DC.business.com/OU=IT,DC=Business,DC=com", "Business\administrator", "Pa$$word01", 1)`), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LDAP, 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("Ldap.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Ldap.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) } } }) } } trufflehog-3.60.0/pkg/detectors/leadfeeder/000077500000000000000000000000001451332102400205755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/leadfeeder/leadfeeder.go000066400000000000000000000042161451332102400232070ustar00rootroot00000000000000package leadfeeder import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"leadfeeder"}) + `\b([a-zA-Z0-9-]{43})\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{"leadfeeder"} } // FromData will find and optionally verify Leadfeeder 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Leadfeeder, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.leadfeeder.com/accounts", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Token token=%s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Leadfeeder } trufflehog-3.60.0/pkg/detectors/leadfeeder/leadfeeder_test.go000066400000000000000000000054751451332102400242560ustar00rootroot00000000000000//go:build detectors // +build detectors package leadfeeder import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLeadfeeder_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("LEADFEEDER") inactiveSecret := testSecrets.MustGetField("LEADFEEDER_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 leadfeeder secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Leadfeeder, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a leadfeeder 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_Leadfeeder, 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("Leadfeeder.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Leadfeeder.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lemlist/000077500000000000000000000000001451332102400201665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lemlist/lemlist.go000066400000000000000000000043371451332102400221750ustar00rootroot00000000000000package lemlist import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lemlist"}) + `\b([a-f0-9]{32})\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{"lemlist"} } // FromData will find and optionally verify Lemlist 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Lemlist, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf(":%s", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.lemlist.com/api/team", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Lemlist } trufflehog-3.60.0/pkg/detectors/lemlist/lemlist_test.go000066400000000000000000000054061451332102400232320ustar00rootroot00000000000000package lemlist import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLemlist_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LEMLIST") inactiveSecret := testSecrets.MustGetField("LEMLIST_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 lemlist secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Lemlist, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lemlist 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_Lemlist, 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("Lemlist.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Lemlist.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lendflow/000077500000000000000000000000001451332102400203275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lendflow/lendflow.go000066400000000000000000000043051451332102400224720ustar00rootroot00000000000000package lendflow import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lendflow"}) + `\b([a-zA-Z0-9]{36}\.[a-zA-Z0-9]{235}\.[a-zA-Z0-9]{32}\-[a-zA-Z0-9]{47}\-[a-zA-Z0-9_]{162}\-[a-zA-Z0-9]{42}\-[a-zA-Z0-9_]{40}\-[a-zA-Z0-9_]{66}\-[a-zA-Z0-9_]{59}\-[a-zA-Z0-9]{7}\-[a-zA-Z0-9_]{220})\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{"lendflow"} } // FromData will find and optionally verify Lendflow 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Lendflow, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", "https://app.lendflow.io/api/v1/deals", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) req.Header.Add("Accept", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Lendflow } trufflehog-3.60.0/pkg/detectors/lendflow/lendflow_test.go000066400000000000000000000054041451332102400235320ustar00rootroot00000000000000//go:build detectors // +build detectors package lendflow import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLendflow_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LENDFLOW") inactiveSecret := testSecrets.MustGetField("LENDFLOW_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 lendflow secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Lendflow, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lendflow 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_Lendflow, 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("Lendflow.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Lendflow.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/lessannoyingcrm/000077500000000000000000000000001451332102400217305ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lessannoyingcrm/lessannoyingcrm.go000066400000000000000000000044321451332102400254750ustar00rootroot00000000000000package lessannoyingcrm import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"less"}) + `\b([a-zA-Z0-9-]{57})\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{"lessannoyingcrm"} } // FromData will find and optionally verify LessAnnoyingCRM 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LessAnnoyingCRM, Raw: []byte(resMatch), } if verify { userCode := strings.Split(resMatch, "-") req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.lessannoyingcrm.com?UserCode=%s&APIToken=%s&Function=GetUserInfo", userCode, resMatch), nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.lessannoyingcrm+json; version=3") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LessAnnoyingCRM } trufflehog-3.60.0/pkg/detectors/lessannoyingcrm/lessannoyingcrm_test.go000066400000000000000000000056061451332102400265400ustar00rootroot00000000000000//go:build detectors // +build detectors package lessannoyingcrm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLessAnnoyingCRM_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LESSANNOYINGCRM_TOKEN") inactiveSecret := testSecrets.MustGetField("LESSANNOYINGCRM_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 lessannoyingcrm secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LessAnnoyingCRM, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lessannoyingcrm 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_LessAnnoyingCRM, 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("LessAnnoyingCRM.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LessAnnoyingCRM.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lexigram/000077500000000000000000000000001451332102400203255ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lexigram/lexigram.go000066400000000000000000000042221451332102400224640ustar00rootroot00000000000000package lexigram import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lexigram"}) + `\b([a-zA-Z0-9\S]{301})\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{"lexigram"} } // FromData will find and optionally verify Lexigram 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Lexigram, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.lexigram.io/v1/lexigraph/search?q=diabetes", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Lexigram } trufflehog-3.60.0/pkg/detectors/lexigram/lexigram_test.go000066400000000000000000000054721451332102400235330ustar00rootroot00000000000000//go:build detectors // +build detectors package lexigram import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLexigram_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("LEXIGRAM") inactiveSecret := testSecrets.MustGetField("LEXIGRAM_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 lexigram secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Lexigram, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lexigram 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_Lexigram, 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("Lexigram.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Lexigram.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) } } }) } } trufflehog-3.60.0/pkg/detectors/linearapi/000077500000000000000000000000001451332102400204615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/linearapi/linearapi.go000066400000000000000000000045371451332102400227650ustar00rootroot00000000000000package linearapi import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(lin_api_[0-9A-Za-z]{40})\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{"lin_api_"} } // FromData will find and optionally verify LinearAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LinearAPI, Raw: []byte(resMatch), } if verify { reqDataQuery := strings.NewReader(`{ "query": "{ viewer { id name } }" }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.linear.app/graphql", reqDataQuery) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", 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 res.StatusCode >= 200 && res.StatusCode < 300 && strings.Contains(body, `"data":`) { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LinearAPI } trufflehog-3.60.0/pkg/detectors/linearapi/linearapi_test.go000066400000000000000000000055121451332102400240160ustar00rootroot00000000000000//go:build detectors // +build detectors package linearapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLinearAPI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LINEARAPI_TOKEN") inactiveSecret := testSecrets.MustGetField("LINEARAPI_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 linearapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LinearAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a linearapi 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_LinearAPI, 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("LinearAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LinearAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/linemessaging/000077500000000000000000000000001451332102400213425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/linemessaging/linemessaging.go000066400000000000000000000042131451332102400245160ustar00rootroot00000000000000package linemessaging import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"line"}) + `\b([A-Za-z0-9+/]{171,172})\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{"line"} } // FromData will find and optionally verify LineMessaging 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LineMessaging, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.line.me/v2/bot/info", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LineMessaging } trufflehog-3.60.0/pkg/detectors/linemessaging/linemessaging_test.go000066400000000000000000000055401451332102400255610ustar00rootroot00000000000000//go:build detectors // +build detectors package linemessaging import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLineMessaging_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LINEMESSAGING_TOKEN") inactiveSecret := testSecrets.MustGetField("LINEMESSAGING_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 line secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LineMessaging, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a line 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_LineMessaging, 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("LineMessaging.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LineMessaging.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) } } }) } } trufflehog-3.60.0/pkg/detectors/linenotify/000077500000000000000000000000001451332102400206755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/linenotify/linenotify.go000066400000000000000000000042531451332102400234100ustar00rootroot00000000000000package linenotify import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"linenotify"}) + `\b([0-9A-Za-z]{43})\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{"linenotify"} } // FromData will find and optionally verify LineNotify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LineNotify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "POST", "https://notify-api.line.me/api/notify?message=Notification Successful", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LineNotify } trufflehog-3.60.0/pkg/detectors/linenotify/linenotify_test.go000066400000000000000000000055241451332102400244510ustar00rootroot00000000000000//go:build detectors // +build detectors package linenotify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLineNotify_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LINENOTIFY_TOKEN") inactiveSecret := testSecrets.MustGetField("LINENOTIFY_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 linenotify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LineNotify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a linenotify 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_LineNotify, 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("LineNotify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LineNotify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/linkpreview/000077500000000000000000000000001451332102400210545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/linkpreview/linkpreview.go000066400000000000000000000041571451332102400237510ustar00rootroot00000000000000package linkpreview import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"linkpreview"}) + `\b([a-zA-Z0-9]{32})\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{"linkpreview"} } // FromData will find and optionally verify LinkPreview 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LinkPreview, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.linkpreview.net/?key=%s&q=https://google.com", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LinkPreview } trufflehog-3.60.0/pkg/detectors/linkpreview/linkpreview_test.go000066400000000000000000000055301451332102400250040ustar00rootroot00000000000000//go:build detectors // +build detectors package linkpreview import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLinkPreview_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("LINKPREVIEW") inactiveSecret := testSecrets.MustGetField("LINKPREVIEW_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 linkpreview secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LinkPreview, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a linkpreview 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_LinkPreview, 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("LinkPreview.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LinkPreview.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) } } }) } } trufflehog-3.60.0/pkg/detectors/liveagent/000077500000000000000000000000001451332102400204735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/liveagent/liveagent.go000066400000000000000000000041451451332102400230040ustar00rootroot00000000000000package liveagent import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"liveagent"}) + `\b([a-zA-Z0-9]{32})\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{"liveagent"} } // FromData will find and optionally verify LiveAgent 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LiveAgent, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://secretscanner.ladesk.com/api/v3/agents", nil) if err != nil { continue } req.Header.Add("apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LiveAgent } trufflehog-3.60.0/pkg/detectors/liveagent/liveagent_test.go000066400000000000000000000055121451332102400240420ustar00rootroot00000000000000//go:build detectors // +build detectors package liveagent import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLiveAgent_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LIVEAGENT_TOKEN") inactiveSecret := testSecrets.MustGetField("LIVEAGENT_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 liveagent secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LiveAgent, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a liveagent 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_LiveAgent, 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("LiveAgent.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LiveAgent.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) } } }) } } trufflehog-3.60.0/pkg/detectors/livestorm/000077500000000000000000000000001451332102400205415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/livestorm/livestorm.go000066400000000000000000000042571451332102400231240ustar00rootroot00000000000000package livestorm import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"livestorm"}) + `\b(eyJhbGciOiJIUzI1NiJ9\.eyJhdWQiOiJhcGkubGl2ZXN0b3JtLmNvIiwianRpIjoi[0-9A-Z-a-z]{134}\.[0-9A-Za-z\-\_]{43}[ \r\n]{1})`) ) // 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{"livestorm"} } // FromData will find and optionally verify Livestorm 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Livestorm, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", "https://api.livestorm.co/v1/ping", nil) if err != nil { continue } req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Livestorm } trufflehog-3.60.0/pkg/detectors/livestorm/livestorm_test.go000066400000000000000000000054161451332102400241610ustar00rootroot00000000000000//go:build detectors // +build detectors package livestorm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLivestorm_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LIVESTORM") inactiveSecret := testSecrets.MustGetField("LIVESTORM_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 livestorm secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Livestorm, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a livestorm 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_Livestorm, 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("Livestorm.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Livestorm.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/loadmill/000077500000000000000000000000001451332102400203125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/loadmill/loadmill.go000066400000000000000000000043301451332102400224360ustar00rootroot00000000000000package loadmill import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"loadmill"}) + `\b([0-9a-zA-Z]{40})\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{"loadmill"} } // FromData will find and optionally verify Loadmill 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Loadmill, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.loadmill.com/api/v1/labels?filter=CI_enable", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.loadmill+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Loadmill } trufflehog-3.60.0/pkg/detectors/loadmill/loadmill_test.go000066400000000000000000000054721451332102400235050ustar00rootroot00000000000000//go:build detectors // +build detectors package loadmill import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLoadmill_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LOADMILL") inactiveSecret := testSecrets.MustGetField("LOADMILL_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 loadmill secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Loadmill, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a loadmill 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_Loadmill, 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("Loadmill.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Loadmill.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lob/000077500000000000000000000000001451332102400172715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lob/lob.go000066400000000000000000000040601451332102400203740ustar00rootroot00000000000000package lob import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lob"}) + `\b([a-zA-Z0-9_]{40})\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{"lob"} } // FromData will find and optionally verify Lob 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Lob, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.lob.com/v1/addresses", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Lob } trufflehog-3.60.0/pkg/detectors/lob/lob_test.go000066400000000000000000000054101451332102400214330ustar00rootroot00000000000000//go:build detectors // +build detectors package lob import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLob_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("LOB") inactiveSecret := testSecrets.MustGetField("LOB_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 lob secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Lob, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lob 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_Lob, 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("Lob.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Lob.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) } } }) } } trufflehog-3.60.0/pkg/detectors/locationiq/000077500000000000000000000000001451332102400206575ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/locationiq/locationiq.go000066400000000000000000000041211451332102400233460ustar00rootroot00000000000000package locationiq import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(pk\.[a-zA-Z-0-9]{32})\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{"locationiq"} } // FromData will find and optionally verify LocationIQ 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LocationIQ, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://us1.locationiq.com/v1/reverse.php?key="+resMatch+"&lat=-37.870662&lon=144.9803321&format=json", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LocationIQ } trufflehog-3.60.0/pkg/detectors/locationiq/locationiq_test.go000066400000000000000000000055161451332102400244160ustar00rootroot00000000000000//go:build detectors // +build detectors package locationiq import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLocationIQ_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("LOCATIONIQ") inactiveSecret := testSecrets.MustGetField("LOCATIONIQ_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 locationiq secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LocationIQ, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a locationiq 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_LocationIQ, 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("LocationIQ.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LocationIQ.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) } } }) } } trufflehog-3.60.0/pkg/detectors/loginradius/000077500000000000000000000000001451332102400210355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/loginradius/loginradius.go000066400000000000000000000042151451332102400237060ustar00rootroot00000000000000package loginradius import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"loginradius"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"loginradius"} } // FromData will find and optionally verify Loginradius 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Loginradius, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.loginradius.com/identity/v2/serverinfo?apikey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Loginradius } trufflehog-3.60.0/pkg/detectors/loginradius/loginradius_test.go000066400000000000000000000055301451332102400247460ustar00rootroot00000000000000//go:build detectors // +build detectors package loginradius import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLoginradius_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("LOGINRADIUS") inactiveSecret := testSecrets.MustGetField("LOGINRADIUS_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 loginradius secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Loginradius, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a loginradius 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_Loginradius, 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("Loginradius.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Loginradius.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lokalisetoken/000077500000000000000000000000001451332102400213615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lokalisetoken/lokalisetoken.go000066400000000000000000000042161451332102400245570ustar00rootroot00000000000000package lokalisetoken import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lokalise"}) + `\b([a-z0-9]{40})\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{"lokalise"} } // FromData will find and optionally verify LokaliseToken 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) // log.Println(dataStr) matches := keyPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LokaliseToken, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.lokalise.com/api2/system/languages", nil) if err != nil { continue } req.Header.Add("x-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 } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LokaliseToken } trufflehog-3.60.0/pkg/detectors/lokalisetoken/lokalisetoken_test.go000066400000000000000000000055501451332102400256200ustar00rootroot00000000000000//go:build detectors // +build detectors package lokalisetoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLokaliseToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LOKALISE_TOKEN") inactiveSecret := testSecrets.MustGetField("LOKALISE_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 lokalisetoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LokaliseToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lokalisetoken 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_LokaliseToken, 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("LokaliseToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LokaliseToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/loyverse/000077500000000000000000000000001451332102400203655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/loyverse/loyverse.go000066400000000000000000000041761451332102400225740ustar00rootroot00000000000000package loyverse import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"loyverse"}) + `\b([0-9-a-z]{32})\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{"loyverse"} } // FromData will find and optionally verify Loyverse 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Loyverse, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.loyverse.com/v1.0/merchant/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Loyverse } trufflehog-3.60.0/pkg/detectors/loyverse/loyverse_test.go000066400000000000000000000054721451332102400236330ustar00rootroot00000000000000//go:build detectors // +build detectors package loyverse import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLoyverse_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("LOYVERSE") inactiveSecret := testSecrets.MustGetField("LOYVERSE_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 loyverse secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Loyverse, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a loyverse 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_Loyverse, 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("Loyverse.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Loyverse.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) } } }) } } trufflehog-3.60.0/pkg/detectors/lunchmoney/000077500000000000000000000000001451332102400206765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/lunchmoney/lunchmoney.go000066400000000000000000000042131451332102400234060ustar00rootroot00000000000000package lunchmoney import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"lunchmoney"}) + `\b([a-f0-9]{50})\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{"lunchmoney"} } // FromData will find and optionally verify LunchMoney 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_LunchMoney, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://dev.lunchmoney.app/v1/categories", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_LunchMoney } trufflehog-3.60.0/pkg/detectors/lunchmoney/lunchmoney_test.go000066400000000000000000000055161451332102400244540ustar00rootroot00000000000000//go:build detectors // +build detectors package lunchmoney import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLunchMoney_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LUNCHMONEY") inactiveSecret := testSecrets.MustGetField("LUNCHMONEY_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 lunchmoney secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_LunchMoney, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a lunchmoney 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_LunchMoney, 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("LunchMoney.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("LunchMoney.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) } } }) } } trufflehog-3.60.0/pkg/detectors/luno/000077500000000000000000000000001451332102400174725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/luno/luno.go000066400000000000000000000046141451332102400210030ustar00rootroot00000000000000package luno import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"luno"}) + `\b([a-zA-Z0-9_-]{43})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"luno"}) + `\b([a-z0-9]{13})\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{"luno"} } // FromData will find and optionally verify Luno 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Luno, Raw: []byte(tokenPatMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.luno.com/api/1/balance", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Luno } trufflehog-3.60.0/pkg/detectors/luno/luno_test.go000066400000000000000000000055361451332102400220460ustar00rootroot00000000000000//go:build detectors // +build detectors package luno import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestLuno_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("LUNO") user := testSecrets.MustGetField("LUNO_USER") inactiveSecret := testSecrets.MustGetField("LUNO_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 luno secret %s within luno %s", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Luno, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a luno secret %s within luno %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Luno, 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("Luno.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Luno.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) } } }) } } trufflehog-3.60.0/pkg/detectors/m3o/000077500000000000000000000000001451332102400172135ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/m3o/m3o.go000066400000000000000000000043601451332102400202430ustar00rootroot00000000000000package m3o import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"m3o"}) + `\b([0-9A-Za-z]{48})\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{"m3o"} } // FromData will find and optionally verify M3o 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_M3o, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{ "days": 2, "location": "London" }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.m3o.com/v1/weather/Forecast", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_M3o } trufflehog-3.60.0/pkg/detectors/m3o/m3o_test.go000066400000000000000000000054101451332102400212770ustar00rootroot00000000000000//go:build detectors // +build detectors package m3o import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestM3o_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("M3O") inactiveSecret := testSecrets.MustGetField("M3O_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 m3o secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_M3o, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a m3o 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_M3o, 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("M3o.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("M3o.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) } } }) } } trufflehog-3.60.0/pkg/detectors/macaddress/000077500000000000000000000000001451332102400206235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/macaddress/macaddress.go000066400000000000000000000041771451332102400232710ustar00rootroot00000000000000package macaddress import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"macaddress"}) + `\b([a-zA-Z0-9_]{32})\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{"macaddress"} } // FromData will find and optionally verify Macaddress 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Macaddress, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.macaddress.io/v1?apiKey=%s&output=json&search=44:38:39:ff:ef:51", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Macaddress } trufflehog-3.60.0/pkg/detectors/macaddress/macaddress_test.go000066400000000000000000000055161451332102400243260ustar00rootroot00000000000000//go:build detectors // +build detectors package macaddress import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMacaddress_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("MACADDRESS") inactiveSecret := testSecrets.MustGetField("MACADDRESS_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 macaddress secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Macaddress, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a macaddress 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_Macaddress, 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("Macaddress.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Macaddress.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) } } }) } } trufflehog-3.60.0/pkg/detectors/madkudu/000077500000000000000000000000001451332102400201475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/madkudu/madkudu.go000066400000000000000000000041031451332102400221260ustar00rootroot00000000000000package madkudu import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"madkudu"}) + `\b([0-9a-f]{32})\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{"madkudu"} } // FromData will find and optionally verify MadKudu 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MadKudu, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.madkudu.com/v1/ping", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MadKudu } trufflehog-3.60.0/pkg/detectors/madkudu/madkudu_test.go000066400000000000000000000053721451332102400231760ustar00rootroot00000000000000//go:build detectors // +build detectors package madkudu import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMadKudu_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MADKUDU") inactiveSecret := testSecrets.MustGetField("MADKUDU_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 madkudu secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MadKudu, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a madkudu 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_MadKudu, 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("MadKudu.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MadKudu.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/magicbell/000077500000000000000000000000001451332102400204345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/magicbell/magicbell.go000066400000000000000000000053251451332102400227070ustar00rootroot00000000000000package magicbell import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"magicbell"}) + `\b([a-zA-Z-0-9]{40})\b`) emailPat = regexp.MustCompile(`\b([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)\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{"magicbell"} } // FromData will find and optionally verify MagicBell 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) apiKeyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) emailMatches := emailPat.FindAllStringSubmatch(dataStr, -1) for _, keyMatch := range apiKeyMatches { if len(keyMatch) != 2 { continue } apiKeyRes := strings.TrimSpace(keyMatch[1]) for _, emailMatch := range emailMatches { if len(emailMatch) != 2 { continue } emailRes := strings.TrimSpace(emailMatch[1]) if detectors.IsKnownFalsePositive(apiKeyRes, detectors.DefaultFalsePositives, true) { // wait- (apiKeyRes, email) might be false positive does not mean (apiKeyRes, another_email) is ? continue } s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MagicBell, Raw: []byte(apiKeyRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.magicbell.com/notification_preferences", nil) if err != nil { continue } req.Header.Add("X-MAGICBELL-API-KEY", apiKeyRes) req.Header.Add("X-MAGICBELL-USER-EMAIL", emailRes) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(apiKeyRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MagicBell } trufflehog-3.60.0/pkg/detectors/magicbell/magicbell_test.go000066400000000000000000000056651451332102400237550ustar00rootroot00000000000000//go:build detectors // +build detectors package magicbell import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMagicBell_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAGICBELL") inactiveSecret := testSecrets.MustGetField("MAGICBELL_INACTIVE") userEmail := testSecrets.MustGetField("MAGICBELL_USER_EMAIL") 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 magicbell secret %s with email %s within", secret, userEmail)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MagicBell, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a magicbell secret %s with email %s within but not valid", inactiveSecret, userEmail)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MagicBell, 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("MagicBell.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MagicBell.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) } } }) } } trufflehog-3.60.0/pkg/detectors/magnetic/000077500000000000000000000000001451332102400203045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/magnetic/magnetic.go000066400000000000000000000042071451332102400224250ustar00rootroot00000000000000package magnetic import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"magnetic"}) + `\b([0-9Aa-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"magnetic"} } // FromData will find and optionally verify Magnetic 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Magnetic, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.magnetichq.com/Magnetic/rest/accountsAPI/itemTypes?token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Magnetic } trufflehog-3.60.0/pkg/detectors/magnetic/magnetic_test.go000066400000000000000000000055001451332102400234610ustar00rootroot00000000000000//go:build detectors // +build detectors package magnetic import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMagnetic_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAGNETIC_TOKEN") inactiveSecret := testSecrets.MustGetField("MAGNETIC_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 magnetic secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Magnetic, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a magnetic 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_Magnetic, 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("Magnetic.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Magnetic.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailboxlayer/000077500000000000000000000000001451332102400212055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailboxlayer/mailboxlayer.go000066400000000000000000000047161451332102400242340ustar00rootroot00000000000000package mailboxlayer import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mailboxlayer"}) + `\b([a-z0-9]{32})\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{"mailboxlayer"} } // FromData will find and optionally verify Mailboxplayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mailboxlayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://apilayer.net/api/check?access_key="+resMatch+"&email=support@email.com", nil) if err != nil { continue } 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) validResponse := strings.Contains(body, `email`) || strings.Contains(body, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) // if client_id and client_secret is valid -> 403 {"error":"invalid_grant","error_description":"Invalid authorization code"} // if invalid -> 401 {"error":"access_denied","error_description":"Unauthorized"} // ingenious! if validResponse { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mailboxlayer } trufflehog-3.60.0/pkg/detectors/mailboxlayer/mailboxlayer_test.go000066400000000000000000000055551451332102400252750ustar00rootroot00000000000000//go:build detectors // +build detectors package mailboxlayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailboxplayer_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILBOXPLAYER_TOKEN") inactiveSecret := testSecrets.MustGetField("MAILBOXPLAYER_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 mailboxlayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailboxlayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailboxlayer 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_Mailboxlayer, 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("Mailboxplayer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mailboxplayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailchimp/000077500000000000000000000000001451332102400204605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailchimp/mailchimp.go000066400000000000000000000042621451332102400227560ustar00rootroot00000000000000package mailchimp import ( "context" "fmt" "net/http" "regexp" "strings" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/common" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(`[0-9a-f]{32}-us[0-9]{1,2}`) ) // 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{"-us"} } // FromData will find and optionally verify Mailchimp 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) //pretty standard regex match matches := keyPat.FindAllString(dataStr, -1) for _, match := range matches { s := detectors.Result{ DetectorType: detectorspb.DetectorType_Mailchimp, Raw: []byte(match), } s.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/mailchimp/", } if verify { datacenter := strings.Split(match, "-")[1] // https://mailchimp.com/developer/guides/marketing-api-conventions/ req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.api.mailchimp.com/3.0/", datacenter), nil) if err != nil { continue } req.SetBasicAuth("anystring", match) req.Header.Add("accept", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(match, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mailchimp } trufflehog-3.60.0/pkg/detectors/mailchimp/mailchimp_test.go000066400000000000000000000053451451332102400240200ustar00rootroot00000000000000//go:build detectors // +build detectors package mailchimp import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailchimp_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILCHIMP") secretInactive := testSecrets.MustGetField("MAILCHIMP_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 mailchimp secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailchimp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailchimp secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailchimp, 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("Mailchimp.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mailchimp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailerlite/000077500000000000000000000000001451332102400206445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailerlite/mailerlite.go000066400000000000000000000041621451332102400233250ustar00rootroot00000000000000package mailerlite import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mailerlite"}) + `\b([a-z0-9]{32})\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{"mailerlite"} } // FromData will find and optionally verify Mailerlite 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mailerlite, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mailerlite.com/api/v2/campaigns", nil) if err != nil { continue } req.Header.Add("X-MailerLite-ApiKey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mailerlite } trufflehog-3.60.0/pkg/detectors/mailerlite/mailerlite_test.go000066400000000000000000000055241451332102400243670ustar00rootroot00000000000000//go:build detectors // +build detectors package mailerlite import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailerlite_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILERLITE_TOKEN") inactiveSecret := testSecrets.MustGetField("MAILERLITE_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 mailerlite secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailerlite, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailerlite 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_Mailerlite, 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("Mailerlite.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mailerlite.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailgun/000077500000000000000000000000001451332102400201515ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailgun/mailgun.go000066400000000000000000000045351451332102400221430ustar00rootroot00000000000000package mailgun import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() tokenPats = map[string]*regexp.Regexp{ "Original MailGun Token": regexp.MustCompile(detectors.PrefixRegex([]string{"mailgun"}) + `\b([a-zA-Z-0-9]{72})\b`), "Key-MailGun Token": regexp.MustCompile(`\b(key-[a-z0-9]{32})\b`), "Hex MailGun Token": regexp.MustCompile(`\b([a-f0-9]{32}-[a-f0-9]{8}-[a-f0-9]{8})\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{"mailgun"} } // FromData will find and optionally verify Mailgun 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) for _, tokenPat := range tokenPats { matches := tokenPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mailgun, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mailgun.net/v3/domains", nil) if err != nil { continue } // If resMatch has "key" prefix, use it as the username for basic auth. if strings.HasPrefix(resMatch, "key-") { req.SetBasicAuth("api", resMatch) } else { req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mailgun } trufflehog-3.60.0/pkg/detectors/mailgun/mailgun_test.go000066400000000000000000000074461451332102400232060ustar00rootroot00000000000000//go:build detectors // +build detectors package mailgun import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailgun_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILGUN_TOKEN") inactiveSecret := testSecrets.MustGetField("MAILGUN_INACTIVE") keyDashSecret := testSecrets.MustGetField("NEW_MAILGUN_TOKEN_ACTIVE") inactiveHexEncodedSecret := testSecrets.MustGetField("NEW_MAILGUN_TOKEN_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 mailgun secret %s within https://api.mailgun.net/v3/domains", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailgun, Verified: true, }, }, wantErr: false, }, { name: "found, verified key-dash mailgun pattern token", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailgun secret %s within https://api.mailgun.net/v3/domains", keyDashSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailgun, Verified: true, }, }, wantErr: false, }, { name: "found, unverified key-dash mailgun pattern token", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailgun secret %s within https://api.mailgun.net/v3/domains", inactiveHexEncodedSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailgun, Verified: false, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailgun secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailgun, 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("Mailgun.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mailgun.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailjetbasicauth/000077500000000000000000000000001451332102400220265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailjetbasicauth/mailjetbasicauth.go000066400000000000000000000036341451332102400256740ustar00rootroot00000000000000package mailjetbasicauth import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mailjet"}) + `\b([A-Za-z0-9]{87}\=)`) ) // 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{"mailjet"} } // FromData will find and optionally verify MailJetBasicAuth 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MailJetBasicAuth, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mailjet.com/v3/REST/message", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MailJetBasicAuth } trufflehog-3.60.0/pkg/detectors/mailjetbasicauth/mailjetbasicauth_test.go000066400000000000000000000055231451332102400267320ustar00rootroot00000000000000//go:build detectors // +build detectors package mailjetbasicauth import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailJetBasicAuth_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILJETBASICAUTH_TOKEN") inactiveSecret := testSecrets.MustGetField("MAILJETBASICAUTH_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 mailjetbasicauth secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MailJetBasicAuth, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailjetbasicauth secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MailJetBasicAuth, 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("MailJetBasicAuth.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MailJetBasicAuth.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailjetsms/000077500000000000000000000000001451332102400206655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailjetsms/mailjetsms.go000066400000000000000000000037071451332102400233730ustar00rootroot00000000000000package mailjetsms import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mailjet"}) + `\b([A-Za-z0-9]{32})\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{"mailjet"} } // FromData will find and optionally verify MailJetSMS 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MailJetSMS, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mailjet.com/v4/sms", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.mailjetsms+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MailJetSMS } trufflehog-3.60.0/pkg/detectors/mailjetsms/mailjetsms_test.go000066400000000000000000000054271451332102400244330ustar00rootroot00000000000000//go:build detectors // +build detectors package mailjetsms import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailJetSMS_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILJETSMS_TOKEN") inactiveSecret := testSecrets.MustGetField("MAILJETSMS_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 mailjetsms secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MailJetSMS, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailjetsms secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MailJetSMS, 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("MailJetSMS.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MailJetSMS.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailmodo/000077500000000000000000000000001451332102400203165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailmodo/mailmodo.go000066400000000000000000000043041451332102400224470ustar00rootroot00000000000000package mailmodo import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mailmodo"}) + `\b([A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7})\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{"mailmodo"} } // FromData will find and optionally verify Mailmodo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mailmodo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mailmodo.com/api/v1/campaigns?type=CONTACT_LIST", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("mmApiKey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mailmodo } trufflehog-3.60.0/pkg/detectors/mailmodo/mailmodo_test.go000066400000000000000000000055001451332102400235050ustar00rootroot00000000000000//go:build detectors // +build detectors package mailmodo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailmodo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILMODO_TOKEN") inactiveSecret := testSecrets.MustGetField("MAILMODO_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 mailmodo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailmodo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailmodo 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_Mailmodo, 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("Mailmodo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mailmodo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mailsac/000077500000000000000000000000001451332102400201265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mailsac/mailsac.go000066400000000000000000000042121451332102400220650ustar00rootroot00000000000000package mailsac import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mailsac"}) + `\b(k_[0-9A-Za-z]{36,})\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{"mailsac"} } // FromData will find and optionally verify Mailsac 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mailsac, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://mailsac.com/api/addresses", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Mailsac-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mailsac } trufflehog-3.60.0/pkg/detectors/mailsac/mailsac_test.go000066400000000000000000000054601451332102400231320ustar00rootroot00000000000000//go:build detectors // +build detectors package mailsac import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMailsac_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAILSAC") inactiveSecret := testSecrets.MustGetField("MAILSAC_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 mailsac secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mailsac, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mailsac 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_Mailsac, 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("Mailsac.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mailsac.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mandrill/000077500000000000000000000000001451332102400203175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mandrill/mandrill.go000066400000000000000000000043011451332102400224460ustar00rootroot00000000000000package mandrill import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mandrill"}) + `\b([A-Za-z0-9_-]{22})\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{"mandrill"} } // FromData will find and optionally verify Mandrill 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mandrill, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`{"key": "%s"}`, resMatch)) req, err := http.NewRequestWithContext(ctx, "POST", "https://mandrillapp.com/api/1.0/users/info", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mandrill } trufflehog-3.60.0/pkg/detectors/mandrill/mandrill_test.go000066400000000000000000000055001451332102400235070ustar00rootroot00000000000000//go:build detectors // +build detectors package mandrill import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMandrill_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MANDRILL_TOKEN") inactiveSecret := testSecrets.MustGetField("MANDRILL_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 mandrill secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mandrill, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mandrill 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_Mandrill, 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("Mandrill.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mandrill.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) } } }) } } trufflehog-3.60.0/pkg/detectors/manifest/000077500000000000000000000000001451332102400203235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/manifest/manifest.go000066400000000000000000000041101451332102400224540ustar00rootroot00000000000000package manifest import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"manifest"}) + `\b([a-zA-z0-9]{32})\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{"manifest"} } // FromData will find and optionally verify Manifest 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Manifest, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.manifest.ly/api/v1/checklists/?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Manifest } trufflehog-3.60.0/pkg/detectors/manifest/manifest_test.go000066400000000000000000000054721451332102400235270ustar00rootroot00000000000000//go:build detectors // +build detectors package manifest import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestManifest_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MANIFEST") inactiveSecret := testSecrets.MustGetField("MANIFEST_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 manifest secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Manifest, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a manifest 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_Manifest, 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("Manifest.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Manifest.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mapbox/000077500000000000000000000000001451332102400200035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mapbox/mapbox.go000066400000000000000000000040621451332102400216220ustar00rootroot00000000000000package mapbox import ( "context" // "log" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() idPat = regexp.MustCompile(`([a-zA-Z-0-9]{4,32})`) keyPat = regexp.MustCompile(`\b(sk\.[a-zA-Z-0-9\.]{80,240})\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{"mapbox"} } // FromData will find and optionally verify MapBox 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { resMatch := strings.TrimSpace(match[1]) for i, idMatch := range idMatches { if i == 11 { if len(idMatch) != 2 { continue } resId := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MapBox, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mapbox.com/tokens/v2/"+resId+"?access_token="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MapBox } trufflehog-3.60.0/pkg/detectors/mapbox/mapbox_test.go000066400000000000000000000055661451332102400226730ustar00rootroot00000000000000//go:build detectors // +build detectors package mapbox import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMapBox_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAPBOX_API_TOKEN") inactiveSecret := testSecrets.MustGetField("MAPBOX_API_INACTIVE") id := testSecrets.MustGetField("MAPBOX_USER") 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 mapbox secret %s within https://api.mapbox.com/tokens/v2/%s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MapBox, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mapbox secret %s within https://api.mapbox.com/tokens/v2/%s but unverified", inactiveSecret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MapBox, 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("MapBox.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MapBox.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mapquest/000077500000000000000000000000001451332102400203545ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mapquest/mapquest.go000066400000000000000000000041241451332102400225430ustar00rootroot00000000000000package mapquest import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mapquest"}) + `\b([0-9A-Za-z]{32})\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{"mapquest"} } // FromData will find and optionally verify Mapquest 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mapquest, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.mapquestapi.com/datamanager/v2/get-column-types?key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mapquest } trufflehog-3.60.0/pkg/detectors/mapquest/mapquest_test.go000066400000000000000000000054721451332102400236110ustar00rootroot00000000000000//go:build detectors // +build detectors package mapquest import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMapquest_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("MAPQUEST") inactiveSecret := testSecrets.MustGetField("MAPQUEST_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 mapquest secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mapquest, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mapquest 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_Mapquest, 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("Mapquest.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mapquest.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) } } }) } } trufflehog-3.60.0/pkg/detectors/marketstack/000077500000000000000000000000001451332102400210265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/marketstack/marketstack.go000066400000000000000000000041651451332102400236740ustar00rootroot00000000000000package marketstack import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"marketstack"}) + `\b([a-z0-9]{32})\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{"marketstack"} } // FromData will find and optionally verify Marketstack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Marketstack, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.marketstack.com/v1/eod?access_key=%s&symbols=AAPL", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Marketstack } trufflehog-3.60.0/pkg/detectors/marketstack/marketstack_test.go000066400000000000000000000055301451332102400247300ustar00rootroot00000000000000//go:build detectors // +build detectors package marketstack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMarketstack_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("MARKETSTACK") inactiveSecret := testSecrets.MustGetField("MARKETSTACK_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 marketstack secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Marketstack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a marketstack 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_Marketstack, 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("Marketstack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Marketstack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mattermostpersonaltoken/000077500000000000000000000000001451332102400235215ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mattermostpersonaltoken/mattermostpersonaltoken.go000066400000000000000000000051351451332102400310600ustar00rootroot00000000000000package mattermostpersonaltoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mattermost"}) + `\b([a-z0-9]{26})\b`) serverPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mattermost"}) + `\b([A-Za-z0-9-_]{1,}.cloud.mattermost.com)\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{"mattermost"} } // FromData will find and optionally verify MattermostPersonalToken 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) serverMatches := serverPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, serverMatch := range serverMatches { if len(serverMatch) != 2 { continue } serverRes := strings.TrimSpace(serverMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MattermostPersonalToken, Raw: []byte(resMatch), RawV2: []byte(resMatch + serverRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://"+serverRes+"/api/v4/users/stats", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MattermostPersonalToken } trufflehog-3.60.0/pkg/detectors/mattermostpersonaltoken/mattermostpersonaltoken_test.go000066400000000000000000000061271451332102400321210ustar00rootroot00000000000000//go:build detectors // +build detectors package mattermostpersonaltoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMattermostPersonalToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MATTERMOSTPERSONALTOKEN_TOKEN") inactiveSecret := testSecrets.MustGetField("MATTERMOSTPERSONALTOKEN_INACTIVE") server := testSecrets.MustGetField("MATTERMOSTPERSONALTOKEN_SERVER") 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 mattermost secret %s within mattermost server %s", secret, server)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MattermostPersonalToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mattermost secret %s within mattermost server %s but not valid", inactiveSecret, "test323561.cloud.mattermost.com")), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MattermostPersonalToken, 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("MattermostPersonalToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MattermostPersonalToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mavenlink/000077500000000000000000000000001451332102400205015ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mavenlink/mavenlink.go000066400000000000000000000042141451332102400230150ustar00rootroot00000000000000package mavenlink import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mavenlink"}) + `\b([0-9a-z]{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{"mavenlink"} } // FromData will find and optionally verify Mavenlink 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mavenlink, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mavenlink.com/api/v1/workspaces.json", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mavenlink } trufflehog-3.60.0/pkg/detectors/mavenlink/mavenlink_test.go000066400000000000000000000055041451332102400240570ustar00rootroot00000000000000//go:build detectors // +build detectors package mavenlink import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMavenlink_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("MAVENLINK") inactiveSecret := testSecrets.MustGetField("MAVENLINK_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 mavenlink secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mavenlink, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mavenlink 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_Mavenlink, 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("Mavenlink.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mavenlink.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) } } }) } } trufflehog-3.60.0/pkg/detectors/maxmindlicense/000077500000000000000000000000001451332102400215155ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/maxmindlicense/maxmindlicense.go000066400000000000000000000042671451332102400250550ustar00rootroot00000000000000package maxmindlicense import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"maxmind", "geoip"}) + `\b([0-9]{2,7})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"maxmind", "geoip"}) + `\b([0-9A-Za-z]{16})\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{"maxmind", "geoip"} } // FromData will find and optionally verify MaxMindLicense 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) keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, keyMatch := range keyMatches { keyRes := strings.TrimSpace(keyMatch[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } idRes := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MaxMindLicense, Redacted: idRes, Raw: []byte(keyRes), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://geoip.maxmind.com/geoip/v2.1/country/8.8.8.8", nil) if err != nil { continue } req.SetBasicAuth(idRes, keyRes) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(keyRes, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MaxMindLicense } trufflehog-3.60.0/pkg/detectors/maxmindlicense/maxmindlicense_test.go000066400000000000000000000056761451332102400261210ustar00rootroot00000000000000//go:build detectors // +build detectors package maxmindlicense import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMaxMindLicense_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MAXMIND_LICENSE") user := testSecrets.MustGetField("MAXMIND_USER") inactiveSecret := testSecrets.MustGetField("MAXMIND_LICENSE_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 geoip secret %s within with maxmind user %s", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MaxMindLicense, Redacted: "510124", Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a maxmind secret %s within with maxmind user %s", inactiveSecret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MaxMindLicense, Redacted: "510124", 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("MaxMindLicense.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MaxMindLicense.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) } } }) } } trufflehog-3.60.0/pkg/detectors/meaningcloud/000077500000000000000000000000001451332102400211625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/meaningcloud/meaningcloud.go000066400000000000000000000051671451332102400241670ustar00rootroot00000000000000package meaningcloud import ( "bytes" "context" "io" "mime/multipart" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"meaningcloud"}) + `\b([a-z0-9]{32})\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{"meaningcloud"} } // FromData will find and optionally verify MeaningCloud 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MeaningCloud, Raw: []byte(resMatch), } if verify { body := &bytes.Buffer{} writer := multipart.NewWriter(body) fw, err := writer.CreateFormField("key") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader(resMatch)) if err != nil { continue } fw, err = writer.CreateFormField("txt") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader("test")) if err != nil { continue } writer.Close() req, err := http.NewRequestWithContext(ctx, "POST", "https://api.meaningcloud.com/lang-4.0/identification", bytes.NewReader(body.Bytes())) if err != nil { continue } req.Header.Add("Content-Type", writer.FormDataContentType()) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MeaningCloud } trufflehog-3.60.0/pkg/detectors/meaningcloud/meaningcloud_test.go000066400000000000000000000055421451332102400252230ustar00rootroot00000000000000//go:build detectors // +build detectors package meaningcloud import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMeaningCloud_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("MEANINGCLOUD") inactiveSecret := testSecrets.MustGetField("MEANINGCLOUD_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 meaningcloud secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MeaningCloud, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a meaningcloud 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_MeaningCloud, 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("MeaningCloud.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MeaningCloud.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mediastack/000077500000000000000000000000001451332102400206225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mediastack/mediastack.go000066400000000000000000000042451451332102400232630ustar00rootroot00000000000000package mediastack import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mediastack"}) + `\b([a-z0-9]{32})\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{"mediastack"} } // FromData will find and optionally verify MediaStack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MediaStack, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.mediastack.com/v1/news?access_key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MediaStack } trufflehog-3.60.0/pkg/detectors/mediastack/mediastack_test.go000066400000000000000000000055161451332102400243240ustar00rootroot00000000000000//go:build detectors // +build detectors package mediastack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMediaStack_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("MEDIASTACK") inactiveSecret := testSecrets.MustGetField("MEDIASTACK_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 mediastack secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MediaStack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mediastack 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_MediaStack, 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("MediaStack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MediaStack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/meistertask/000077500000000000000000000000001451332102400210505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/meistertask/meistertask.go000066400000000000000000000042231451332102400237330ustar00rootroot00000000000000package meistertask import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"meistertask"}) + `\b([a-zA-Z0-9]{43})\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{"meistertask"} } // FromData will find and optionally verify Meistertask 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Meistertask, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.meistertask.com/api/projects", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Meistertask } trufflehog-3.60.0/pkg/detectors/meistertask/meistertask_test.go000066400000000000000000000055301451332102400247740ustar00rootroot00000000000000//go:build detectors // +build detectors package meistertask import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMeistertask_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MEISTERTASK") inactiveSecret := testSecrets.MustGetField("MEISTERTASK_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 meistertask secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Meistertask, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a meistertask 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_Meistertask, 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("Meistertask.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Meistertask.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mesibo/000077500000000000000000000000001451332102400177735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mesibo/mesibo.go000066400000000000000000000040211451332102400215750ustar00rootroot00000000000000package mesibo import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mesibo"}) + `\b([0-9A-Za-z]{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{"mesibo"} } // FromData will find and optionally verify Mesibo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mesibo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mesibo.com/api.php?op=useradd&token="+resMatch, nil) if err != nil { continue } 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, "AUTHFAIL") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mesibo } trufflehog-3.60.0/pkg/detectors/mesibo/mesibo_test.go000066400000000000000000000054461451332102400226500ustar00rootroot00000000000000//go:build detectors // +build detectors package mesibo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMesibo_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("MESIBO") inactiveSecret := testSecrets.MustGetField("MESIBO_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 mesibo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mesibo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mesibo 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_Mesibo, 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("Mesibo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mesibo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/messagebird/000077500000000000000000000000001451332102400210025ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/messagebird/messagebird.go000066400000000000000000000036241451332102400236230ustar00rootroot00000000000000package messagebird import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"messagebird"}) + `\b([A-Za-z0-9_-]{25})\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{"messagebird"} } // FromData will find and optionally verify MessageBird 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MessageBird, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://rest.messagebird.com/messages", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("AccessKey %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MessageBird } trufflehog-3.60.0/pkg/detectors/messagebird/messagebird_test.go000066400000000000000000000054411451332102400246610ustar00rootroot00000000000000//go:build detectors // +build detectors package messagebird import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMessageBird_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MESSAGEBIRD_TOKEN") inactiveSecret := testSecrets.MustGetField("MESSAGEBIRD_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 messagebird secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MessageBird, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a messagebird secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MessageBird, 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("MessageBird.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MessageBird.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) } } }) } } trufflehog-3.60.0/pkg/detectors/metaapi/000077500000000000000000000000001451332102400201355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/metaapi/metaapi.go000066400000000000000000000052711451332102400221110ustar00rootroot00000000000000package metaapi import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"metaapi", "meta-api"}) + `\b([0-9a-f]{64})\b`) spellPat = regexp.MustCompile(detectors.PrefixRegex([]string{"metaapi", "meta-api"}) + `\b([0-9a-f]{24})\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{"metaapi", "meta-api"} } // FromData will find and optionally verify MetaAPI 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) spellMatches := spellPat.FindAllStringSubmatch(dataStr, -1) for _, spellMatch := range spellMatches { if len(spellMatch) != 2 { continue } resSpellMatch := strings.TrimSpace(spellMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MetaAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", fmt.Sprintf("https://api.meta-api.io/api/spells/%s/runSync", resSpellMatch), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("apikey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() body, errBody := io.ReadAll(res.Body) if errBody == nil { bodyStr := string(body) if res.StatusCode >= 200 && res.StatusCode < 300 && strings.Contains(bodyStr, `"success":true`) { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MetaAPI } trufflehog-3.60.0/pkg/detectors/metaapi/metaapi_test.go000066400000000000000000000055541451332102400231540ustar00rootroot00000000000000//go:build detectors // +build detectors package metaapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMetaAPI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("METAAPI") inactiveSecret := testSecrets.MustGetField("METAAPI_INACTIVE") spell := testSecrets.MustGetField("METAAPI_SPELL") 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 meta-api spell %s with meta-api secret %s within", spell, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MetaAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a meta-api spell %s with meta-api secret %s within but not valid", spell, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MetaAPI, 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("MetaAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MetaAPI.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/metrilo/000077500000000000000000000000001451332102400201705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/metrilo/metrilo.go000066400000000000000000000045451451332102400222020ustar00rootroot00000000000000package metrilo import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"metrilo"}) + `\b([a-z0-9]{16})\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{"metrilo"} } // FromData will find and optionally verify Metrilo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Metrilo, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"time":1518004715732,"token": "` + resMatch + `" ,"platform":"Wordpress 4.2.7 / Woocommerce 3.5","pluginVersion":"1.1.0","params":{"id":"12","name":"Clothing","url":"https://dummysite.com"}}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://trk.mtrl.me/category", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Length", `0`) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Metrilo } trufflehog-3.60.0/pkg/detectors/metrilo/metrilo_test.go000066400000000000000000000054601451332102400232360ustar00rootroot00000000000000//go:build detectors // +build detectors package metrilo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMetrilo_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("METRILO") inactiveSecret := testSecrets.MustGetField("METRILO_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 metrilo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Metrilo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a metrilo 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_Metrilo, 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("Metrilo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Metrilo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/microsoftteamswebhook/000077500000000000000000000000001451332102400231335ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook.go000066400000000000000000000062541451332102400301070ustar00rootroot00000000000000package microsoftteamswebhook import ( "context" "fmt" "io" "net/http" "regexp" "strings" "time" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( defaultClient = common.SaneHttpClientTimeOut(5 * time.Second) // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`(https:\/\/[a-zA-Z-0-9]+\.webhook\.office\.com\/webhookb2\/[a-zA-Z-0-9]{8}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{12}\@[a-zA-Z-0-9]{8}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{12}\/IncomingWebhook\/[a-zA-Z-0-9]{32}\/[a-zA-Z-0-9]{8}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{4}-[a-zA-Z-0-9]{12})`) ) // 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{"webhook.office.com"} } // FromData will find and optionally verify MicrosoftTeamsWebhook 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MicrosoftTeamsWebhook, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/microsoftteams/", } if verify { client := s.client if client == nil { client = defaultClient } isVerified, verificationErr := verifyWebhook(ctx, client, resMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, false) { continue } results = append(results, s1) } return results, nil } func verifyWebhook(ctx context.Context, client *http.Client, webhookURL string) (bool, error) { payload := strings.NewReader(`{'text':''}`) req, err := http.NewRequestWithContext(ctx, "POST", webhookURL, 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 res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { return false, err } switch { case res.StatusCode == http.StatusBadRequest: if strings.Contains(string(body), "Text is required") { return true, nil } return false, fmt.Errorf("unexpected response body: %s", string(body)) case res.StatusCode < 200 || res.StatusCode >= 500: return false, fmt.Errorf("unexpected HTTP response status: %d", res.StatusCode) default: return false, nil } } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MicrosoftTeamsWebhook } trufflehog-3.60.0/pkg/detectors/microsoftteamswebhook/microsoftteamswebhook_test.go000066400000000000000000000115311451332102400311400ustar00rootroot00000000000000//go:build detectors // +build detectors package microsoftteamswebhook 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 TestMicrosoftTeamsWebhook_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MICROSOFT_TEAMS_WEBHOOK") inactiveSecret := testSecrets.MustGetField("MICROSOFT_TEAMS_WEBHOOK_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a microsoftteamswebhook secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MicrosoftTeamsWebhook, Verified: true, }, }, wantErr: false, }, { name: "found, verified but unexpected response body", s: Scanner{client: common.ConstantResponseHttpClient(400, "nope")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a microsoftteamswebhook secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MicrosoftTeamsWebhook, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(500, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a microsoftteamswebhook secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MicrosoftTeamsWebhook, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a microsoftteamswebhook secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MicrosoftTeamsWebhook, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a microsoftteamswebhook 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_MicrosoftTeamsWebhook, 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("MicrosoftTeamsWebhook.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Errorf("MicrosoftTeamsWebhook.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError, tt.wantVerificationErr) return } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("MicrosoftTeamsWebhook.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mindmeister/000077500000000000000000000000001451332102400210355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mindmeister/mindmeister.go000066400000000000000000000046711451332102400237140ustar00rootroot00000000000000package mindmeister import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mindmeister"}) + `\b([a-zA-Z0-9]{43})\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{"mindmeister"} } // FromData will find and optionally verify Mindmeister 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mindmeister, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://www.mindmeister.com/services/rest/oauth2?method=mm.maps.getList", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `"stat":"ok"`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mindmeister } trufflehog-3.60.0/pkg/detectors/mindmeister/mindmeister_test.go000066400000000000000000000055301451332102400247460ustar00rootroot00000000000000//go:build detectors // +build detectors package mindmeister import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMindmeister_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MINDMEISTER") inactiveSecret := testSecrets.MustGetField("MINDMEISTER_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 mindmeister secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mindmeister, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mindmeister 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_Mindmeister, 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("Mindmeister.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mindmeister.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) } } }) } } trufflehog-3.60.0/pkg/detectors/miro/000077500000000000000000000000001451332102400174635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/miro/miro.go000066400000000000000000000042211451332102400207570ustar00rootroot00000000000000package miro import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"miro"}) + `\b([0-9a-zA-Z]{27})\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{"miro"} } // FromData will find and optionally verify Miro 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Miro, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.miro.com/v1/users/me", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Miro } trufflehog-3.60.0/pkg/detectors/miro/miro_test.go000066400000000000000000000054221451332102400220220ustar00rootroot00000000000000//go:build detectors // +build detectors package miro import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMiro_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MIRO") inactiveSecret := testSecrets.MustGetField("MIRO_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 miro secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Miro, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a miro 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_Miro, 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("Miro.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Miro.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mite/000077500000000000000000000000001451332102400174535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mite/mite.go000066400000000000000000000046331451332102400207460ustar00rootroot00000000000000package mite import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mite"}) + `\b([0-9a-z]{16})\b`) urlPat = regexp.MustCompile(`\b([0-9a-z-]{1,}.mite.yo.lk)\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{"mite"} } // FromData will find and optionally verify Mite 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, urlMatch := range urlMatches { if len(urlMatch) != 2 { continue } resURL := strings.TrimSpace(urlMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mite, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/account.json", resURL), nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-MiteApiKey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mite } trufflehog-3.60.0/pkg/detectors/mite/mite_test.go000066400000000000000000000055171451332102400220070ustar00rootroot00000000000000//go:build detectors // +build detectors package mite import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMite_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MITE") url := testSecrets.MustGetField("MITE_URL") inactiveSecret := testSecrets.MustGetField("MITE_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 mite secret %s within %s", secret, url)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mite, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mite secret %s within %s but not valid", inactiveSecret, url)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mite, 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("Mite.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mite.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mixmax/000077500000000000000000000000001451332102400200205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mixmax/mixmax.go000066400000000000000000000041171451332102400216550ustar00rootroot00000000000000package mixmax import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mixmax"}) + `\b([a-zA-Z0-9_-]{36})\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{"mixmax"} } // FromData will find and optionally verify Mixmax 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mixmax, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mixmax.com/v1/users/me", nil) if err != nil { continue } req.Header.Add("X-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 } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mixmax } trufflehog-3.60.0/pkg/detectors/mixmax/mixmax_test.go000066400000000000000000000054461451332102400227220ustar00rootroot00000000000000//go:build detectors // +build detectors package mixmax import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMixmax_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MIXMAX") inactiveSecret := testSecrets.MustGetField("MIXMAX_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 mixmax secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mixmax, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mixmax 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_Mixmax, 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("Mixmax.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mixmax.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mixpanel/000077500000000000000000000000001451332102400203325ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mixpanel/mixpanel.go000066400000000000000000000046541451332102400225070ustar00rootroot00000000000000package mixpanel import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mixpanel"}) + `\b([a-zA-Z0-9-]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mixpanel"}) + `\b([a-zA-Z0-9.-]{30,40})\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{"mixpanel"} } // FromData will find and optionally verify Mixpanel 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mixpanel, Raw: []byte(tokenPatMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://mixpanel.com/api/app/me", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mixpanel } trufflehog-3.60.0/pkg/detectors/mixpanel/mixpanel_test.go000066400000000000000000000056131451332102400235420ustar00rootroot00000000000000//go:build detectors // +build detectors package mixpanel import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMixpanel_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("MIXPANEL") id := testSecrets.MustGetField("MIXPANEL_ID") inactiveSecret := testSecrets.MustGetField("MIXPANEL_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 mixpanel secret %s within mixpanel %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mixpanel, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mixpanel secret %s within mixpanel %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mixpanel, 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("Mixpanel.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mixpanel.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mockaroo/000077500000000000000000000000001451332102400203275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mockaroo/mockaroo.go000066400000000000000000000045741451332102400225020ustar00rootroot00000000000000package mockaroo import ( "context" "encoding/json" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mockaroo"}) + `\b([0-9a-z]{8})\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{"mockaroo"} } // FromData will find and optionally verify Mockaroo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mockaroo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.mockaroo.com/api/types?key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { var t typeRes err = json.NewDecoder(res.Body).Decode(&t) if err == nil && len(t.Types) > 0 { s1.Verified = true } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mockaroo } type typeRes struct { Types []struct { Name string `json:"name"` Type any `json:"type"` Parameters []any `json:"parameters"` } `json:"types"` } trufflehog-3.60.0/pkg/detectors/mockaroo/mockaroo_test.go000066400000000000000000000054721451332102400235370ustar00rootroot00000000000000//go:build detectors // +build detectors package mockaroo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMockaroo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MOCKAROO") inactiveSecret := testSecrets.MustGetField("MOCKAROO_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 mockaroo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mockaroo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mockaroo 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_Mockaroo, 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("Mockaroo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mockaroo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/moderation/000077500000000000000000000000001451332102400206565ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/moderation/moderation.go000066400000000000000000000046001451332102400233460ustar00rootroot00000000000000package moderation import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"moderation"}) + `\b([a-zA-Z0-9]{36}\.[a-zA-Z0-9]{115}\.[a-zA-Z0-9_]{43})\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{"moderation"} } // FromData will find and optionally verify Moderation 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Moderation, Raw: []byte(resMatch), } if verify { timeout := 5 * time.Second client.Timeout = timeout payload := strings.NewReader(`{"value": "This is an english text"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://moderationapi.com/api/v1/analyze/language", payload) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) req.Header.Add("Content-type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Moderation } trufflehog-3.60.0/pkg/detectors/moderation/moderation_test.go000066400000000000000000000054301451332102400244070ustar00rootroot00000000000000//go:build detectors // +build detectors package moderation import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestModeration_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MODERATION") inactiveSecret := testSecrets.MustGetField("MODERATION_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 moderation secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Moderation, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a moderation 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_Moderation, 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("Moderation.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Moderation.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/monday/000077500000000000000000000000001451332102400200045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/monday/monday.go000066400000000000000000000043231451332102400216240ustar00rootroot00000000000000package monday import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"monday"}) + `\b(ey[a-zA-Z0-9_.]{210,225})\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{"monday"} } // FromData will find and optionally verify Monday 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Monday, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"query":"{boards(limit:1){id name}}"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.monday.com/v2", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Monday } trufflehog-3.60.0/pkg/detectors/monday/monday_test.go000066400000000000000000000054541451332102400226710ustar00rootroot00000000000000//go:build detectors // +build detectors package monday import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMonday_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MONDAY_TOKEN") inactiveSecret := testSecrets.MustGetField("MONDAY_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 monday secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Monday, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a monday 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_Monday, 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("Monday.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Monday.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mongodb/000077500000000000000000000000001451332102400201425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mongodb/mongodb.go000066400000000000000000000051531451332102400221220ustar00rootroot00000000000000package mongodb import ( "context" "regexp" "strings" "time" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" "go.mongodb.org/mongo-driver/x/mongo/driver/auth" "go.mongodb.org/mongo-driver/x/mongo/driver/topology" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct { timeout time.Duration // Zero value means "default timeout" } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( defaultTimeout = 2 * time.Second // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(mongodb(\+srv)?://[\S]{3,50}:([\S]{3,88})@[-.%\w\/:]+)\b`) // TODO: Add support for sharded cluster, replica set and Atlas Deployment. ) // 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{"mongodb"} } // FromData will find and optionally verify MongoDB 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) for _, match := range matches { resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MongoDB, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/mongo/", } if verify { timeout := s.timeout if timeout == 0 { timeout = defaultTimeout } err := verifyUri(resMatch, timeout) s1.Verified = err == nil if !isErrDeterminate(err) { s1.VerificationError = err } } results = append(results, s1) } return results, nil } func isErrDeterminate(err error) bool { switch e := err.(type) { case topology.ConnectionError: switch e.Unwrap().(type) { case *auth.Error: return true default: return false } default: return false } } func verifyUri(uri string, timeout time.Duration) error { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri)) if err != nil { return err } defer func() { _ = client.Disconnect(ctx) }() return client.Ping(ctx, readpref.Primary()) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MongoDB } trufflehog-3.60.0/pkg/detectors/mongodb/mongodb_test.go000066400000000000000000000101471451332102400231600ustar00rootroot00000000000000//go:build detectors // +build detectors package mongodb import ( "context" "fmt" "strings" "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 TestMongoDB_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MONGODB_URI") inactiveSecret := testSecrets.MustGetField("MONGODB_INACTIVE_URI") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mongodb secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MongoDB, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mongodb 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_MongoDB, Verified: false, }, }, wantErr: false, }, { name: "found, would be verified but for connection timeout", s: Scanner{timeout: 1 * time.Microsecond}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mongodb secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MongoDB, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, bad host", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mongodb secret %s within", strings.ReplaceAll(secret, ".mongodb.net", ".mongodb.net.bad"))), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MongoDB, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { 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("MongoDB.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]) } got[i].Raw = nil if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationErr = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "VerificationError") if diff := cmp.Diff(tt.want, got, ignoreOpts); diff != "" { t.Errorf("MongoDB.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) } } }) } } trufflehog-3.60.0/pkg/detectors/monkeylearn/000077500000000000000000000000001451332102400210415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/monkeylearn/monkeylearn.go000066400000000000000000000042761451332102400237250ustar00rootroot00000000000000package monkeylearn import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"monkeylearn"}) + `\b([0-9a-f]{40})\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{"monkeylearn"} } // FromData will find and optionally verify MonkeyLearn 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MonkeyLearn, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.monkeylearn.com/v3/classifiers", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MonkeyLearn } trufflehog-3.60.0/pkg/detectors/monkeylearn/monkeylearn_test.go000066400000000000000000000054561451332102400247650ustar00rootroot00000000000000package monkeylearn import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMonkeyLearn_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MONKEYLEARN") inactiveSecret := testSecrets.MustGetField("MONKEYLEARN_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 monkeylearn secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MonkeyLearn, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a monkeylearn 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_MonkeyLearn, 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("MonkeyLearn.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("MonkeyLearn.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) } } }) } } trufflehog-3.60.0/pkg/detectors/moonclerk/000077500000000000000000000000001451332102400205065ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/moonclerk/moonclerk.go000066400000000000000000000042611451332102400230310ustar00rootroot00000000000000package moonclerk import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"moonclerk"}) + `\b([0-9a-z]{32})\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{"moonclerk"} } // FromData will find and optionally verify Moonclerk 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Moonclerk, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.moonclerk.com/forms", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.moonclerk+json;version=1") req.Header.Add("Authorization", "Token token="+resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Moonclerk } trufflehog-3.60.0/pkg/detectors/moonclerk/moonclerk_test.go000066400000000000000000000055061451332102400240730ustar00rootroot00000000000000//go:build detectors // +build detectors package moonclerk import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMoonclerk_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MOONCLERCK") inactiveSecret := testSecrets.MustGetField("MOONCLERCK_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 moonclerk secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Moonclerk, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a moonclerk 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_Moonclerk, 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("Moonclerk.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Moonclerk.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) } } }) } } trufflehog-3.60.0/pkg/detectors/moosend/000077500000000000000000000000001451332102400201615ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/moosend/moosend.go000066400000000000000000000041211451332102400221520ustar00rootroot00000000000000package moosend import ( "context" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"moosend"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"moosend"} } // FromData will find and optionally verify Moosend 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Moosend, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.moosend.com/v3/lists.json?apikey="+resMatch+"&format=json", nil) if err != nil { continue } 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, "CreatedOn") { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Moosend } trufflehog-3.60.0/pkg/detectors/moosend/moosend_test.go000066400000000000000000000054661451332102400232260ustar00rootroot00000000000000//go:build detectors // +build detectors package moosend import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMoosend_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MOOSEND_TOKEN") inactiveSecret := testSecrets.MustGetField("MOOSEND_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 moosend secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Moosend, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a moosend 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_Moosend, 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("Moosend.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Moosend.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) } } }) } } trufflehog-3.60.0/pkg/detectors/moralis/000077500000000000000000000000001451332102400201635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/moralis/moralis.go000066400000000000000000000043121451332102400221600ustar00rootroot00000000000000package moralis import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"moralis"}) + `\b([0-9a-zA-Z]{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{"moralis"} } // FromData will find and optionally verify Moralis 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Moralis, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://deep-index.moralis.io/api/v2/0xd8da6bf26964af9d7eed9e03e53415d37aa96045/nft?chain=eth&format=decimal", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("X-API-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Moralis } trufflehog-3.60.0/pkg/detectors/moralis/moralis_test.go000066400000000000000000000054601451332102400232240ustar00rootroot00000000000000//go:build detectors // +build detectors package moralis import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMoralis_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MORALIS") inactiveSecret := testSecrets.MustGetField("MORALIS_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 moralis secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Moralis, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a moralis 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_Moralis, 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("Moralis.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Moralis.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mrticktock/000077500000000000000000000000001451332102400206675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mrticktock/mrticktock.go000066400000000000000000000053561451332102400234010ustar00rootroot00000000000000package mrticktock import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. emailPat = regexp.MustCompile(`\b([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-z]+)\b`) pwordPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mrticktock"}) + `\b([a-zA-Z0-9!=@#$%()_^]{1,50})`) ) // 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{"mrticktock"} } // FromData will find and optionally verify Mrticktock 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 := emailPat.FindAllStringSubmatch(dataStr, -1) passwordMatches := pwordPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, passwordMatch := range passwordMatches { if len(passwordMatch) != 2 { continue } resPassword := strings.TrimSpace(passwordMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mrticktock, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`email=%s&password=%s`, resMatch, resPassword)) req, err := http.NewRequestWithContext(ctx, "POST", "https://mrticktock.com/app/api/is_timer_active", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 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 res.StatusCode >= 200 && res.StatusCode < 300 && strings.Contains(body, `"errors":[]`) { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mrticktock } trufflehog-3.60.0/pkg/detectors/mrticktock/mrticktock_test.go000066400000000000000000000056261451332102400244400ustar00rootroot00000000000000//go:build detectors // +build detectors package mrticktock import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMrticktock_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } email := testSecrets.MustGetField("SCANNERS_EMAIL") pword := testSecrets.MustGetField("SCANNERS_PASS") inactiveSecret := testSecrets.MustGetField("SCANNERS_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 mrticktock secret %s within %s", email, pword)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mrticktock, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mrticktock secret %s within %s but not valid", email, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mrticktock, 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("Mrticktock.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mrticktock.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) } } }) } } trufflehog-3.60.0/pkg/detectors/mux/000077500000000000000000000000001451332102400173265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/mux/mux.go000066400000000000000000000051031451332102400204650ustar00rootroot00000000000000package mux import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mux"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"mux"}) + `([ \r\n]{0,1}[0-9A-Za-z\/\+]{75}[ \r\n]{1})`) ) // 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{"mux"} } // FromData will find and optionally verify Mux 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Mux, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecretMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.mux.com/video/v1/assets", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.SetBasicAuth(resMatch, resSecretMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Mux } trufflehog-3.60.0/pkg/detectors/mux/mux_test.go000066400000000000000000000056431451332102400215350ustar00rootroot00000000000000//go:build detectors // +build detectors package mux import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMux_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } key := testSecrets.MustGetField("MUX_KEY") inactiveKey := testSecrets.MustGetField("MUX_KEY_INACTIVE") secret := testSecrets.MustGetField("MUX") inactiveSecret := testSecrets.MustGetField("MUX_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 mux key %s with mux secret %s within", key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mux, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a mux key %s with mux secret %s within but not valid", inactiveKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Mux, 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("Mux.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Mux.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) } } }) } } trufflehog-3.60.0/pkg/detectors/myfreshworks/000077500000000000000000000000001451332102400212605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/myfreshworks/myfreshworks.go000066400000000000000000000061411451332102400243540ustar00rootroot00000000000000package myfreshworks import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // 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. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"freshworks"}) + `\b([a-z0-9A-Z-_]{22})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"freshworks"}) + `\b([a-zA-Z0-9-_]{2,20})\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{"freshworks"} } // FromData will find and optionally verify Myfreshworks 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idmatch := range idmatches { if len(idmatch) != 2 { continue } resIdMatch := strings.TrimSpace(idmatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Myfreshworks, Raw: []byte(resMatch), } if verify { client := s.getClient() isVerified, verificationErr := verifyMyfreshworks(ctx, client, resMatch, resIdMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } } return results, nil } func (s Scanner) getClient() *http.Client { if s.client != nil { return s.client } return defaultClient } func verifyMyfreshworks(ctx context.Context, client *http.Client, resMatch, resIdMatch string) (bool, error) { req, err := http.NewRequestWithContext(ctx, "GET", "https://"+resIdMatch+".myfreshworks.com/crm/sales/api/sales_accounts/filters", nil) if err != nil { return false, err } req.Header.Add("Authorization", fmt.Sprintf("Token token=%s", resMatch)) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() if res.StatusCode == http.StatusOK { return true, nil } else if !(res.StatusCode == http.StatusUnauthorized || res.StatusCode == http.StatusForbidden) { return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } return false, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Myfreshworks } trufflehog-3.60.0/pkg/detectors/myfreshworks/myfreshworks_test.go000066400000000000000000000101471451332102400254140ustar00rootroot00000000000000//go:build detectors // +build detectors package myfreshworks 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 TestMyfreshworks_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MYFRESHWORKS_TOKEN") inactiveSecret := testSecrets.MustGetField("MYFRESHWORKS_INACTIVE") domain := testSecrets.MustGetField("MYFRESHWORKS_DOMAIN") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a myfreshworks secret %s domain %s here", secret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Myfreshworks, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a invalid myfreshworks secret %s domain %s here", inactiveSecret, domain)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Myfreshworks, Verified: false, }, }, wantErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a myfreshworks secret %s domain %s here", secret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Myfreshworks, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a myfreshworks secret %s domain %s here", secret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Myfreshworks, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { 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("Myfreshworks.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]) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Myfreshworks.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) } } }) } } trufflehog-3.60.0/pkg/detectors/myintervals/000077500000000000000000000000001451332102400210725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/myintervals/myintervals.go000066400000000000000000000044641451332102400240060ustar00rootroot00000000000000package myintervals import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"myintervals"}) + `\b([0-9a-z]{11})\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{"myintervals"} } // FromData will find and optionally verify Myintervals 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_MyIntervals, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:X", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.myintervals.com/client/", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_MyIntervals } trufflehog-3.60.0/pkg/detectors/myintervals/myintervals_test.go000066400000000000000000000055301451332102400250400ustar00rootroot00000000000000//go:build detectors // +build detectors package myintervals import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestMyintervals_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("MYINTERVALS") inactiveSecret := testSecrets.MustGetField("MYINTERVALS_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 myintervals secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_MyIntervals, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a myintervals 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_MyIntervals, 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("Myintervals.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Myintervals.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nasdaqdatalink/000077500000000000000000000000001451332102400214745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nasdaqdatalink/nasdaqdatalink.go000066400000000000000000000043611451332102400250060ustar00rootroot00000000000000package nasdaqdatalink import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nasdaq"}) + `\b([a-zA-Z0-9_-]{20})\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{"nasdaq"} } // FromData will find and optionally verify NasdaqDataLink 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NasdaqDataLink, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://data.nasdaq.com/api/v3/datasets/FRED/GDP.csv?api_key=%s&collapse=annual&rows=6&order=asc&column_index=1", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NasdaqDataLink } trufflehog-3.60.0/pkg/detectors/nasdaqdatalink/nasdaqdatalink_test.go000066400000000000000000000055661451332102400260550ustar00rootroot00000000000000//go:build detectors // +build detectors package nasdaqdatalink import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNasdaqDataLink_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("NASDAQDATALINK") inactiveSecret := testSecrets.MustGetField("NASDAQDATALINK_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 nasdaqdatalink secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NasdaqDataLink, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nasdaqdatalink 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_NasdaqDataLink, 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("NasdaqDataLink.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NasdaqDataLink.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nethunt/000077500000000000000000000000001451332102400202025ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nethunt/nethunt.go000066400000000000000000000047001451332102400222170ustar00rootroot00000000000000package nethunt import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nethunt"}) + `\b([a-z0-9-\S]{36})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nethunt"}) + `\b([a-zA-Z0-9.-@]{25,30})\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{"nethunt"} } // FromData will find and optionally verify Nethunt 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nethunt, Raw: []byte(tokenPatMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://nethunt.com/api/v1/zapier/triggers/readable-folder", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nethunt } trufflehog-3.60.0/pkg/detectors/nethunt/nethunt_test.go000066400000000000000000000056271451332102400232670ustar00rootroot00000000000000//go:build detectors // +build detectors package nethunt import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNethunt_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NETHUNT_TOKEN") inactiveSecret := testSecrets.MustGetField("NETHUNT_INACTIVE") user := testSecrets.MustGetField("ACCOUNT_USER") 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 nethunt secret %s within nethunt %s but verified", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nethunt, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nethunt secret %s within nethunt %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nethunt, 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("Nethunt.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nethunt.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) } } }) } } trufflehog-3.60.0/pkg/detectors/netlify/000077500000000000000000000000001451332102400201675ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/netlify/netlify.go000066400000000000000000000037531451332102400222000ustar00rootroot00000000000000package netlify import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"netlify"}) + `\b([A-Za-z0-9_-]{43,45})\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{"netlify"} } // FromData will find and optionally verify Netlify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Netlify, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/netlify/", } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.netlify.com/api/v1/sites", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Netlify } trufflehog-3.60.0/pkg/detectors/netlify/netlify_test.go000066400000000000000000000053711451332102400232350ustar00rootroot00000000000000//go:build detectors // +build detectors package netlify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNetlify_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NETLIFY_TOKEN") inactiveSecret := testSecrets.MustGetField("NETLIFY_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 netlify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Netlify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a netlify secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Netlify, 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("Netlify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Netlify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/neutrinoapi/000077500000000000000000000000001451332102400210525ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/neutrinoapi/neutrinoapi.go000066400000000000000000000055161451332102400237450ustar00rootroot00000000000000package neutrinoapi import ( "bytes" "context" "fmt" "io" "mime/multipart" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"neutrinoapi"}) + `\b([a-zA-Z0-9]{48})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"neutrinoapi"}) + `\b([a-zA-Z0-9]{6,24})\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{"neutrinoapi"} } // FromData will find and optionally verify NeutrinoApi 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NeutrinoApi, Raw: []byte(resMatch), } if verify { body := &bytes.Buffer{} writer := multipart.NewWriter(body) fw, err := writer.CreateFormField("url") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader("https://google.com")) if err != nil { continue } writer.Close() req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("https://neutrinoapi.net/url-info?user-id=%s&api-key=%s", resIdMatch, resMatch), bytes.NewReader(body.Bytes())) if err != nil { continue } req.Header.Add("Content-Type", writer.FormDataContentType()) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NeutrinoApi } trufflehog-3.60.0/pkg/detectors/neutrinoapi/neutrinoapi_test.go000066400000000000000000000056601451332102400250040ustar00rootroot00000000000000//go:build detectors // +build detectors package neutrinoapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNeutrinoApi_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("NEUTRINOAPI") id := testSecrets.MustGetField("NEUTRINOAPI_ID") inactiveSecret := testSecrets.MustGetField("NEUTRINOAPI_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 neutrinoapi secret %s within neutrinoapi %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NeutrinoApi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a neutrinoapi secret %s within neutrinoapi %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NeutrinoApi, 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("NeutrinoApi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NeutrinoApi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/newrelicpersonalapikey/000077500000000000000000000000001451332102400232745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/newrelicpersonalapikey/newrelicpersonalapikey.go000066400000000000000000000042501451332102400304030ustar00rootroot00000000000000package newrelicpersonalapikey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"newrelic"}) + `\b([A-Za-z0-9_\.]{4}-[A-Za-z0-9_\.]{42})\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{"newrelic"} } // FromData will find and optionally verify NewRelicPersonalApiKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NewRelicPersonalApiKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.newrelic.com/v2/users.json", nil) if err != nil { continue } req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, false) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NewRelicPersonalApiKey } trufflehog-3.60.0/pkg/detectors/newrelicpersonalapikey/newrelicpersonalapikey_test.go000066400000000000000000000057141451332102400314500ustar00rootroot00000000000000//go:build detectors // +build detectors package newrelicpersonalapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNewRelicPersonalApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NEWRELICPERSONALAPIKEY_TOKEN") inactiveSecret := testSecrets.MustGetField("NEWRELICPERSONALAPIKEY_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 newrelicpersonalapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NewRelicPersonalApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a newrelicpersonalapikey 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_NewRelicPersonalApiKey, 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("NewRelicPersonalApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NewRelicPersonalApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/newsapi/000077500000000000000000000000001451332102400201635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/newsapi/newsapi.go000066400000000000000000000041341451332102400221620ustar00rootroot00000000000000package newsapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"newsapi"}) + `\b([a-z0-9]{32})`) ) // 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{"newsapi"} } // FromData will find and optionally verify Newsapi 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Newsapi, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://newsapi.org/v2/everything?q=Apple&from=2021-09-29&sortBy=popularity&apiKey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Newsapi } trufflehog-3.60.0/pkg/detectors/newsapi/newsapi_test.go000066400000000000000000000054661451332102400232320ustar00rootroot00000000000000//go:build detectors // +build detectors package newsapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNewsapi_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NEWSAPI_TOKEN") inactiveSecret := testSecrets.MustGetField("NEWSAPI_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 newsapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Newsapi, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a newsapi 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_Newsapi, 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("Newsapi.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Newsapi.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) } } }) } } trufflehog-3.60.0/pkg/detectors/newscatcher/000077500000000000000000000000001451332102400210235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/newscatcher/newscatcher.go000066400000000000000000000041671451332102400236700ustar00rootroot00000000000000package newscatcher import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"newscatcher"}) + `\b([0-9A-Za-z_]{43})\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{"newscatcher"} } // FromData will find and optionally verify Newscatcher 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Newscatcher, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.newscatcherapi.com/v2/search?q=Tesla", nil) if err != nil { continue } req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Newscatcher } trufflehog-3.60.0/pkg/detectors/newscatcher/newscatcher_test.go000066400000000000000000000055301451332102400247220ustar00rootroot00000000000000//go:build detectors // +build detectors package newscatcher import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNewscatcher_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("NEWSCATCHER") inactiveSecret := testSecrets.MustGetField("NEWSCATCHER_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 newscatcher secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Newscatcher, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a newscatcher 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_Newscatcher, 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("Newscatcher.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Newscatcher.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nexmoapikey/000077500000000000000000000000001451332102400210465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nexmoapikey/nexmoapikey.go000066400000000000000000000047401451332102400237330ustar00rootroot00000000000000package nexmoapikey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nexmo"}) + `\b([A-Za-z0-9_-]{8})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nexmo"}) + `\b([A-Za-z0-9_-]{16})\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{"nexmo"} } // FromData will find and optionally verify NexmoApiKey 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) secretPat := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretPat { if len(match) != 2 { continue } resSecret := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NexmoApiKey, Raw: []byte(resMatch), RawV2: []byte(resMatch + resSecret), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://rest.nexmo.com/account/get-balance?api_key="+resMatch+"&api_secret="+resSecret+"", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NexmoApiKey } trufflehog-3.60.0/pkg/detectors/nexmoapikey/nexmoapikey_test.go000066400000000000000000000057251451332102400247760ustar00rootroot00000000000000//go:build detectors // +build detectors package nexmoapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNexmoApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NEXMOAPIKEY_TOKEN") key := testSecrets.MustGetField("NEXMOAPIKEY_KEY") inactiveSecret := testSecrets.MustGetField("NEXMOAPIKEY_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 nexmoapikey secret %s within https://rest.nexmo.com/account/get-balance?api_key=%s", secret, key)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NexmoApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nexmoapikey secret %s within %s but not valid", inactiveSecret, key)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NexmoApiKey, 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("NexmoApiKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NexmoApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nftport/000077500000000000000000000000001451332102400202115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nftport/nftport.go000066400000000000000000000042711451332102400222400ustar00rootroot00000000000000package nftport import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nftport"}) + `\b([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})\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{"nftport"} } // FromData will find and optionally verify Nftport 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nftport, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.nftport.xyz/me/contracts", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nftport } trufflehog-3.60.0/pkg/detectors/nftport/nftport_test.go000066400000000000000000000054661451332102400233060ustar00rootroot00000000000000//go:build detectors // +build detectors package nftport import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNftport_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NFTPORT_TOKEN") inactiveSecret := testSecrets.MustGetField("NFTPORT_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 nftport secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nftport, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nftport 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_Nftport, 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("Nftport.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nftport.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) } } }) } } trufflehog-3.60.0/pkg/detectors/ngc/000077500000000000000000000000001451332102400172645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/ngc/ngc.go000066400000000000000000000052201451332102400203610ustar00rootroot00000000000000package ngc import ( "context" "encoding/base64" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. // keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ngc"}) + `\b([[:alnum:]]{26}:[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12})\b`) keyPat1 = regexp.MustCompile(`\b([[:alnum:]]{84})\b`) keyPat2 = regexp.MustCompile(`\b([[:alnum:]]{26}:[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12})\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{"ngc"} } // FromData will find and optionally verify NGC 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 := keyPat1.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) decode, _ := base64.StdEncoding.DecodeString(resMatch) containsKey := keyPat2.MatchString(string(decode)) if containsKey { s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NGC, Raw: []byte(resMatch), } if verify { key := "Basic " + string(base64.StdEncoding.EncodeToString([]byte("$oauthtoken:"+resMatch))) req, err := http.NewRequestWithContext(ctx, "GET", "https://authn.nvidia.com/token?service=ngc", nil) if err != nil { continue } req.Header = http.Header{ "accept": {"*/*"}, "Authorization": {key}, } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NGC } trufflehog-3.60.0/pkg/detectors/ngc/ngc_test.go000066400000000000000000000054021451332102400214220ustar00rootroot00000000000000//go:build detectors // +build detectors package ngc import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNGC_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "hackthisai", "th-ngc-valid") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NGC") inactiveSecret := testSecrets.MustGetField("NGC_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 ngc secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NGC, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a ngc 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_NGC, 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("NGC.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NGC.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nicereply/000077500000000000000000000000001451332102400205075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nicereply/nicereply.go000066400000000000000000000043661451332102400230410ustar00rootroot00000000000000package nicereply import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nicereply"}) + `\b([0-9a-f]{40})\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{"nicereply"} } // FromData will find and optionally verify Nicereply 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nicereply, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf(":%s", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://api.nicereply.com/v1/users/stats", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nicereply } trufflehog-3.60.0/pkg/detectors/nicereply/nicereply_test.go000066400000000000000000000055041451332102400240730ustar00rootroot00000000000000//go:build detectors // +build detectors package nicereply import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNicereply_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("NICEREPLY") inactiveSecret := testSecrets.MustGetField("NICEREPLY_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 nicereply secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nicereply, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nicereply 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_Nicereply, 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("Nicereply.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nicereply.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nightfall/000077500000000000000000000000001451332102400204655ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nightfall/nightfall.go000066400000000000000000000042261451332102400227700ustar00rootroot00000000000000package nightfall import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(NF\-[a-zA-Z0-9]{32})\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{"nightfall"} } // FromData will find and optionally verify Nightfall 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nightfall, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"fileSizeBytes":256}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.nightfall.ai/v3/upload", payload) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nightfall } trufflehog-3.60.0/pkg/detectors/nightfall/nightfall_test.go000066400000000000000000000055041451332102400240270ustar00rootroot00000000000000//go:build detectors // +build detectors package nightfall import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNightfall_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NIGHTFALL") inactiveSecret := testSecrets.MustGetField("NIGHTFALL_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 nightfall secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nightfall, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nightfall 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_Nightfall, 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("Nightfall.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nightfall.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nimble/000077500000000000000000000000001451332102400177635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nimble/nimble.go000066400000000000000000000041611451332102400215620ustar00rootroot00000000000000package nimble import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nimble"}) + `\b([a-zA-Z0-9]{30})\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{"nimble"} } // FromData will find and optionally verify Nimble 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nimble, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.nimble.com/api/v1/myself", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nimble } trufflehog-3.60.0/pkg/detectors/nimble/nimble_test.go000066400000000000000000000054541451332102400226270ustar00rootroot00000000000000//go:build detectors // +build detectors package nimble import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNimble_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NIMBLE_TOKEN") inactiveSecret := testSecrets.MustGetField("NIMBLE_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 nimble secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nimble, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nimble 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_Nimble, 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("Nimble.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nimble.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nitro/000077500000000000000000000000001451332102400176505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nitro/nitro.go000066400000000000000000000041671451332102400213420ustar00rootroot00000000000000package nitro import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nitro"}) + `\b([0-9a-f]{32})\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{"nitro"} } // FromData will find and optionally verify Nitro 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nitro, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://nitro.alconost.com/api/v1/account", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nitro } trufflehog-3.60.0/pkg/detectors/nitro/nitro_test.go000066400000000000000000000054341451332102400223770ustar00rootroot00000000000000//go:build detectors // +build detectors package nitro import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNitro_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("NITRO") inactiveSecret := testSecrets.MustGetField("NITRO_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 nitro secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nitro, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nitro 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_Nitro, 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("Nitro.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nitro.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) } } }) } } trufflehog-3.60.0/pkg/detectors/noticeable/000077500000000000000000000000001451332102400206225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/noticeable/noticeable.go000066400000000000000000000044061451332102400232620ustar00rootroot00000000000000package noticeable import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"noticeable"}) + `\b([0-9a-zA-Z]{20})\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{"noticeable"} } // FromData will find and optionally verify Noticeable 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Noticeable, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"query": "YOUR_GRAPHQL_PAYLOAD"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.noticeable.io/graphql", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Apikey %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Noticeable } trufflehog-3.60.0/pkg/detectors/noticeable/noticeable_test.go000066400000000000000000000054751451332102400243300ustar00rootroot00000000000000//go:build detectors // +build detectors package noticeable import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNoticeable_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("NOTICEABLE") inactiveSecret := testSecrets.MustGetField("NOTICEABLE_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 noticeable secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Noticeable, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a noticeable 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_Noticeable, 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("Noticeable.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Noticeable.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) } } }) } } trufflehog-3.60.0/pkg/detectors/notion/000077500000000000000000000000001451332102400200235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/notion/notion.go000066400000000000000000000053001451332102400216560ustar00rootroot00000000000000package notion import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(secret_[A-Za-z0-9]{43})\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{"notion"} } // FromData will find and optionally verify Notion 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Notion, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.notion.com/v1/users", nil) if err != nil { continue } req.Header.Add("Notion-Version", "2022-06-28") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 || res.StatusCode == 403 { // if >= 200 and < 300, the secret is valid and has privileges for the /v1/users endpoint // If 403, the secret is valid, but does not have privileges for the /v1/users endpoint, // Notion returns 401 for all non-valid keys, thus 403 indicates it has fine-tuned permissions, // /v1/search, /v1/databases/*, etc. may work. s1.Verified = true } else if res.StatusCode == 401 { // The secret is determinately not verified (nothing to do) // Nothing returns 401 on all requests not containing a api key. } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } else { s1.VerificationError = err } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Notion } trufflehog-3.60.0/pkg/detectors/notion/notion_test.go000066400000000000000000000054541451332102400227270ustar00rootroot00000000000000//go:build detectors // +build detectors package notion import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNotion_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NOTION_TOKEN") inactiveSecret := testSecrets.MustGetField("NOTION_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 notion secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Notion, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a notion 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_Notion, 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("Notion.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Notion.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nozbeteams/000077500000000000000000000000001451332102400206645ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nozbeteams/nozbeteams.go000066400000000000000000000043041451332102400233630ustar00rootroot00000000000000package nozbeteams import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nozbe", "nozbeteams"}) + `\b([0-9A-Za-z]{16}_[0-9A-Za-z\-_]{64}[ \r\n]{1})`) ) // 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{"nozbe", "nozbeteams"} } // FromData will find and optionally verify NozbeTeams 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NozbeTeams, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api4.nozbe.com/v1/api/users", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NozbeTeams } trufflehog-3.60.0/pkg/detectors/nozbeteams/nozbeteams_test.go000066400000000000000000000054301451332102400244230ustar00rootroot00000000000000//go:build detectors // +build detectors package nozbeteams import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNozbeTeams_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NOZBETEAMS") inactiveSecret := testSecrets.MustGetField("NOZBETEAMS_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 nozbeteams secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NozbeTeams, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nozbeteams 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_NozbeTeams, 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("NozbeTeams.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NozbeTeams.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/npmtoken/000077500000000000000000000000001451332102400203505ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/npmtoken/npmtoken.go000066400000000000000000000045271451332102400225420ustar00rootroot00000000000000package npmtoken import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (s Scanner) Version() int { return 1 } var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"npm"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"npm"} } // FromData will find and optionally verify NpmToken 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NpmToken, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/npm/", } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://registry.npmjs.org/-/whoami", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NpmToken } trufflehog-3.60.0/pkg/detectors/npmtoken/npmtoken_test.go000066400000000000000000000054721451332102400236010ustar00rootroot00000000000000//go:build detectors // +build detectors package npmtoken import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNpmToken_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NPMTOKEN") inactiveSecret := testSecrets.MustGetField("NPMTOKEN_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 npmtoken secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NpmToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a npmtoken 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_NpmToken, 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("NpmToken.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NpmToken.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) } } }) } } trufflehog-3.60.0/pkg/detectors/npmtokenv2/000077500000000000000000000000001451332102400206205ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/npmtokenv2/npmtokenv2.go000066400000000000000000000043511451332102400232550ustar00rootroot00000000000000package npmtokenv2 import ( "context" "fmt" "net/http" "regexp" "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{} // Ensure the Scanner satisfies the interfaces at compile time. var _ detectors.Detector = (*Scanner)(nil) var _ detectors.Versioner = (*Scanner)(nil) func (s Scanner) Version() int { return 2 } var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`(npm_[0-9a-zA-Z]{36})`) ) // 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{"npm_"} } // FromData will find and optionally verify NpmTokenV2 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := match[1] s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NpmToken, Raw: []byte(resMatch), } s1.ExtraData = map[string]string{ "rotation_guide": "https://howtorotate.com/docs/tutorials/npm/", } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://registry.npmjs.org/-/whoami", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NpmToken } trufflehog-3.60.0/pkg/detectors/npmtokenv2/npmtokenv2_test.go000066400000000000000000000055301451332102400243140ustar00rootroot00000000000000//go:build detectors // +build detectors package npmtokenv2 import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNpmToken_New_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NPMTOKEN_NEW") inactiveSecret := testSecrets.MustGetField("NPMTOKEN_NEW_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 npmtoken_new secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NpmToken, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a npmtoken_new 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_NpmToken, 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("NpmToken_New.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("NpmToken_New.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nugetapikey/000077500000000000000000000000001451332102400210425ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nugetapikey/nugetapikey.go000066400000000000000000000042761451332102400237270ustar00rootroot00000000000000package nugetapikey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nuget"}) + `\b([a-z0-9]{46})\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{"nuget"} } // FromData will find and optionally verify Nugetapikey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_NuGetApiKey, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "PUT", "https://www.nuget.org/api/v2/package", nil) if err != nil { continue } req.Header.Add("Content-Length", "0") req.Header.Add("X-NuGet-ApiKey", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() // we can either match on response code "400" or "Bad Request" response if res.StatusCode == 400 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_NuGetApiKey } trufflehog-3.60.0/pkg/detectors/nugetapikey/nugetapikey_test.go000066400000000000000000000055311451332102400247610ustar00rootroot00000000000000//go:build detectors // +build detectors package nugetapikey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNugetapikey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NUGETAPIKEY") inactiveSecret := testSecrets.MustGetField("NUGETAPIKEY_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 nugetapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_NuGetApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nugetapikey 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_NuGetApiKey, 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("Nugetapikey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nugetapikey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/numverify/000077500000000000000000000000001451332102400205415ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/numverify/numverify.go000066400000000000000000000047621451332102400231250ustar00rootroot00000000000000package numverify import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"numverify"}) + `\b([a-z0-9]{32})\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{"numverify"} } // FromData will find and optionally verify Numverify 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Numverify, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://apilayer.net/api/validate?access_key=%s&number=14158586273", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `country_code`) || strings.Contains(bodyString, `"info":"Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Numverify } trufflehog-3.60.0/pkg/detectors/numverify/numverify_test.go000066400000000000000000000055041451332102400241570ustar00rootroot00000000000000//go:build detectors // +build detectors package numverify import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNumverify_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("NUMVERIFY") inactiveSecret := testSecrets.MustGetField("NUMVERIFY_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 numverify secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Numverify, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a numverify 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_Numverify, 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("Numverify.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Numverify.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nutritionix/000077500000000000000000000000001451332102400211115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nutritionix/nutritionix.go000066400000000000000000000052241451332102400240370ustar00rootroot00000000000000package nutritionix import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nutritionix"}) + `\b([a-z0-9]{32})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nutritionix"}) + `\b([a-z0-9]{8})\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{"nutritionix"} } // FromData will find and optionally verify Nutritionix 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nutritionix, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"query":"for breakfast i ate 2 eggs, bacon, and french toast","timezone":"US/Eastern"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://trackapi.nutritionix.com/v2/natural/nutrients", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-app-id", resIdMatch) req.Header.Add("x-app-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nutritionix } trufflehog-3.60.0/pkg/detectors/nutritionix/nutritionix_test.go000066400000000000000000000056601451332102400251020ustar00rootroot00000000000000//go:build detectors // +build detectors package nutritionix import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNutritionix_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("NUTRITIONIX") id := testSecrets.MustGetField("NUTRITIONIX_ID") inactiveSecret := testSecrets.MustGetField("NUTRITIONIX_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 nutritionix secret %s within nutritionix %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nutritionix, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nutritionix secret %s within nutritionix %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nutritionix, 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("Nutritionix.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nutritionix.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nylas/000077500000000000000000000000001451332102400176435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nylas/nylas.go000066400000000000000000000042511451332102400213220ustar00rootroot00000000000000package nylas import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nylas"}) + `\b([0-9A-Za-z]{30})\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{"nylas"} } // FromData will find and optionally verify Nylas 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nylas, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.nylas.com/account", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.nylas+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nylas } trufflehog-3.60.0/pkg/detectors/nylas/nylas_test.go000066400000000000000000000054341451332102400223650ustar00rootroot00000000000000//go:build detectors // +build detectors package nylas import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNylas_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NYLAS") inactiveSecret := testSecrets.MustGetField("NYLAS_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 nylas secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nylas, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nylas 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_Nylas, 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("Nylas.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nylas.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) } } }) } } trufflehog-3.60.0/pkg/detectors/nytimes/000077500000000000000000000000001451332102400202055ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/nytimes/nytimes.go000066400000000000000000000041121451332102400222220ustar00rootroot00000000000000package nytimes import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"nytimes"}) + `\b([a-z0-9A-Z-]{32})\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{"nytimes"} } // FromData will find and optionally verify Nytimes 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Nytimes, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.nytimes.com/svc/archive/v1/2019/1.json?api-key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Nytimes } trufflehog-3.60.0/pkg/detectors/nytimes/nytimes_test.go000066400000000000000000000054661451332102400232760ustar00rootroot00000000000000//go:build detectors // +build detectors package nytimes import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestNytimes_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("NYTIMES_TOKEN") inactiveSecret := testSecrets.MustGetField("NYTIMES_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 nytimes secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Nytimes, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a nytimes 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_Nytimes, 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("Nytimes.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Nytimes.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) } } }) } } trufflehog-3.60.0/pkg/detectors/oanda/000077500000000000000000000000001451332102400175775ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/oanda/oanda.go000066400000000000000000000042021451332102400212060ustar00rootroot00000000000000package oanda import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"oanda"}) + `\b([a-zA-Z0-9]{24})\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{"oanda"} } // FromData will find and optionally verify Oanda 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Oanda, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://web-services.oanda.com/rates/api/v2/currencies.json", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Oanda } trufflehog-3.60.0/pkg/detectors/oanda/oanda_test.go000066400000000000000000000054341451332102400222550ustar00rootroot00000000000000//go:build detectors // +build detectors package oanda import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOanda_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("OANDA") inactiveSecret := testSecrets.MustGetField("OANDA_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 oanda secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Oanda, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a oanda 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_Oanda, 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("Oanda.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Oanda.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) } } }) } } trufflehog-3.60.0/pkg/detectors/okta/000077500000000000000000000000001451332102400174535ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/okta/okta.go000066400000000000000000000046731451332102400207520ustar00rootroot00000000000000package okta import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( domainPat = regexp.MustCompile(`\b[a-z0-9-]{1,40}\.okta(?:preview|-emea){0,1}\.com\b`) tokenPat = regexp.MustCompile(`\b00[a-zA-Z0-9_-]{40}\b`) // TODO: Oauth client secrets ) // 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{"okta"} } // FromData will find and optionally verify Okta secrets in a given set of bytes. func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { for _, tokenMatch := range tokenPat.FindAll(data, -1) { token := string(tokenMatch) for _, domainMatch := range domainPat.FindAll(data, -1) { domain := string(domainMatch) s := detectors.Result{ DetectorType: detectorspb.DetectorType_Okta, Raw: []byte(token), RawV2: []byte(fmt.Sprintf("%s:%s", domain, token)), } if verify { // curl -v -X GET \ // -H "Accept: application/json" \ // -H "Content-Type: application/json" \ // -H "Authorization: Bearer token" \ // "https://subdomain.okta.com/api/v1/users/me" // url := fmt.Sprintf("https://%s/api/v1/users/me", domain) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return results, err } req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("SSWS %s", token)) resp, err := common.SaneHttpClient().Do(req) if err != nil { continue } defer resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 300 { body, _ := io.ReadAll(resp.Body) if strings.Contains(string(body), "activated") { s.Verified = true } } } if !s.Verified { if detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) { continue } } results = append(results, s) } } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Okta } trufflehog-3.60.0/pkg/detectors/okta/okta_test.go000066400000000000000000000054271451332102400220070ustar00rootroot00000000000000//go:build detectors // +build detectors package okta import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOkta_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("OKTA") secretInactive := testSecrets.MustGetField("OKTA_INACTIVE") domain := testSecrets.MustGetField("OKTA_DOMAIN") 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 token, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a okta secret %s within oktaDomain %s", secret, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Okta, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a okta secret %s within oktaDomain %s", secretInactive, domain)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Okta, 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("Okta.FromData) error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Okta.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) } } }) } } trufflehog-3.60.0/pkg/detectors/omnisend/000077500000000000000000000000001451332102400203315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/omnisend/omnisend.go000066400000000000000000000041541451332102400225000ustar00rootroot00000000000000package omnisend import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"omnisend"}) + `\b([a-z0-9A-Z-]{75})\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{"omnisend"} } // FromData will find and optionally verify Omnisend 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Omnisend, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.omnisend.com/v3/contacts?limit=100&offset=0", nil) if err != nil { continue } req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Omnisend } trufflehog-3.60.0/pkg/detectors/omnisend/omnisend_test.go000066400000000000000000000055001451332102400235330ustar00rootroot00000000000000//go:build detectors // +build detectors package omnisend import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOmnisend_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("OMNISEND_TOKEN") inactiveSecret := testSecrets.MustGetField("OMNISEND_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 omnisend secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Omnisend, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a omnisend 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_Omnisend, 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("Omnisend.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Omnisend.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) } } }) } } trufflehog-3.60.0/pkg/detectors/onedesk/000077500000000000000000000000001451332102400201455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/onedesk/onedesk.go000066400000000000000000000053061451332102400221300ustar00rootroot00000000000000package onedesk import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. emailPat = regexp.MustCompile(`\b([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-z]+)\b`) pwordPat = regexp.MustCompile(detectors.PrefixRegex([]string{"onedesk"}) + `\b([a-zA-Z0-9!=@#$%^]{8,64})`) ) // 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{"onedesk"} } // FromData will find and optionally verify Onedesk 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 := emailPat.FindAllStringSubmatch(dataStr, -1) pwordMatches := pwordPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, pwordMatch := range pwordMatches { if len(pwordMatch) != 2 { continue } resPword := strings.TrimSpace(pwordMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Onedesk, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(fmt.Sprintf(`{"email": "%s", "password": "%s"}`, resMatch, resPword)) req, err := http.NewRequestWithContext(ctx, "POST", "https://app.onedesk.com/rest/2.0/login/loginUser", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") 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 res.StatusCode >= 200 && res.StatusCode < 300 && strings.Contains(body, `"code":"SUCCESS"`) { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Onedesk } trufflehog-3.60.0/pkg/detectors/onedesk/onedesk_test.go000066400000000000000000000056351451332102400231740ustar00rootroot00000000000000//go:build detectors // +build detectors package onedesk import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOnedesk_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } email := testSecrets.MustGetField("SCANNERS_EMAIL") pword := testSecrets.MustGetField("SCANNERS_PASS") inactivePword := testSecrets.MustGetField("SCANNERS_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 onedesk email %s within onedesk password %s", email, pword)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Onedesk, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a onedesk secret %s within onedesk password %s but not valid", email, inactivePword)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Onedesk, 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("Onedesk.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Onedesk.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) } } }) } } trufflehog-3.60.0/pkg/detectors/onelogin/000077500000000000000000000000001451332102400203275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/onelogin/onelogin.go000066400000000000000000000050251451332102400224720ustar00rootroot00000000000000package onelogin import ( "context" "fmt" "net/http" "regexp" "strings" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( oauthClientIDPat = regexp.MustCompile(`(?i)id[a-zA-Z0-9_' "=]{0,20}([a-z0-9]{64})`) oauthClientSecretPat = regexp.MustCompile(`(?i)secret[a-zA-Z0-9_' "=]{0,20}([a-z0-9]{64})`) // TODO: Legacy API tokens apiDomains = []string{"api.us.onelogin.com", "api.eu.onelogin.com"} client = http.Client{Timeout: time.Second * 5} ) // 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{"onelogin"} } // FromData will find and optionally verify Onelogin 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) for _, clientID := range oauthClientIDPat.FindAllStringSubmatch(dataStr, -1) { if len(clientID) != 2 { continue } for _, clientSecret := range oauthClientSecretPat.FindAllStringSubmatch(dataStr, -1) { if len(clientSecret) != 2 { continue } s := detectors.Result{ DetectorType: detectorspb.DetectorType_OneLogin, Raw: []byte(clientID[1]), RawV2: []byte(fmt.Sprintf("%s:%s", clientID[1], clientSecret[1])), Redacted: clientID[1], } if verify { for _, domain := range apiDomains { tokenURL := fmt.Sprintf("https://%s/auth/oauth2/v2/token", domain) req, err := http.NewRequestWithContext(ctx, "POST", tokenURL, strings.NewReader(`{"grant_type":"client_credentials"}`)) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("client_id:%s, client_secret:%s", clientID[1], clientSecret[1])) req.Header.Add("Content-Type", "application/json; charset=utf-8") res, err := client.Do(req) if err == nil { res.Body.Close() // The request body is unused. if res.StatusCode >= 200 && res.StatusCode < 300 { s.Verified = true } } } } if !s.Verified && detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OneLogin } trufflehog-3.60.0/pkg/detectors/onelogin/onelogin_test.go000066400000000000000000000054251451332102400235350ustar00rootroot00000000000000//go:build detectors // +build detectors package onelogin import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOnelogin_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ONELOGIN") secretInactive := testSecrets.MustGetField("ONELOGIN_INACTIVE") id := testSecrets.MustGetField("ONELOGIN_ID") 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 onelogin secret=%s within onelogin id %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OneLogin, Verified: true, Redacted: id, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a onelogin secret=%s within onelogin id %s", secretInactive, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OneLogin, Verified: false, Redacted: id, }, }, 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("Onelogin.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Onelogin.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) } } }) } } trufflehog-3.60.0/pkg/detectors/onepagecrm/000077500000000000000000000000001451332102400206355ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/onepagecrm/onepagecrm.go000066400000000000000000000047211451332102400233100ustar00rootroot00000000000000package onepagecrm import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"onepagecrm"}) + `\b([a-zA-Z0-9=]{44})`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"onepagecrm"}) + `\b([a-z0-9]{24})\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{"onepagecrm"} } // FromData will find and optionally verify OnepageCRM 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } tokenPatMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } userPatMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_OnepageCRM, Raw: []byte(tokenPatMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.onepagecrm.com/api/v3/contacts.json?per_page=20&page=1", nil) if err != nil { continue } req.SetBasicAuth(userPatMatch, tokenPatMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(tokenPatMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OnepageCRM } trufflehog-3.60.0/pkg/detectors/onepagecrm/onepagecrm_test.go000066400000000000000000000056621451332102400243540ustar00rootroot00000000000000//go:build detectors // +build detectors package onepagecrm import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOnepageCRM_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("ONEPAGECRM") user := testSecrets.MustGetField("ONEPAGECRM_USER") inactiveSecret := testSecrets.MustGetField("ONEPAGECRM_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 onepagecrm secret %s within onepagecrm %s within", secret, user)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OnepageCRM, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a onepagecrm secret %s within onepagecrm %s but not valid", inactiveSecret, user)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OnepageCRM, 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("OnepageCRM.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("OnepageCRM.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) } } }) } } trufflehog-3.60.0/pkg/detectors/onesignal/000077500000000000000000000000001451332102400204745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/onesignal/onesignal.go000066400000000000000000000044631451332102400230110ustar00rootroot00000000000000package onesignal import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"onesignal"}) + common.UUIDPattern) ) // 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{"onesignal"} } // FromData will find and optionally verify Onesignal 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Onesignal, Raw: []byte(resMatch), } if verify { data := fmt.Sprintf("%s:", resMatch) sEnc := b64.StdEncoding.EncodeToString([]byte(data)) req, err := http.NewRequestWithContext(ctx, "GET", "https://onesignal.com/api/v1/apps", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.onesignal+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Onesignal } trufflehog-3.60.0/pkg/detectors/onesignal/onesignal_test.go000066400000000000000000000055041451332102400240450ustar00rootroot00000000000000//go:build detectors // +build detectors package onesignal import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOnesignal_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("ONESIGNAL") inactiveSecret := testSecrets.MustGetField("ONESIGNAL_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 onesignal secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Onesignal, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a onesignal 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_Onesignal, 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("Onesignal.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Onesignal.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) } } }) } } trufflehog-3.60.0/pkg/detectors/onwaterio/000077500000000000000000000000001451332102400205245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/onwaterio/onwaterio.go000066400000000000000000000041611451332102400230640ustar00rootroot00000000000000package onwaterio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"onwater"}) + `\b([a-zA-Z0-9_-]{20})\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{"onwater"} } // FromData will find and optionally verify OnWaterIO 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_OnWaterIO, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.onwater.io/api/v1/results/23.92323,-66.3?access_token=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OnWaterIO } trufflehog-3.60.0/pkg/detectors/onwaterio/onwaterio_test.go000066400000000000000000000055001451332102400241210ustar00rootroot00000000000000//go:build detectors // +build detectors package onwaterio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOnWaterIO_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("ONWATERIO") inactiveSecret := testSecrets.MustGetField("ONWATERIO_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 onwater secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OnWaterIO, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a onwater 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_OnWaterIO, 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("OnWaterIO.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("OnWaterIO.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) } } }) } } trufflehog-3.60.0/pkg/detectors/oopspam/000077500000000000000000000000001451332102400201735ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/oopspam/oopspam.go000066400000000000000000000052231451332102400222020ustar00rootroot00000000000000package oopspam import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"oopspam"}) + `\b([a-zA-Z0-9]{40})\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{"oopspam"} } // FromData will find and optionally verify OOPSpam 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_OOPSpam, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"checkForLength":true,"content":"Dear Agent, We are a manufacturing company which specializes in supplying Aluminum Rod with Zinc Alloy Rod to customers worldwide, based in Japan, Asia. We have been unable to follow up payments effectively for transactions with debtor customers in your country due to our distant locations, thus our reason for requesting for your services representation.","senderIP":"185.234.219.246","allowedCountries":["it","us"],"allowedLanguages":["en"]}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.oopspam.com/v1/spamdetection", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OOPSpam } trufflehog-3.60.0/pkg/detectors/oopspam/oopspam_test.go000066400000000000000000000054601451332102400232440ustar00rootroot00000000000000//go:build detectors // +build detectors package oopspam import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOOPSpam_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("OOPSPAM") inactiveSecret := testSecrets.MustGetField("OOPSPAM_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 oopspam secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OOPSpam, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a oopspam 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_OOPSpam, 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("OOPSpam.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("OOPSpam.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) } } }) } } trufflehog-3.60.0/pkg/detectors/openai/000077500000000000000000000000001451332102400177705ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/openai/openai.go000066400000000000000000000060441451332102400215760ustar00rootroot00000000000000package openai import ( "context" "encoding/json" "fmt" "net/http" "regexp" "strconv" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "github.com/trufflesecurity/trufflehog/v3/pkg/common" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) // The magic string T3BlbkFJ is the base64-encoded string: OpenAI var keyPat = regexp.MustCompile(`\b(sk-[[:alnum:]]{20}T3BlbkFJ[[:alnum:]]{20})\b`) // TODO: Add secret context?? Information about access, ownership etc type orgResponse struct { Data []organization `json:"data"` } type organization struct { ID string `json:"id"` Title string `json:"title"` User string `json:"name"` Description string `json:"description"` Personal bool `json:"personal"` Default bool `json:"is_default"` Role string `json:"role"` } // 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{"T3BlbkFJ"} } // FromData will find and optionally verify OpenAI 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) for _, match := range matches { // First match is entire regex, second is the first group. if len(match) != 2 { continue } token := match[1] s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_OpenAI, Redacted: token[:3] + "..." + token[47:], Raw: []byte(token), } if verify { client := common.SaneHttpClient() // Undocumented API // https://api.openai.com/v1/organizations req, err := http.NewRequestWithContext(ctx, "GET", "https://api.openai.com/v1/organizations", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json; charset=utf-8") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token)) res, err := client.Do(req) if err == nil { if res.StatusCode >= 200 && res.StatusCode < 300 { var orgs orgResponse err = json.NewDecoder(res.Body).Decode(&orgs) res.Body.Close() if err == nil { s1.Verified = true org := orgs.Data[0] s1.ExtraData = map[string]string{ "id": org.ID, "title": org.Title, "user": org.User, "description": org.Description, "role": org.Role, "is_personal": strconv.FormatBool(org.Personal), "is_default": strconv.FormatBool(org.Default), "total_orgs": fmt.Sprintf("%d", len(orgs.Data)), } } } } } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OpenAI } trufflehog-3.60.0/pkg/detectors/openai/openai_test.go000066400000000000000000000055361451332102400226420ustar00rootroot00000000000000//go:build detectors // +build detectors package openai import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOpenAI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } oaiUnverified := testSecrets.MustGetField("OPENAI_UNVERIFIED") oaiVerified := testSecrets.MustGetField("OPENAI_VERIFIED") 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, unverified OpenAI token sk-", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find an OpenAI secret %s within", oaiUnverified)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OpenAI, Redacted: "sk-...gOPc", Verified: false, }, }, wantErr: false, }, { name: "Found, verified OpenAI token sk-", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find an OpenAI secret %s within", oaiVerified)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OpenAI, Verified: true, Redacted: "sk-...gOPb", }, }, 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("OpenAI.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil got[i].ExtraData = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("OpenAI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/opencagedata/000077500000000000000000000000001451332102400211305ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/opencagedata/opencagedata.go000066400000000000000000000042041451332102400240720ustar00rootroot00000000000000package opencagedata import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"opencagedata"}) + `\b([a-z0-9]{32})\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{"opencagedata"} } // FromData will find and optionally verify OpenCageData 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_OpenCageData, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.opencagedata.com/geocode/v1/json?q=12.8797,121.7740&key=%s", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OpenCageData } trufflehog-3.60.0/pkg/detectors/opencagedata/opencagedata_test.go000066400000000000000000000055421451332102400251370ustar00rootroot00000000000000//go:build detectors // +build detectors package opencagedata import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOpenCageData_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("OPENCAGEDATA") inactiveSecret := testSecrets.MustGetField("OPENCAGEDATA_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 opencagedata secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OpenCageData, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a opencagedata 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_OpenCageData, 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("OpenCageData.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("OpenCageData.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) } } }) } } trufflehog-3.60.0/pkg/detectors/opengraphr/000077500000000000000000000000001451332102400206625ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/opengraphr/opengraphr.go000066400000000000000000000041611451332102400233600ustar00rootroot00000000000000package opengraphr import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"opengraphr"}) + `\b([0-9Aa-zA-Z]{80})\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{"opengraphr"} } // FromData will find and optionally verify Opengraphr 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Opengraphr, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.opengraphr.com/v1/og?api_token="+resMatch+"&url=https://www.marlonpamisa.com/", nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Opengraphr } trufflehog-3.60.0/pkg/detectors/opengraphr/opengraphr_test.go000066400000000000000000000055161451332102400244240ustar00rootroot00000000000000//go:build detectors // +build detectors package opengraphr import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOpengraphr_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("OPENGRAPHR") inactiveSecret := testSecrets.MustGetField("OPENGRAPHR_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 opengraphr secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Opengraphr, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a opengraphr 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_Opengraphr, 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("Opengraphr.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Opengraphr.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) } } }) } } trufflehog-3.60.0/pkg/detectors/openuv/000077500000000000000000000000001451332102400200315ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/openuv/openuv.go000066400000000000000000000041401451332102400216730ustar00rootroot00000000000000package openuv import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"openuv"}) + `\b([0-9a-z]{32})\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{"openuv"} } // FromData will find and optionally verify Openuv 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Openuv, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.openuv.io/api/v1/uv?lat=-33.34&lng=115.342", nil) if err != nil { continue } req.Header.Add("x-access-token", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Openuv } trufflehog-3.60.0/pkg/detectors/openuv/openuv_test.go000066400000000000000000000054461451332102400227440ustar00rootroot00000000000000//go:build detectors // +build detectors package openuv import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOpenuv_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("OPENUV") inactiveSecret := testSecrets.MustGetField("OPENUV_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 openuv secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Openuv, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a openuv 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_Openuv, 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("Openuv.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Openuv.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) } } }) } } trufflehog-3.60.0/pkg/detectors/openweather/000077500000000000000000000000001451332102400210365ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/openweather/openweather.go000066400000000000000000000041541451332102400237120ustar00rootroot00000000000000package openweather import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"openweather"}) + `\b([a-z0-9]{32})\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{"openweather"} } // FromData will find and optionally verify OpenWeather 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_OpenWeather, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.openweathermap.org/data/2.5/weather?id=524901&lang=fr&appid="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_OpenWeather } trufflehog-3.60.0/pkg/detectors/openweather/openweather_test.go000066400000000000000000000055361451332102400247560ustar00rootroot00000000000000//go:build detectors // +build detectors package openweather import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOpenWeather_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("OPENWEATHER_TOKEN") inactiveSecret := testSecrets.MustGetField("OPENWEATHER_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 openweather secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_OpenWeather, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a openweather 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_OpenWeather, 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("OpenWeather.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("OpenWeather.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) } } }) } } trufflehog-3.60.0/pkg/detectors/opsgenie/000077500000000000000000000000001451332102400203265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/opsgenie/opsgenie.go000066400000000000000000000043021451332102400224650ustar00rootroot00000000000000package opsgenie import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"opsgenie"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`) ) func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Opsgenie } // 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{"opsgenie"} } // FromData will find and optionally verify Opsgenie 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Opsgenie, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.opsgenie.com/v2/alerts", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("GenieKey %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return detectors.CleanResults(results), nil } trufflehog-3.60.0/pkg/detectors/opsgenie/opsgenie_test.go000066400000000000000000000054201451332102400235260ustar00rootroot00000000000000package opsgenie import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOpsgenie_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("OPSGENIE") inactiveSecret := testSecrets.MustGetField("OPSGENIE_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 opsgenie secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Opsgenie, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a opsgenie 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_Opsgenie, 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("Opsgenie.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Opsgenie.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) } } }) } } trufflehog-3.60.0/pkg/detectors/optimizely/000077500000000000000000000000001451332102400207225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/optimizely/optimizely.go000066400000000000000000000042151451332102400234600ustar00rootroot00000000000000package optimizely import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"optimizely"}) + `\b([0-9A-Za-z-:]{54})\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{"optimizely"} } // FromData will find and optionally verify Optimizely 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Optimizely, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.optimizely.com/v2/projects", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Optimizely } trufflehog-3.60.0/pkg/detectors/optimizely/optimizely_test.go000066400000000000000000000055161451332102400245240ustar00rootroot00000000000000//go:build detectors // +build detectors package optimizely import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOptimizely_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("OPTIMIZELY") inactiveSecret := testSecrets.MustGetField("OPTIMIZELY_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 optimizely secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Optimizely, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a optimizely 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_Optimizely, 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("Optimizely.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Optimizely.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) } } }) } } trufflehog-3.60.0/pkg/detectors/owlbot/000077500000000000000000000000001451332102400200235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/owlbot/owlbot.go000066400000000000000000000041671451332102400216700ustar00rootroot00000000000000package owlbot import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"owlbot"}) + `\b([a-z0-9]{40})\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{"owlbot"} } // FromData will find and optionally verify Owlbot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Owlbot, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://owlbot.info/api/v4/dictionary/security", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Owlbot } trufflehog-3.60.0/pkg/detectors/owlbot/owlbot_test.go000066400000000000000000000054251451332102400227250ustar00rootroot00000000000000//go:build detectors // +build detectors package owlbot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestOwlbot_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("OWLBOT") inactiveSecret := testSecrets.MustGetField("OWLBOT_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 owlbot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Owlbot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a owlbot 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_Owlbot, 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("Owlbot.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Owlbot.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) } } }) } } trufflehog-3.60.0/pkg/detectors/packagecloud/000077500000000000000000000000001451332102400211375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/packagecloud/packagecloud.go000066400000000000000000000041461451332102400241150ustar00rootroot00000000000000package packagecloud import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"packagecloud"}) + `\b([0-9a-f]{48})\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{"packagecloud"} } // FromData will find and optionally verify PackageCloud 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PackageCloud, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://packagecloud.io/api/v1/repos", nil) if err != nil { continue } req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PackageCloud } trufflehog-3.60.0/pkg/detectors/packagecloud/packagecloud_test.go000066400000000000000000000055421451332102400251550ustar00rootroot00000000000000//go:build detectors // +build detectors package packagecloud import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPackageCloud_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PACKAGECLOUD") inactiveSecret := testSecrets.MustGetField("PACKAGECLOUD_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 packagecloud secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PackageCloud, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a packagecloud 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_PackageCloud, 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("PackageCloud.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PackageCloud.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pagerdutyapikey/000077500000000000000000000000001451332102400217245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pagerdutyapikey/pagerdutyapikey.go000066400000000000000000000054021451332102400254630ustar00rootroot00000000000000package pagerdutyapikey import ( "context" "fmt" "net/http" "regexp" "strings" "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 } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) const verifyURL = "https://api.pagerduty.com/users" var ( defaultClient = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pagerduty", "pager_duty", "pd_", "pd-"}) + `\b([a-zA-Z0-9_+-]{20})\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{"pagerduty", "pager_duty", "pd_", "pd-"} } // FromData will find and optionally verify PagerDutyApiKey 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PagerDutyApiKey, Raw: []byte(resMatch), } if verify { client := s.getClient() isVerified, verificationErr := verifyPagerdutyapikey(ctx, client, resMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(string(s1.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) getClient() *http.Client { if s.client != nil { return s.client } return defaultClient } func verifyPagerdutyapikey(ctx context.Context, client *http.Client, token string) (bool, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, verifyURL, nil) if err != nil { return false, err } req.Header.Add("Accept", "application/vnd.pagerduty+json;version=2") req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Token %s", token)) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() switch res.StatusCode { case http.StatusOK: return true, nil case http.StatusUnauthorized: return false, nil default: return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PagerDutyApiKey } trufflehog-3.60.0/pkg/detectors/pagerdutyapikey/pagerdutyapikey_test.go000066400000000000000000000103611451332102400265220ustar00rootroot00000000000000//go:build detectors // +build detectors package pagerdutyapikey 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 TestPagerDutyApiKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PAGERDUTYAPIKEY_TOKEN") invalidSecret := testSecrets.MustGetField("PAGERDUTYAPIKEY_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pagerdutyapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PagerDutyApiKey, Verified: true, }, }, wantErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pagerdutyapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PagerDutyApiKey, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pagerdutyapikey secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PagerDutyApiKey, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pagerdutyapikey secret %s within but not valid", invalidSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PagerDutyApiKey, 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("PagerDutyApiKey.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Errorf("PagerDutyApiKey.FromData() verificationError = %v, wantVerificationErr %v", got[i].VerificationError, tt.wantVerificationErr) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("PagerDutyApiKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pandadoc/000077500000000000000000000000001451332102400202665ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pandadoc/pandadoc.go000066400000000000000000000042061451332102400223700ustar00rootroot00000000000000package pandadoc import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pandadoc"}) + `\b([a-zA-Z0-9]{40})\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{"pandadoc"} } // FromData will find and optionally verify Pandadoc 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pandadoc, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.pandadoc.com/public/v1/documents", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("API-Key %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pandadoc } trufflehog-3.60.0/pkg/detectors/pandadoc/pandadoc_test.go000066400000000000000000000054721451332102400234350ustar00rootroot00000000000000//go:build detectors // +build detectors package pandadoc import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPandadoc_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("PANDADOC") inactiveSecret := testSecrets.MustGetField("PANDADOC_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 pandadoc secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pandadoc, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pandadoc 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_Pandadoc, 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("Pandadoc.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pandadoc.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pandascore/000077500000000000000000000000001451332102400206345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pandascore/pandascore.go000066400000000000000000000037231451332102400233070ustar00rootroot00000000000000package pandascore import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pandascore"}) + `([ \r\n]{0,1}[0-9A-Za-z\-\_]{51}[ \r\n]{1})`) ) // 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{"pandascore"} } // FromData will find and optionally verify PandaScore 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PandaScore, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", "https://api.pandascore.co/videogames", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { s1.Verified = false } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PandaScore } trufflehog-3.60.0/pkg/detectors/pandascore/pandascore_test.go000066400000000000000000000054311451332102400243440ustar00rootroot00000000000000//go:build detectors // +build detectors package pandascore import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPandaScore_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PANDASCORE") inactiveSecret := testSecrets.MustGetField("PANDASCORE_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 pandascore secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PandaScore, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pandascore 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_PandaScore, 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("PandaScore.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PandaScore.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/paperform/000077500000000000000000000000001451332102400205105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paperform/paperform.go000066400000000000000000000042111451332102400230300ustar00rootroot00000000000000package paperform import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"paperform"}) + `\b([a-z0-9A-Z-._]{850,1000})\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{"paperform"} } // FromData will find and optionally verify Paperform 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Paperform, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.paperform.co/v1/forms", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Paperform } trufflehog-3.60.0/pkg/detectors/paperform/paperform_test.go000066400000000000000000000055041451332102400240750ustar00rootroot00000000000000//go:build detectors // +build detectors package paperform import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPaperform_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PAPERFORM") inactiveSecret := testSecrets.MustGetField("PAPERFORM_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 paperform secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Paperform, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paperform 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_Paperform, 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("Paperform.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Paperform.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) } } }) } } trufflehog-3.60.0/pkg/detectors/paralleldots/000077500000000000000000000000001451332102400212035ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paralleldots/paralleldots.go000066400000000000000000000054311451332102400242230ustar00rootroot00000000000000package paralleldots import ( "bytes" "context" "io" "mime/multipart" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"paralleldots"}) + `\b([0-9A-Za-z]{43})\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{"paralleldots"} } // FromData will find and optionally verify Paralleldots 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_ParallelDots, Raw: []byte(resMatch), } if verify { payload := &bytes.Buffer{} writer := multipart.NewWriter(payload) fw, err := writer.CreateFormField("api_key") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader(resMatch)) if err != nil { continue } fw, err = writer.CreateFormField("text") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader("sample text")) if err != nil { continue } writer.Close() req, err := http.NewRequestWithContext(ctx, "POST", "https://apis.paralleldots.com/v4/intent", bytes.NewReader(payload.Bytes())) if err != nil { continue } req.Header.Add("Content-Type", writer.FormDataContentType()) 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 (res.StatusCode >= 200 && res.StatusCode < 300) && strings.Contains(body, "intent") { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_ParallelDots } trufflehog-3.60.0/pkg/detectors/paralleldots/paralleldots_test.go000066400000000000000000000055421451332102400252650ustar00rootroot00000000000000//go:build detectors // +build detectors package paralleldots import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestParalleldots_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PARALLELDOTS") inactiveSecret := testSecrets.MustGetField("PARALLELDOTS_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 paralleldots secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_ParallelDots, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paralleldots 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_ParallelDots, 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("Paralleldots.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Paralleldots.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) } } }) } } trufflehog-3.60.0/pkg/detectors/parsehub/000077500000000000000000000000001451332102400203265ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/parsehub/parsehub.go000066400000000000000000000041441451332102400224710ustar00rootroot00000000000000package parsehub import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"parsehub"}) + `\b([0-9a-zA-Z]{12})\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{"parsehub"} } // FromData will find and optionally verify Parsehub 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Parsehub, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://www.parsehub.com/api/v2/projects?api_key=%s&limit=1", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Parsehub } trufflehog-3.60.0/pkg/detectors/parsehub/parsehub_test.go000066400000000000000000000054721451332102400235350ustar00rootroot00000000000000//go:build detectors // +build detectors package parsehub import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestParsehub_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PARSEHUB") inactiveSecret := testSecrets.MustGetField("PARSEHUB_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 parsehub secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Parsehub, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a parsehub 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_Parsehub, 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("Parsehub.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Parsehub.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) } } }) } } trufflehog-3.60.0/pkg/detectors/parsers/000077500000000000000000000000001451332102400201745ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/parsers/parsers.go000066400000000000000000000043701451332102400222060ustar00rootroot00000000000000package parsers import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"parsers"}) + `\b([0-9a-z]{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{"parsers"} } // FromData will find and optionally verify Parsers 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Parsers, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"sql": "CREATE TABLE t();SELECT 1; SELECT 1 FROM ; SELECT 2"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.parsers.dev/api/v1/parse/postgresql/", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("x-api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Parsers } trufflehog-3.60.0/pkg/detectors/parsers/parsers_test.go000066400000000000000000000054601451332102400232460ustar00rootroot00000000000000//go:build detectors // +build detectors package parsers import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestParsers_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PARSERS") inactiveSecret := testSecrets.MustGetField("PARSERS_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 parsers secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Parsers, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a parsers 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_Parsers, 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("Parsers.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Parsers.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) } } }) } } trufflehog-3.60.0/pkg/detectors/parseur/000077500000000000000000000000001451332102400201765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/parseur/parseur.go000066400000000000000000000041441451332102400222110ustar00rootroot00000000000000package parseur import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"parseur"}) + `\b([a-f0-9]{40})\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{"parseur"} } // FromData will find and optionally verify Parseur 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Parseur, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.parseur.com/", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Parseur } trufflehog-3.60.0/pkg/detectors/parseur/parseur_test.go000066400000000000000000000054061451332102400232520ustar00rootroot00000000000000package parseur import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestParseur_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PARSEUR") inactiveSecret := testSecrets.MustGetField("PARSEUR_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 parseur secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Parseur, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a parseur 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_Parseur, 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("Parseur.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Parseur.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) } } }) } } trufflehog-3.60.0/pkg/detectors/partnerstack/000077500000000000000000000000001451332102400212165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/partnerstack/partnerstack.go000066400000000000000000000043211451332102400242460ustar00rootroot00000000000000package partnerstack import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"partnerstack"}) + `\b([0-9A-Za-z]{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{"partnerstack"} } // FromData will find and optionally verify Partnerstack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Partnerstack, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.partnerstack.com/api/v2/partnerships", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Partnerstack } trufflehog-3.60.0/pkg/detectors/partnerstack/partnerstack_test.go000066400000000000000000000055421451332102400253130ustar00rootroot00000000000000//go:build detectors // +build detectors package partnerstack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPartnerstack_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("PARTNERSTACK") inactiveSecret := testSecrets.MustGetField("PARTNERSTACK_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 partnerstack secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Partnerstack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a partnerstack 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_Partnerstack, 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("Partnerstack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Partnerstack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/passbase/000077500000000000000000000000001451332102400203165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/passbase/passbase.go000066400000000000000000000041461451332102400224530ustar00rootroot00000000000000package passbase import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"passbase"}) + `\b([a-zA-Z0-9]{128})\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{"passbase"} } // FromData will find and optionally verify Passbase 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Passbase, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.passbase.com/verification/v1/settings", nil) if err != nil { continue } req.Header.Add("X-API-KEY", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Passbase } trufflehog-3.60.0/pkg/detectors/passbase/passbase_test.go000066400000000000000000000054721451332102400235150ustar00rootroot00000000000000//go:build detectors // +build detectors package passbase import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPassbase_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PASSBASE") inactiveSecret := testSecrets.MustGetField("PASSBASE_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 passbase secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Passbase, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a passbase 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_Passbase, 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("Passbase.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Passbase.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pastebin/000077500000000000000000000000001451332102400203225ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pastebin/pastebin.go000066400000000000000000000054271451332102400224660ustar00rootroot00000000000000package pastebin import ( "bytes" "context" "io" "mime/multipart" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pastebin"}) + `\b([a-zA-Z0-9_]{32})\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{"pastebin"} } // FromData will find and optionally verify Pastebin 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pastebin, Raw: []byte(resMatch), } if verify { body := &bytes.Buffer{} writer := multipart.NewWriter(body) fw, err := writer.CreateFormField("api_dev_key") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader(resMatch)) if err != nil { continue } fw, err = writer.CreateFormField("api_paste_code") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader("test")) if err != nil { continue } fw, err = writer.CreateFormField("api_option") if err != nil { continue } _, err = io.Copy(fw, strings.NewReader("paste")) if err != nil { continue } writer.Close() req, err := http.NewRequestWithContext(ctx, "POST", "https://pastebin.com/api/api_post.php", bytes.NewReader(body.Bytes())) if err != nil { continue } req.Header.Add("Content-Type", writer.FormDataContentType()) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pastebin } trufflehog-3.60.0/pkg/detectors/pastebin/pastebin_test.go000066400000000000000000000054721451332102400235250ustar00rootroot00000000000000//go:build detectors // +build detectors package pastebin import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPastebin_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("PASTEBIN") inactiveSecret := testSecrets.MustGetField("PASTEBIN_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 pastebin secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pastebin, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pastebin 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_Pastebin, 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("Pastebin.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pastebin.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) } } }) } } trufflehog-3.60.0/pkg/detectors/paydirtapp/000077500000000000000000000000001451332102400206725ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paydirtapp/paydirtapp.go000066400000000000000000000041131451332102400233750ustar00rootroot00000000000000package paydirtapp import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"paydirtapp"}) + `\b([a-z0-9]{32})\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{"paydirtapp"} } // FromData will find and optionally verify paydirtapp 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Paydirtapp, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://paydirtapp.com/api/v1/clients?api_key="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Paydirtapp } trufflehog-3.60.0/pkg/detectors/paydirtapp/paydirtapp_test.go000066400000000000000000000055211451332102400244400ustar00rootroot00000000000000//go:build detectors // +build detectors package paydirtapp import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPaydirtyapp_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PAYDIRTAPP") inactiveSecret := testSecrets.MustGetField("PAYDIRTAPP_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 paydirtapp secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Paydirtapp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paydirtapp 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_Paydirtapp, 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("Paydirtyapp.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Paydirtyapp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/paymoapp/000077500000000000000000000000001451332102400203435ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paymoapp/paymoapp.go000066400000000000000000000042471451332102400225270ustar00rootroot00000000000000package paymoapp import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"paymoapp"}) + `\b([a-zA-Z0-9]{44})\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{"paymoapp"} } // FromData will find and optionally verify Paymoapp 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Paymoapp, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://app.paymoapp.com/api/me", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Paymoapp } trufflehog-3.60.0/pkg/detectors/paymoapp/paymoapp_test.go000066400000000000000000000054721451332102400235670ustar00rootroot00000000000000//go:build detectors // +build detectors package paymoapp import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPaymoapp_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PAYMOAPP") inactiveSecret := testSecrets.MustGetField("PAYMOAPP_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 paymoapp secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Paymoapp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paymoapp 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_Paymoapp, 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("Paymoapp.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Paymoapp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/paymongo/000077500000000000000000000000001451332102400203465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paymongo/paymongo.go000066400000000000000000000043421451332102400225310ustar00rootroot00000000000000package paymongo import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"paymongo"}) + `\b([a-zA-Z0-9_]{32})\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{"paymongo"} } // FromData will find and optionally verify Paymongo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Paymongo, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"data":{"attributes":{"type":"paymaya"}}}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.paymongo.com/v1/payment_methods", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.SetBasicAuth(resMatch, "") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Paymongo } trufflehog-3.60.0/pkg/detectors/paymongo/paymongo_test.go000066400000000000000000000054721451332102400235750ustar00rootroot00000000000000//go:build detectors // +build detectors package paymongo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPaymongo_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("PAYMONGO") inactiveSecret := testSecrets.MustGetField("PAYMONGO_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 paymongo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Paymongo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paymongo 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_Paymongo, 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("Paymongo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Paymongo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/paypaloauth/000077500000000000000000000000001451332102400210445ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paypaloauth/paypaloauth.go000066400000000000000000000055171451332102400237320ustar00rootroot00000000000000package paypaloauth import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. idPat = regexp.MustCompile(`\b([A-Za-z0-9_\.]{7}-[A-Za-z0-9_\.]{72}|[A-Za-z0-9_\.]{5}-[A-Za-z0-9_\.]{38})\b`) keyPat = regexp.MustCompile(`\b([A-Za-z0-9_\.\-]{44,80})\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{"paypal"} } // FromData will find and optionally verify PaypalOauth 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) idmatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, idMatch := range idmatches { if len(idMatch) != 2 { continue } resIDMatch := strings.TrimSpace(idMatch[1]) for _, secretMatch := range matches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PaypalOauth, Raw: []byte(resSecretMatch), } if verify { data := fmt.Sprintf("%s:%s", resIDMatch, resSecretMatch) encoded := b64.StdEncoding.EncodeToString([]byte(data)) payload := strings.NewReader("grant_type=client_credentials") req, err := http.NewRequestWithContext(ctx, "POST", "https://api-m.sandbox.paypal.com/v1/oauth2/token", payload) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Accept-Language", "en_US") req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", encoded)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resIDMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PaypalOauth } trufflehog-3.60.0/pkg/detectors/paypaloauth/paypaloauth_test.go000066400000000000000000000074571451332102400247760ustar00rootroot00000000000000//go:build detectors // +build detectors package paypaloauth import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPaypalOauth_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PAYPALOAUTH_SECRET") inactiveSecret := testSecrets.MustGetField("PAYPALOAUTH_SECRET_INACTIVE") id := testSecrets.MustGetField("PAYPALOAUTH_CLIENTID") newId := testSecrets.MustGetField("PAYPALOAUTH_NEW_INACTIVE_CLIENTID") newSecret := testSecrets.MustGetField("PAYPALOAUTH_NEW_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 a paypaloauth secret %s within %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PaypalOauth, Verified: true, }, { DetectorType: detectorspb.DetectorType_PaypalOauth, Verified: false, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paypaloauth secret %s within %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PaypalOauth, Verified: false, }, { DetectorType: detectorspb.DetectorType_PaypalOauth, Verified: false, }, }, wantErr: false, }, { name: "new format, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paypaloauth secret %s within %s but not valid", newSecret, newId)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PaypalOauth, Verified: false, }, { DetectorType: detectorspb.DetectorType_PaypalOauth, 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("PaypalOauth.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PaypalOauth.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) } } }) } } trufflehog-3.60.0/pkg/detectors/paystack/000077500000000000000000000000001451332102400203345ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/paystack/paystack.go000066400000000000000000000035641451332102400225120ustar00rootroot00000000000000package paystack import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // TODO: support live key keyPat = regexp.MustCompile(`\b(sk\_[a-z]{1,}\_[A-Za-z0-9]{40})\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{"paystack"} } // FromData will find and optionally verify Paystack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Paystack, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.paystack.co/customer", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, false) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Paystack } trufflehog-3.60.0/pkg/detectors/paystack/paystack_test.go000066400000000000000000000054031451332102400235430ustar00rootroot00000000000000//go:build detectors // +build detectors package paystack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPaystack_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PAYSTACK_TOKEN") inactiveSecret := testSecrets.MustGetField("PAYSTACK_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 paystack secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Paystack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a paystack secret %s within but unverified", inactiveSecret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Paystack, 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("Paystack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Paystack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pdflayer/000077500000000000000000000000001451332102400203235ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pdflayer/pdflayer.go000066400000000000000000000046251451332102400224670ustar00rootroot00000000000000package pdflayer import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pdflayer"}) + `\b([a-z0-9]{32})\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{"pdflayer"} } // FromData will find and optionally verify PdfLayer 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PdfLayer, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.pdflayer.com/api/convert?access_key=%s&document_url=https://pdflayer.com/downloads/invoice.html", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { bodyBytes, err := io.ReadAll(res.Body) if err == nil { bodyString := string(bodyBytes) validResponse := strings.Contains(bodyString, `Contents`) defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { if validResponse { s1.Verified = true } else { s1.Verified = false } } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PdfLayer } trufflehog-3.60.0/pkg/detectors/pdflayer/pdflayer_test.go000066400000000000000000000054721451332102400235270ustar00rootroot00000000000000//go:build detectors // +build detectors package pdflayer import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPdfLayer_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("PDFLAYER") inactiveSecret := testSecrets.MustGetField("PDFLAYER_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 pdflayer secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PdfLayer, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pdflayer 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_PdfLayer, 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("PdfLayer.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PdfLayer.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pdfshift/000077500000000000000000000000001451332102400203245ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pdfshift/pdfshift.go000066400000000000000000000042131451332102400224620ustar00rootroot00000000000000package pdfshift import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pdfshift"}) + `\b([0-9a-f]{32})\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{"pdfshift"} } // FromData will find and optionally verify PdfShift 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PdfShift, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.pdfshift.io/v3/credits/usage", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.SetBasicAuth("api", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PdfShift } trufflehog-3.60.0/pkg/detectors/pdfshift/pdfshift_test.go000066400000000000000000000054721451332102400235310ustar00rootroot00000000000000//go:build detectors // +build detectors package pdfshift import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPdfShift_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("PDFSHIFT") inactiveSecret := testSecrets.MustGetField("PDFSHIFT_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 pdfshift secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PdfShift, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pdfshift 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_PdfShift, 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("PdfShift.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PdfShift.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) } } }) } } trufflehog-3.60.0/pkg/detectors/peopledatalabs/000077500000000000000000000000001451332102400214755ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/peopledatalabs/peopledatalabs.go000066400000000000000000000043031451332102400250040ustar00rootroot00000000000000package peopledatalabs import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"peopledatalabs"}) + `\b([a-z0-9]{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{"peopledatalabs"} } // FromData will find and optionally verify PeopleDataLabs 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PeopleDataLabs, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.peopledatalabs.com/v5/person/enrich?min_likelihood=6&profile=https://linkedin.com/in/williamhgates", nil) if err != nil { continue } req.Header.Add("X-Api-Key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PeopleDataLabs } trufflehog-3.60.0/pkg/detectors/peopledatalabs/peopledatalabs_test.go000066400000000000000000000055661451332102400260570ustar00rootroot00000000000000//go:build detectors // +build detectors package peopledatalabs import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPeopleDataLabs_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PEOPLEDATALABS") inactiveSecret := testSecrets.MustGetField("PEOPLEDATALABS_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 peopledatalabs secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PeopleDataLabs, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a peopledatalabs 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_PeopleDataLabs, 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("PeopleDataLabs.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PeopleDataLabs.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pepipost/000077500000000000000000000000001451332102400203605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pepipost/pepipost.go000066400000000000000000000042171451332102400225560ustar00rootroot00000000000000package pepipost import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pepipost", "netcore"}) + `\b([a-zA-Z-0-9]{32})\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{"pepipost", "netcore"} } // FromData will find and optionally verify Pepipost 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pepipost, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.pepipost.com/v5.1/domain/getDomains?domain=pepisandbox.com", nil) if err != nil { continue } req.Header.Add("api_key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pepipost } trufflehog-3.60.0/pkg/detectors/pepipost/pepipost_test.go000066400000000000000000000055001451332102400236110ustar00rootroot00000000000000//go:build detectors // +build detectors package pepipost import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPepipost_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PEPIPOST_TOKEN") inactiveSecret := testSecrets.MustGetField("PEPIPOST_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 pepipost secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pepipost, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pepipost 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_Pepipost, 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("Pepipost.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pepipost.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) } } }) } } trufflehog-3.60.0/pkg/detectors/percy/000077500000000000000000000000001451332102400176375ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/percy/percy.go000066400000000000000000000042651451332102400213170ustar00rootroot00000000000000package percy import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"percy"}) + `\bPERCY_TOKEN=([0-9Aa-f]{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{"percy"} } // FromData will find and optionally verify Percy 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Percy, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://percy.io/api/v1/projects", nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.percy+json; version=3") req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Percy } trufflehog-3.60.0/pkg/detectors/percy/percy_test.go000066400000000000000000000054451451332102400223570ustar00rootroot00000000000000//go:build detectors // +build detectors package percy import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPercy_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PERCY_VERIFIED") inactiveSecret := testSecrets.MustGetField("PERCY_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 percy secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Percy, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a percy 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_Percy, 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("Percy.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Percy.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pinata/000077500000000000000000000000001451332102400177715ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pinata/pinata.go000066400000000000000000000055421451332102400216020ustar00rootroot00000000000000package pinata import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pinata"}) + `\b([0-9a-z]{64})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pinata"}) + `\b([0-9a-z]{20})\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{"pinata"} } // FromData will find and optionally verify Pinata 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } resIdMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pinata, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout payload := strings.NewReader(`{"pinataMetadata": {"name": "ItemStatus","keyvalues": {"ItemID": "Item001","CheckpointID": "Checkpoint002","Source": "CompanyA","WeightInKilos": 5.25}},"pinataContent": {"itemName": "exampleItemName","inspectedBy": "Inspector001","dataValues": []}}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.pinata.cloud/pinning/pinJSONToIPFS", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") req.Header.Add("pinata_api_key", resIdMatch) req.Header.Add("pinata_secret_api_key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pinata } trufflehog-3.60.0/pkg/detectors/pinata/pinata_test.go000066400000000000000000000055631451332102400226440ustar00rootroot00000000000000//go:build detectors // +build detectors package pinata import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPinata_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PINATA") key := testSecrets.MustGetField("PINATA_KEY") inactiveSecret := testSecrets.MustGetField("PINATA_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 pinata secret %s within pinata %s", secret, key)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pinata, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pinata secret %s within pinata %s but not valid", inactiveSecret, key)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pinata, 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("Pinata.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pinata.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pipedream/000077500000000000000000000000001451332102400204635ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pipedream/pipedream.go000066400000000000000000000042641451332102400227660ustar00rootroot00000000000000package pipedream import ( "context" "fmt" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pipedream"}) + `\b([a-z0-9]{32})\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{"pipedream"} } // FromData will find and optionally verify Pipedream 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pipedream, Raw: []byte(resMatch), } if verify { timeout := 15 * time.Second client.Timeout = timeout req, err := http.NewRequest("GET", "https://api.pipedream.com/v1/users/me", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pipedream } trufflehog-3.60.0/pkg/detectors/pipedream/pipedream_test.go000066400000000000000000000054161451332102400240250ustar00rootroot00000000000000//go:build detectors // +build detectors package pipedream import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPipedream_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PIPEDREAM") inactiveSecret := testSecrets.MustGetField("PIPEDREAM_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 pipedream secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pipedream, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pipedream 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_Pipedream, 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("Pipedream.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pipedream.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/pipedrive/000077500000000000000000000000001451332102400205045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pipedrive/pipedrive.go000066400000000000000000000042341451332102400230250ustar00rootroot00000000000000package pipedrive import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pipedrive"}) + `\b([a-zA-Z0-9]{40})\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{"pipedrive"} } // FromData will find and optionally verify Pipedrive 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pipedrive, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://hooman.pipedrive.com/api/v1/users?api_token="+resMatch, nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pipedrive } trufflehog-3.60.0/pkg/detectors/pipedrive/pipedrive_test.go000066400000000000000000000055121451332102400240640ustar00rootroot00000000000000//go:build detectors // +build detectors package pipedrive import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPipedrive_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PIPEDRIVE_TOKEN") inactiveSecret := testSecrets.MustGetField("PIPEDRIVE_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 pipedrive secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pipedrive, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pipedrive 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_Pipedrive, 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("Pipedrive.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pipedrive.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pivotaltracker/000077500000000000000000000000001451332102400215475ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pivotaltracker/pivotaltracker.go000066400000000000000000000041331451332102400251310ustar00rootroot00000000000000package pivotaltracker import ( "context" "net/http" "regexp" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // Get token at https://www.pivotaltracker.com/profile keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pivotal"}) + `([a-z0-9]{32})`) ) // 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{"pivotal"} } // FromData will find and optionally verify PivotalTracker 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) for _, match := range matches { // First match is entire regex, second is the first group. if len(match) != 2 { continue } token := match[1] s := detectors.Result{ DetectorType: detectorspb.DetectorType_PivotalTracker, Raw: []byte(token), } if verify { client := common.SaneHttpClient() // https://www.pivotaltracker.com/help/api/rest/v5#top req, err := http.NewRequestWithContext(ctx, "GET", "https://www.pivotaltracker.com/services/v5/me", nil) if err != nil { continue } req.Header.Add("Content-Type", "application/json; charset=utf-8") req.Header.Add("X-TrackerToken", token) res, err := client.Do(req) if err == nil { res.Body.Close() // The request body is unused. if res.StatusCode >= 200 && res.StatusCode < 300 { s.Verified = true } } } if !s.Verified && detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) { continue } results = append(results, s) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PivotalTracker } trufflehog-3.60.0/pkg/detectors/pivotaltracker/pivotaltracker_test.go000066400000000000000000000054101451332102400261670ustar00rootroot00000000000000//go:build detectors // +build detectors package pivotaltracker import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPivotalTracker_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PIVOTALTRACKER") secretInactive := testSecrets.MustGetField("PIVOTALTRACKER_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 pivotal secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PivotalTracker, Verified: true, }, }, wantErr: false, }, { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pivotal secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PivotalTracker, 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("PivotalTracker.FromData() error = %v, wantErr %v", err, tt.wantErr) return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PivotalTracker.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pixabay/000077500000000000000000000000001451332102400201525ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pixabay/pixabay.go000066400000000000000000000041421451332102400221370ustar00rootroot00000000000000package pixabay import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pixabay"}) + `\b([a-z0-9-]{34})\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{"pixabay"} } // FromData will find and optionally verify Pixabay 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Pixabay, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://pixabay.com/api/?key=%s&q=yellow+flowers&image_type=photo", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Pixabay } trufflehog-3.60.0/pkg/detectors/pixabay/pixabay_test.go000066400000000000000000000054601451332102400232020ustar00rootroot00000000000000//go:build detectors // +build detectors package pixabay import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPixabay_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("PIXABAY") inactiveSecret := testSecrets.MustGetField("PIXABAY_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 pixabay secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Pixabay, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pixabay 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_Pixabay, 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("Pixabay.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Pixabay.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) } } }) } } trufflehog-3.60.0/pkg/detectors/plaidkey/000077500000000000000000000000001451332102400203175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/plaidkey/plaidkey.go000066400000000000000000000056401451332102400224550ustar00rootroot00000000000000package plaidkey import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"plaid"}) + `\b([a-z0-9]{30})\b`) idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"plaid"}) + `\b([a-z0-9]{24})\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{"plaid"} } // FromData will find and optionally verify PlaidKey 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } idresMatch := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PlaidKey, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(`{"client_id":"` + idresMatch + `","secret":"` + resMatch + `","user":{"client_user_id":"60e3ee4019a2660010f8bc54","phone_number_verified_time":"0001-01-01T00:00:00Z","email_address_verified_time":"0001-01-01T00:00:00Z"},"client_name":"Plaid Test App","products":["auth","transactions"],"country_codes":["US"],"webhook":"https://webhook-uri.com","account_filters":{"depository":{"account_subtypes":["checking","savings"]}},"language":"en","link_customization_name":"default"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://development.plaid.com/link/token/create", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PlaidKey } trufflehog-3.60.0/pkg/detectors/plaidkey/plaidkey_test.go000066400000000000000000000056351451332102400235200ustar00rootroot00000000000000//go:build detectors // +build detectors package plaidkey import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPlaidKey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PLAIDKEY_SECRET") inactiveSecret := testSecrets.MustGetField("PLAIDKEY_SECRET_INACTIVE") id := testSecrets.MustGetField("PLAIDKEY_CLIENTID") 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 plaidkey secret %s within plaidkey %s", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlaidKey, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a plaidkey secret %s within but plaidkey %s not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlaidKey, 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("PlaidKey.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PlaidKey.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) } } }) } } trufflehog-3.60.0/pkg/detectors/planetscale/000077500000000000000000000000001451332102400210105ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/planetscale/planetscale.go000066400000000000000000000043121451332102400236320ustar00rootroot00000000000000package planetscale import ( "context" "fmt" "net/http" "regexp" "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 } var ( defaultClient = common.SaneHttpClient() usernamePat = regexp.MustCompile(`\b[a-z0-9]{12}\b`) passwordPat = regexp.MustCompile(`\bpscale_tkn_[A-Za-z0-9_]{43}\b`) ) // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) // Keywords are used for efficiently pre-filtering chunks. func (s Scanner) Keywords() []string { return []string{"pscale_tkn_"} } func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { dataStr := string(data) usernameMatches := usernamePat.FindAllString(dataStr, -1) passwordMatches := passwordPat.FindAllString(dataStr, -1) for _, username := range usernameMatches { for _, password := range passwordMatches { credentials := fmt.Sprintf("%s:%s", username, password) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PlanetScale, Raw: []byte(credentials), } if verify { client := s.client if client == nil { client = defaultClient } // Construct HTTP request req, err := http.NewRequestWithContext(ctx, "GET", "https://api.planetscale.com/v1/organizations", nil) if err != nil { continue } req.Header.Set("Authorization", credentials) req.Header.Set("accept", "application/json") // Send HTTP request res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else if res.StatusCode == 401 { // The secret is determinately not verified s1.Verified = false } else { s1.VerificationError = fmt.Errorf("unexpected status code %d", res.StatusCode) } } else { s1.VerificationError = err } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PlanetScale } trufflehog-3.60.0/pkg/detectors/planetscale/planetscale_test.go000066400000000000000000000110331451332102400246670ustar00rootroot00000000000000//go:build detectors // +build detectors package planetscale 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 TestPlanetscale_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PLANET_SCALE_TOKEN") secretID := testSecrets.MustGetField("PLANET_SCALE_ID") inactiveSecret := testSecrets.MustGetField("PLANET_SCALE_TOKEN_INACTIVE") inactiveSecretID := testSecrets.MustGetField("PLANET_SCALE_ID_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planetscale secret %s within with id %s", secret, secretID)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanetScale, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planetscale secret %s within with id %s but not valid", inactiveSecret, inactiveSecretID)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanetScale, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planetscale secret %s within with id %s", secret, secretID)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanetScale, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planetscale secret %s within with id %s", secret, secretID)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanetScale, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, } 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("Planetscale.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Planetscale.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) } } }) } } trufflehog-3.60.0/pkg/detectors/planetscaledb/000077500000000000000000000000001451332102400213165ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/planetscaledb/planetscaledb.go000066400000000000000000000042161451332102400244510ustar00rootroot00000000000000package planetscaledb import ( "context" "database/sql" "regexp" "strings" "github.com/go-sql-driver/mysql" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( usernamePat = regexp.MustCompile(`\b[a-z0-9]{20}\b`) passwordPat = regexp.MustCompile(`\bpscale_pw_[A-Za-z0-9_]{43}\b`) hostPat = regexp.MustCompile(`\b(aws|gcp)\.connect\.psdb\.cloud\b`) ) // Keywords are used for efficiently pre-filtering chunks. func (s Scanner) Keywords() []string { return []string{"pscale_pw_"} } // FromData will find and optionally verify Planetscaledb 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) usernameMatches := usernamePat.FindAllStringSubmatch(dataStr, -1) passwordMatches := passwordPat.FindAllStringSubmatch(dataStr, -1) hostMatches := hostPat.FindAllString(dataStr, -1) for _, username := range usernameMatches { for _, password := range passwordMatches { for _, host := range hostMatches { s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PlanetScaleDb, Raw: []byte(strings.Join([]string{host, username[0], password[0]}, "\t")), } if verify { cfg := mysql.Config{ User: username[0], Passwd: password[0], Net: "tcp", Addr: host, TLSConfig: "true", // assuming SSL is required AllowNativePasswords: true, } db, err := sql.Open("mysql", cfg.FormatDSN()) if err != nil { s1.VerificationError = err } else { err = db.PingContext(ctx) if err == nil { s1.Verified = true } else { s1.VerificationError = err } db.Close() } } results = append(results, s1) } } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PlanetScaleDb } trufflehog-3.60.0/pkg/detectors/planetscaledb/planetscaledb_test.go000066400000000000000000000066751451332102400255230ustar00rootroot00000000000000//go:build detectors // +build detectors package planetscaledb 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 TestPlanetscaledb_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } username := testSecrets.MustGetField("PLANET_SCALEDB_USERNAME") host := testSecrets.MustGetField("PLANET_SCALEDB_HOST") password := testSecrets.MustGetField("PLANET_SCALEDB_PASSWORD") inactivePassword := testSecrets.MustGetField("PLANET_SCALEDB_PASSWORD_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planetscaledb secret %s %s %s", username, password, host)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanetScaleDb, Verified: true, }, }, wantErr: false, wantVerificationErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planetscaledb secret %s %s %s", username, inactivePassword, host)), }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanetScaleDb, Verified: false, }, }, wantErr: false, wantVerificationErr: 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, wantVerificationErr: 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("Planetscaledb.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Planetscaledb.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) } } }) } } trufflehog-3.60.0/pkg/detectors/planviewleankit/000077500000000000000000000000001451332102400217125ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/planviewleankit/planviewleankit.go000066400000000000000000000051701451332102400254410ustar00rootroot00000000000000package planviewleankit import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"planviewleankit", "planview"}) + `\b([0-9a-f]{128})\b`) subDomainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"planviewleankit", "planview"}) + `(?:subdomain).\b([a-zA-Z][a-zA-Z0-9.-]{1,23}[a-zA-Z0-9])\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{"planviewleankit", "planview"} } // FromData will find and optionally verify PlanviewLeanKit 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) subdomainMatches := subDomainPat.FindAllStringSubmatch(dataStr, -1) for _, subdomainMatch := range subdomainMatches { if len(subdomainMatch) != 2 { continue } resSubdomainMatch := strings.TrimSpace(subdomainMatch[1]) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PlanviewLeanKit, Raw: []byte(resMatch), } if verify { req, err := http.NewRequest("GET", fmt.Sprintf("https://%s.leankit.com/io/account", resSubdomainMatch), nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PlanviewLeanKit } trufflehog-3.60.0/pkg/detectors/planviewleankit/planviewleankit_test.go000066400000000000000000000060741451332102400265040ustar00rootroot00000000000000//go:build detectors // +build detectors package planviewleankit import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPlanviewLeanKit_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PLANVIEWLEANKIT") inactiveSecret := testSecrets.MustGetField("PLANVIEWLEANKIT_INACTIVE") subdomain := testSecrets.MustGetField("PLANVIEWLEANKIT_SUBDOMAIN") // log.Println(secret) // log.Println(inactiveSecret) // log.Println(subdomain) 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 planviewleankit subdomain %s with planviewleankit secret %s within", subdomain, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanviewLeanKit, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planviewleankit subdomain %s with planviewleankit secret %s within but not valid", subdomain, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PlanviewLeanKit, 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("PlanviewLeanKit.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PlanviewLeanKit.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++ { s.FromData(ctx, false, data) } }) } } trufflehog-3.60.0/pkg/detectors/planyo/000077500000000000000000000000001451332102400200175ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/planyo/planyo.go000066400000000000000000000046221451332102400216540ustar00rootroot00000000000000package planyo import ( "context" "fmt" "io" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"planyo"}) + `\b([0-9a-z]{62})\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{"planyo"} } // FromData will find and optionally verify Planyo 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Planyo, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://www.planyo.com/rest/?method=get_site_info&api_key=%s", resMatch), nil) if err != nil { continue } req.Header.Add("Accept", "application/vnd.planyo+json; version=3") 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) validResponse := strings.Contains(body, "data") || strings.Contains(body, "Your Planyo site has expired") if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Planyo } trufflehog-3.60.0/pkg/detectors/planyo/planyo_test.go000066400000000000000000000054461451332102400227200ustar00rootroot00000000000000//go:build detectors // +build detectors package planyo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPlanyo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PLANYO") inactiveSecret := testSecrets.MustGetField("PLANYO_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 planyo secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Planyo, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a planyo 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_Planyo, 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("Planyo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Planyo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/plivo/000077500000000000000000000000001451332102400176465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/plivo/plivo.go000066400000000000000000000046501451332102400213330ustar00rootroot00000000000000package plivo import ( "context" b64 "encoding/base64" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"plivo"}) + `\b([A-Z]{20})\b`) keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"plivo"}) + `\b([A-Za-z0-9_-]{40})\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{"plivo"} } // FromData will find and optionally verify Plivo 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) idMatches := idPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, idMatch := range idMatches { if len(idMatch) != 2 { continue } id := strings.TrimSpace(idMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Plivo, Redacted: id, Raw: []byte(resMatch), RawV2: []byte(resMatch + id), } stringResMatch := fmt.Sprintf("%s:%s", id, resMatch) decodeSecret := b64.StdEncoding.EncodeToString([]byte(stringResMatch)) if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.plivo.com/v1/Account/"+id+"/Number/", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Basic %s", decodeSecret)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Plivo } trufflehog-3.60.0/pkg/detectors/plivo/plivo_test.go000066400000000000000000000057341451332102400223760ustar00rootroot00000000000000//go:build detectors // +build detectors package plivo import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPlivo_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PLIVO_TOKEN") inactiveSecret := testSecrets.MustGetField("PLIVO_INACTIVE") id := testSecrets.MustGetField("PLIVO_ID") 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 plivo secret %s within https://api.plivo.com/v1/Account/%s/Number/", secret, id)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Plivo, Redacted: id, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a plivo secret %s within https://api.plivo.com/v1/Account/%s/Number/ but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Plivo, Redacted: id, 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("Plivo.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Plivo.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) } } }) } } trufflehog-3.60.0/pkg/detectors/podio/000077500000000000000000000000001451332102400176275ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/podio/podio.go000066400000000000000000000041371451332102400212750ustar00rootroot00000000000000package podio import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"podio"}) + `\b([[0-9a-z]{32})\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{"podio"} } // FromData will find and optionally verify Podio 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Podio, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.podio.com/user", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Podio } trufflehog-3.60.0/pkg/detectors/podio/podio_test.go000066400000000000000000000054341451332102400223350ustar00rootroot00000000000000//go:build detectors // +build detectors package podio import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPodio_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PODIO") inactiveSecret := testSecrets.MustGetField("PODIO_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 podio secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Podio, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a podio 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_Podio, 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("Podio.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Podio.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) } } }) } } trufflehog-3.60.0/pkg/detectors/pollsapi/000077500000000000000000000000001451332102400203405ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/pollsapi/pollsapi.go000066400000000000000000000041431451332102400225140ustar00rootroot00000000000000package pollsapi import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"pollsapi"}) + `\b([A-Z0-9]{28})\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{"pollsapi"} } // FromData will find and optionally verify PollsAPI 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PollsAPI, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.pollsapi.com/v1/get/polls?offset=0&limit=10", nil) if err != nil { continue } req.Header.Add("api-key", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PollsAPI } trufflehog-3.60.0/pkg/detectors/pollsapi/pollsapi_test.go000066400000000000000000000054721451332102400235610ustar00rootroot00000000000000//go:build detectors // +build detectors package pollsapi import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPollsAPI_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POLLSAPI") inactiveSecret := testSecrets.MustGetField("POLLSAPI_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 pollsapi secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PollsAPI, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a pollsapi 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_PollsAPI, 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("PollsAPI.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PollsAPI.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) } } }) } } trufflehog-3.60.0/pkg/detectors/poloniex/000077500000000000000000000000001451332102400203525ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/poloniex/poloniex.go000066400000000000000000000064311451332102400225420ustar00rootroot00000000000000package poloniex import ( "context" "crypto/hmac" "crypto/sha512" "encoding/hex" "net/http" "net/url" "regexp" "strconv" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"poloniex"}) + `\b([0-9A-Z]{8}-[0-9A-Z]{8}-[0-9A-Z]{8}-[0-9A-Z]{8})\b`) secretPat = regexp.MustCompile(detectors.PrefixRegex([]string{"poloniex"}) + `\b([0-9a-f]{128})\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{"poloniex"} } // FromData will find and optionally verify Poloniex 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) secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) for _, secretMatch := range secretMatches { if len(secretMatch) != 2 { continue } resSecretMatch := strings.TrimSpace(secretMatch[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Poloniex, Raw: []byte(resSecretMatch), RawV2: []byte(resMatch + resSecretMatch), } if verify { timestamp := strconv.FormatInt(time.Now().Unix()*1000, 10) payload := url.Values{} payload.Add("command", "returnBalances") payload.Add("nonce", timestamp) signature := getPoloniexSignature(resSecretMatch, payload.Encode()) req, err := http.NewRequestWithContext(ctx, "POST", "https://poloniex.com/tradingApi", strings.NewReader(payload.Encode())) if err != nil { continue } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Key", resMatch) req.Header.Add("Sign", signature) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } if detectors.IsKnownFalsePositive(resSecretMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } } return results, nil } func getPoloniexSignature(secret string, payload string) string { mac := hmac.New(sha512.New, []byte(secret)) mac.Write([]byte(payload)) macsum := mac.Sum(nil) return hex.EncodeToString(macsum) } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Poloniex } trufflehog-3.60.0/pkg/detectors/poloniex/poloniex_test.go000066400000000000000000000057511451332102400236050ustar00rootroot00000000000000//go:build detectors // +build detectors package poloniex import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPoloniex_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } key := testSecrets.MustGetField("POLONIEX_KEY") inactiveKey := testSecrets.MustGetField("POLONIEX_KEY_INACTIVE") secret := testSecrets.MustGetField("POLONIEX") inactiveSecret := testSecrets.MustGetField("POLONIEX_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 poloniex key %s with poloniex secret %s within", key, secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Poloniex, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a poloniex key %s with poloniex secret %s within but not valid", inactiveKey, inactiveSecret)), // the secret would satisfy the regex but not pass validation verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Poloniex, 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("Poloniex.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Poloniex.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) } } }) } } trufflehog-3.60.0/pkg/detectors/polygon/000077500000000000000000000000001451332102400202045ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/polygon/polygon.go000066400000000000000000000041021451332102400222170ustar00rootroot00000000000000package polygon import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"polygon"}) + `\b([a-z0-9A-Z]{32})\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{"polygon"} } // FromData will find and optionally verify Polygon 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Polygon, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.polygon.io/v2/reference/locales?apiKey="+resMatch, nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Polygon } trufflehog-3.60.0/pkg/detectors/polygon/polygon_test.go000066400000000000000000000054661451332102400232740ustar00rootroot00000000000000//go:build detectors // +build detectors package polygon import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPolygon_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POLYGON_TOKEN") inactiveSecret := testSecrets.MustGetField("POLYGON_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 polygon secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Polygon, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a polygon 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_Polygon, 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("Polygon.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Polygon.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) } } }) } } trufflehog-3.60.0/pkg/detectors/positionstack/000077500000000000000000000000001451332102400214075ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/positionstack/positionstack.go000066400000000000000000000042201451332102400246260ustar00rootroot00000000000000package positionstack import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"positionstack"}) + `\b([a-zA-Z0-9_]{32})\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{"positionstack"} } // FromData will find and optionally verify PositionStack 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PositionStack, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.positionstack.com/v1/forward?access_key=%s&query=Philippines", resMatch), nil) if err != nil { continue } res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PositionStack } trufflehog-3.60.0/pkg/detectors/positionstack/positionstack_test.go000066400000000000000000000055541451332102400257000ustar00rootroot00000000000000//go:build detectors // +build detectors package positionstack import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPositionStack_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("POSITIONSTACK") inactiveSecret := testSecrets.MustGetField("POSITIONSTACK_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 positionstack secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PositionStack, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a positionstack 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_PositionStack, 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("PositionStack.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PositionStack.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) } } }) } } trufflehog-3.60.0/pkg/detectors/postageapp/000077500000000000000000000000001451332102400206605ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/postageapp/postageapp.go000066400000000000000000000042431451332102400233550ustar00rootroot00000000000000package postageapp import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"postageapp"}) + `\b([0-9A-Za-z]{32})\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{"postageapp"} } // FromData will find and optionally verify PostageApp 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PostageApp, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "POST", "https://api.postageapp.com/v.1.0/get_account_info.json?api_key="+resMatch, nil) if err != nil { continue } req.Header.Add("Content-Transfer-Encoding", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PostageApp } trufflehog-3.60.0/pkg/detectors/postageapp/postageapp_test.go000066400000000000000000000055161451332102400244200ustar00rootroot00000000000000//go:build detectors // +build detectors package postageapp import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPostageApp_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POSTAGEAPP") inactiveSecret := testSecrets.MustGetField("POSTAGEAPP_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 postageapp secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PostageApp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postageapp 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_PostageApp, 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("PostageApp.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PostageApp.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) } } }) } } trufflehog-3.60.0/pkg/detectors/postbacks/000077500000000000000000000000001451332102400205065ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/postbacks/postbacks.go000066400000000000000000000046351451332102400230360ustar00rootroot00000000000000package postbacks import ( "context" "net/http" "regexp" "strings" "time" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"postbacks"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\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{"postbacks"} } // FromData will find and optionally verify Postbacks 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Postbacks, Raw: []byte(resMatch), } if verify { timeout := 10 * time.Second client.Timeout = timeout payload := strings.NewReader(`{"url": "https://foo.com/bar","send_at": 1579049877,"body_string": "{ \"email_id\":\"123456789\" }"}`) req, err := http.NewRequestWithContext(ctx, "POST", "https://api.postbacks.io/v1/requestPostback", payload) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Postbacks-Authorization", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Postbacks } trufflehog-3.60.0/pkg/detectors/postbacks/postbacks_test.go000066400000000000000000000055041451332102400240710ustar00rootroot00000000000000//go:build detectors // +build detectors package postbacks import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPostbacks_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POSTBACKS") inactiveSecret := testSecrets.MustGetField("POSTBACKS_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 postbacks secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Postbacks, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postbacks 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_Postbacks, 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("Postbacks.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Postbacks.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) } } }) } } trufflehog-3.60.0/pkg/detectors/posthog/000077500000000000000000000000001451332102400202005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/posthog/posthog.go000066400000000000000000000042461451332102400222200ustar00rootroot00000000000000package posthog import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(phc_[a-zA-Z0-9_]{43})\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{"phc_"} } // FromData will find and optionally verify AppPosthog 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_PosthogApp, Raw: []byte(resMatch), } if verify { payload := strings.NewReader(` { "api_key": "` + resMatch + `", "distinct_id": "1234" }`) req, err := http.NewRequestWithContext(ctx, "POST", "https://app.posthog.com/decide/", payload) if err != nil { continue } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PosthogApp } trufflehog-3.60.0/pkg/detectors/posthog/posthog_test.go000066400000000000000000000055211451332102400232540ustar00rootroot00000000000000//go:build detectors // +build detectors package posthog import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestAppPosthog_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("APPPOSTHOG_TOKEN") inactiveSecret := testSecrets.MustGetField("APPPOSTHOG_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 appposthog secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PosthogApp, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a appposthog 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_PosthogApp, 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("AppPosthog.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("AppPosthog.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) } } }) } } trufflehog-3.60.0/pkg/detectors/postman/000077500000000000000000000000001451332102400201765ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/postman/postman.go000066400000000000000000000047261451332102400222170ustar00rootroot00000000000000package postman import ( "context" "fmt" "net/http" "regexp" "strings" "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 } const verifyURL = "https://api.getpostman.com/collections" // 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. keyPat = regexp.MustCompile(`\b(PMAK-[a-zA-Z-0-9]{59})\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{"PMAK-"} } // FromData will find and optionally verify Postman 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Postman, Raw: []byte(resMatch), } if verify { client := s.getClient() isVerified, verificationErr := verifyPostman(ctx, client, resMatch) s1.Verified = isVerified s1.VerificationError = verificationErr } if !s1.Verified && detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } results = append(results, s1) } return results, nil } func (s Scanner) getClient() *http.Client { if s.client != nil { return s.client } return defaultClient } func verifyPostman(ctx context.Context, client *http.Client, token string) (bool, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, verifyURL, nil) if err != nil { return false, err } req.Header.Add("x-api-key", token) res, err := client.Do(req) if err != nil { return false, err } defer res.Body.Close() switch res.StatusCode { case http.StatusOK: return true, nil case http.StatusUnauthorized: return false, nil default: return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode) } } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Postman } trufflehog-3.60.0/pkg/detectors/postman/postman_test.go000066400000000000000000000101441451332102400232450ustar00rootroot00000000000000//go:build detectors // +build detectors package postman 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 TestPostman_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POSTMAN_TOKEN") inactiveSecret := testSecrets.MustGetField("POSTMAN_INACTIVE") type args struct { ctx context.Context data []byte verify bool } tests := []struct { name string s Scanner args args want []detectors.Result wantErr bool wantVerificationErr bool }{ { name: "found, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postman secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Postman, Verified: true, }, }, wantErr: false, }, { name: "found, would be verified if not for timeout", s: Scanner{client: common.SaneHttpClientTimeOut(1 * time.Microsecond)}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postman secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Postman, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, verified but unexpected api surface", s: Scanner{client: common.ConstantResponseHttpClient(404, "")}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postman secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Postman, Verified: false, }, }, wantErr: false, wantVerificationErr: true, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postman 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_Postman, 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("Postman.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]) } if (got[i].VerificationError != nil) != tt.wantVerificationErr { t.Errorf("Postman.FromData() error = %v, wantErr %v", got[i].VerificationError, tt.wantVerificationErr) } } ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError") if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" { t.Errorf("Postman.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) } } }) } } trufflehog-3.60.0/pkg/detectors/postmark/000077500000000000000000000000001451332102400203555ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/postmark/postmark.go000066400000000000000000000037751451332102400225600ustar00rootroot00000000000000package postmark import ( "context" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"postmark"}) + `\b([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\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{"postmark"} } // FromData will find and optionally verify Postmark 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Postmark, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.postmarkapp.com/deliverystats", nil) if err != nil { continue } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") req.Header.Add("X-Postmark-Server-Token", resMatch) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Postmark } trufflehog-3.60.0/pkg/detectors/postmark/postmark_test.go000066400000000000000000000055001451332102400236030ustar00rootroot00000000000000//go:build detectors // +build detectors package postmark import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPostmark_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POSTMARK_TOKEN") inactiveSecret := testSecrets.MustGetField("POSTMARK_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 postmark secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Postmark, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a postmark 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_Postmark, 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("Postmark.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Postmark.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) } } }) } } trufflehog-3.60.0/pkg/detectors/powrbot/000077500000000000000000000000001451332102400202115ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/powrbot/powrbot.go000066400000000000000000000042161451332102400222370ustar00rootroot00000000000000package powrbot import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"powrbot"}) + `\b([a-z0-9A-Z]{40})\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{"powrbot"} } // FromData will find and optionally verify Powrbot 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Powrbot, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://powrbot.com/api/v1/search/single/?company=Apple", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("secret-key %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Powrbot } trufflehog-3.60.0/pkg/detectors/powrbot/powrbot_test.go000066400000000000000000000054661451332102400233060ustar00rootroot00000000000000//go:build detectors // +build detectors package powrbot import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPowrbot_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("POWRBOT_TOKEN") inactiveSecret := testSecrets.MustGetField("POWRBOT_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 powrbot secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Powrbot, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a powrbot 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_Powrbot, 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("Powrbot.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Powrbot.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) } } }) } } trufflehog-3.60.0/pkg/detectors/prefect/000077500000000000000000000000001451332102400201455ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/prefect/prefect.go000066400000000000000000000041131451332102400221230ustar00rootroot00000000000000package prefect import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives. keyPat = regexp.MustCompile(`\b(pnu_[a-zA-Z0-9]{36})\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{"pnu_"} } // FromData will find and optionally verify Prefect 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Prefect, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.prefect.cloud/auth/login", nil) if err != nil { continue } req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key. if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Prefect } trufflehog-3.60.0/pkg/detectors/prefect/prefect_test.go000066400000000000000000000054601451332102400231700ustar00rootroot00000000000000//go:build detectors // +build detectors package prefect import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPrefect_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PREFECT") inactiveSecret := testSecrets.MustGetField("PREFECT_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 prefect secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Prefect, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a prefect 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_Prefect, 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("Prefect.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Prefect.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) } } }) } } trufflehog-3.60.0/pkg/detectors/privatekey/000077500000000000000000000000001451332102400207005ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/privatekey/cracker.go000066400000000000000000000012501451332102400226370ustar00rootroot00000000000000package privatekey import ( "bytes" "crypto/x509" _ "embed" "errors" "golang.org/x/crypto/ssh" ) //go:embed "list.txt" var rawCrackList []byte var passphrases [][]byte func init() { passphrases = bytes.Split(rawCrackList, []byte("\n")) } var ( ErrUncrackable = errors.New("unable to crack encryption") ) func crack(in []byte) (any, string, error) { for _, passphrase := range passphrases { parsed, err := ssh.ParseRawPrivateKeyWithPassphrase(in, passphrase) if err != nil { if errors.Is(err, x509.IncorrectPasswordError) { continue } else { return nil, "", err } } return parsed, string(passphrase), nil } return nil, "", ErrUncrackable } trufflehog-3.60.0/pkg/detectors/privatekey/cracker_test.go000066400000000000000000000100341451332102400236760ustar00rootroot00000000000000//go:build detectors // +build detectors package privatekey import ( _ "embed" "testing" "golang.org/x/crypto/ssh" ) var ( testEncryptedKeyCorrectPassword = []byte("123456") testEncryptedKeyIncorrectPassword = []byte("incorrect") testEncryptedKey = []byte(normalize(`-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAjNIZuun xgLkM8KuzfmQuRAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDe3Al0EMPz utVNk5DixaYrGMK56RqUoqGBinke6SWVWmqom1lBcJWzor6HlnMRPPr7YCEsJKL4IpuVwu inRa5kdtNTyM7yyQTSR2xXCS0fUItNuq8pUktsH8VUggpMeew8hJv7rFA7tnIg3UXCl6iF OLZKbDA5aa24idpcD8b1I9/RzTOB1fu0of5xd9vgODzGw5JvHQSJ0FaA42aNBMGwrDhDB3 sgnRNdWf6NNIh8KpXXMKJADf3klsyn6He8L2bPMp8a4wwys2YB35p5zQ0JURovsdewlOxH NT7eP19eVf4dCreibxUmRUaob5DEoHEk8WrxjKWIYUuLeD6AfcW6oXyRU2Yy8Vrt6SqFl5 WAi47VMFTkDZYS/eCvG53q9UBHpCj7Qvb0vSkCZXBvBIhlw193F3PX4WvO1IXsMwvQ1D1X lmomsItbqM0cJyKw6LU18QWiBHvE7BqcphaoL5E08W2ATTSRIMCp6rt4rptM7KyGK8rc6W UYrCnWt6KlCA8AAAWQXk+lVx6bH5itIKKYmQr6cR/5xtZ2GHAxnYtvlW3xnGhU0MHv+lJ2 uoWlT2RXE5pdMUQj7rNWAMqkwifSKZs9wBfYeo1TaFDmC3nW7yHSN3XTuO78mPIW5JyvmE Rj5qjsUn7fNmzECoAxnVERhwnF3KqUBEPzIAc6/7v/na9NTiiGaJPco9lvCoPWbVLN08WG SuyU+0x5zc3ebzuPcYqu5/c5nmiGxhALrIhjIS0OV1mtAAFhvdMjMIHOijOzSKVCC7rRk5 kG9EMLNvOn/DUVSRHamw5gs2V3V+Zq2g5nYWfgq8aDSTB8XlIzOj1cz3HwfN6pfSNQ/3Qe wOQfWfTWdO+JSL8aoBN5Wg8tDbgmvmbFrINsJfFfSm0wZgcHhC7Ul4U3v4c8PoNdK9HXwi TKKzJ9nxLYb+vDh50cnkseu2gt0KwVpjIorxEqeK755mKPao3JmOMr6uFTQsb+g+ZNgPwl nRHA4Igx+zADFj3twldnKIiRpBQ5J4acur3uQ+saanBTXgul1TiFiUGT2cnz+IiCsdPovg TAMt868W5LmzpfH4Cy54JtaRC4/UuMnkTGbWgutVDnWj2stOAzsQ1YmhH5igUmc94mUL+W 8vQDCKpeI8n+quDS9zxTvy4L4H5Iz7OZlh0h6N13BDvCYXKcNF/ugkfxZbu8mZsZQQzXNR wOrEtKoHc4AnXYNzsuHEoEyLyJxGfFRDSTLbyN9wFOS/c0k9Gjte+kQRZjBVGORE5sN6X3 akUnTF76RhbEc+LamrwM1h5340bwosRbR8I+UrsQdFfJBEj1ZSyMRJlMkFUNi6blt7bhyx ea+Pm2A614nlYUBjw2KKzzn8N/0H2NpJjIptvDsbrx3BS/rKwOeJwavRrGnIlEzuAag4vx Zb2TPVta45uz7fQP5IBl83b0BJKI5Zv/fniUeLI78W/UsZqb64YQbfRyBzFtI1T/SsCi0B e0EyKMzbxtSceT1Mb8eJiVIq04Xpwez9fIUt5rSedZD8KPq8P6s0cGsR7Qmw6eXZ/dBR/a s5vPhfIUmQawmnwAVuWNRdQQ79jUBSn5M+ZRVVTgEG+vFyvxr/bZqOo1JCoq5BmQhLWGRJ Dk9TolbeFIVFrkuXkcu99a079ux7XSkON64oPzHrcsEzjPA1GPqs9CGBSO16wq/nI3zg+E kcOCaurc9yHJJPwduem0+8WLX3WoGNfQRKurtQze2ppy8KarEtDhDd96sKkhYaqOg3GOX8 Yx827L4vuWSJSIqKuO2kH6kOCMUNO16piv0z/8u3CJxOGh9+4FZIop81fiFTKLhV3/gwLm fzFY++KIZrLfZcUjzd80NNEja69F452Eb9HrI5BurN/PznDEi9bzM598Y7beyl4/kd4R2e S7SW9/LOrGw5UgxtiU+kV8nPz1PdgxO4sRlnntSBEwkQBzMkLOpq2h2BuJ2TlMP/TWuwLQ sDkv1Yk1pD0roGmtMzbujnURGxqRJ8gUmuIot4hpfyRSssvnRQQZ3lQCQCwHiE+HJxXWf5 c58zOMjW7o21tI8e13uUnbRoQVJM9XYqk1usPXIkYPYL9uOw3AW/Zn+cnDrsXvTK9ZxgGD /90b1BNwVqMlUK+QggHNwl5qD8eoXK5cDvav66te+E+V7FYFQ06w3tytRVz8SjoaiChN02 muIjvl6G7Hoj1hObM2t/ZheN1EShS11z868hhS6Mx7GvIdtkXuvdiBYMiBLOshJQxB8Mzx iug9W+Di3upLf0UMC1TqADGphsIHRU7RbmHQ8Rwp7dogswmDfpRSapPt9p0D+6Ad5VBzi3 f3BPXj76UBLMEJCrZR1P28vnAA7AyNHaLvMPlWDMG5v3V/UV+ugyFcoBAOyjiQgYST8F3e Hx7UPVlTK8dyvk1Z+Yw0nrfNClI= -----END OPENSSH PRIVATE KEY-----`)) ) func Test_crack(t *testing.T) { tests := []struct { name string in []byte wantedPassphrase string wantErr bool }{ { name: "crackable", wantErr: false, in: testEncryptedKey, wantedPassphrase: string(testEncryptedKeyCorrectPassword), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, passphrase, err := crack(tt.in) if (err != nil) != tt.wantErr { t.Errorf("crack() error = %v, wantErr %v", err, tt.wantErr) return } if passphrase != string(tt.wantedPassphrase) { t.Errorf("crack() passphrase = %v, want %v", passphrase, string(testEncryptedKeyCorrectPassword)) } }) } } func BenchmarkParseWrongPassword(b *testing.B) { for n := 0; n < b.N; n++ { ssh.ParseRawPrivateKeyWithPassphrase(testEncryptedKey, testEncryptedKeyIncorrectPassword) } } func BenchmarkParseRightPassword(b *testing.B) { for n := 0; n < b.N; n++ { ssh.ParseRawPrivateKeyWithPassphrase(testEncryptedKey, testEncryptedKeyCorrectPassword) } } func BenchmarkCracker(b *testing.B) { for n := 0; n < b.N; n++ { crack(testEncryptedKey) } } trufflehog-3.60.0/pkg/detectors/privatekey/fingerprint.go000066400000000000000000000024521451332102400235610ustar00rootroot00000000000000package privatekey import ( "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "errors" "fmt" "golang.org/x/crypto/ssh" ) var ( ErrNotSupported = errors.New("key type not supported") ErrEncryptedKey = errors.New("key is encrypted") ) func FingerprintPEMKey(parsedKey any) (string, error) { var pubKey any switch privateKey := parsedKey.(type) { case *rsa.PrivateKey: pubKey = &privateKey.PublicKey case *ecdsa.PrivateKey: pubKey = &privateKey.PublicKey case *ed25519.PrivateKey: pubKey = privateKey.Public() // No fingerprinting support for DSA // case *dsa.PrivateKey: // pubKey = privateKey.PublicKey default: return "", ErrNotSupported } return fingerprintPublicKey(pubKey) } func fingerprintPublicKey(pubKey any) (string, error) { publickeyBytes, err := x509.MarshalPKIXPublicKey(pubKey) if err != nil { return "", err } publicKeyFingerprint := sha1.Sum(publickeyBytes) publicKeyFingerprintInHex := hex.EncodeToString(publicKeyFingerprint[:]) return publicKeyFingerprintInHex, nil } func fingerprintSSHPublicKey(pubKey ssh.PublicKey) string { publicKeyFingerprint := sha256.Sum256(pubKey.Marshal()) return fmt.Sprintf("SHA256:%s", base64.RawStdEncoding.EncodeToString(publicKeyFingerprint[:])) } trufflehog-3.60.0/pkg/detectors/privatekey/list.txt000066400000000000000000000036261451332102400224230ustar00rootroot00000000000000123456 12345 123456789 password iloveyou princess 1234567 12345678 abc123 nicole daniel babygirl monkey lovely jessica 654321 michael ashley qwerty 111111 iloveu 000000 michelle tigger sunshine chocolate password1 soccer anthony friends butterfly purple angel jordan liverpool justin loveme fuckyou 123123 football secret andrea carlos jennifer joshua bubbles 1234567890 superman hannah amanda loveyou pretty basketball andrew angels tweety flower playboy hello elizabeth hottie tinkerbell charlie samantha barbie chelsea lovers teamo jasmine brandon 666666 shadow melissa eminem matthew robert danielle forever family jonathan 987654321 computer whatever dragon vanessa cookie naruto summer sweety spongebob joseph junior softball taylor yellow daniela lauren mickey princesa alexandra alexis jesus estrella miguel william thomas beautiful mylove angela poohbear patrick iloveme sakura adrian alexander destiny christian 121212 sayang america dancer monica richard 112233 princess1 555555 diamond carolina steven rangers louise orange 789456 999999 shorty 11111 nathan snoopy gabriel hunter cherry killer sandra alejandro buster george brittany alejandra patricia rachel tequiero 7777777 cheese 159753 arsenal dolphin antonio heather david ginger stephanie peanut blink182 sweetie 222222 beauty 987654 victoria honey 00000 fernando pokemon maggie corazon chicken pepper cristina rainbow kisses manuel myspace rebelde angel1 ricardo babygurl heaven 55555 baseball martin greenday november alyssa madison mother 123321 123abc mahalkita batman september december morgan mariposa maria gabriela iloveyou2 bailey jeremy pamela kimberly gemini shannon pictures asshole sophie jessie hellokitty claudia babygirl1 angelica austin mahalko victor horses tiffany mariana eduardo andres courtney booboo kissme harley ronaldo iloveyou1 precious october inuyasha peaches veronica chris 888888 adriana cutie james banana prince friend jesus1 crystal celtictrufflehog-3.60.0/pkg/detectors/privatekey/normalize.go000066400000000000000000000016241451332102400232320ustar00rootroot00000000000000package privatekey import ( "strings" ) func normalize(in string) string { in = strings.ReplaceAll(in, `"`, "") in = strings.ReplaceAll(in, `'`, "") in = strings.ReplaceAll(in, "\t", "") in = strings.ReplaceAll(in, `\t`, "") in = strings.ReplaceAll(in, `\\t`, "") in = strings.ReplaceAll(in, `\n`, "\n") in = strings.ReplaceAll(in, `\\r\\n`, "\n") in = strings.ReplaceAll(in, `\r\n`, "\n") in = strings.ReplaceAll(in, "\r\n", "\n") in = strings.ReplaceAll(in, `\\r`, "\n") in = strings.ReplaceAll(in, "\r", "\n") in = strings.ReplaceAll(in, `\r`, "\n") in = strings.ReplaceAll(in, `\\n`, "\n") in = strings.ReplaceAll(in, `\n\n`, "\n") in = strings.ReplaceAll(in, "\n\n", "\n") in = strings.ReplaceAll(in, `\\`, "\n") cleaned := strings.Builder{} parts := strings.Split(in, "\n") for _, line := range parts { cleaned.WriteString(strings.TrimSpace(line) + "\n") } return cleaned.String() } trufflehog-3.60.0/pkg/detectors/privatekey/privatekey.go000066400000000000000000000113211451332102400234100ustar00rootroot00000000000000package privatekey import ( "context" "encoding/json" "fmt" "net/http" "regexp" "strings" "time" "github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" "golang.org/x/crypto/ssh" ) type Scanner struct { IncludeExpired bool } // Ensure the Scanner satisfies the interface at compile time. var _ detectors.Detector = (*Scanner)(nil) var ( // TODO: add base64 encoded key support client = common.RetryableHttpClient() keyPat = regexp.MustCompile(`(?i)-----\s*?BEGIN[ A-Z0-9_-]*?PRIVATE KEY\s*?-----[\s\S]*?----\s*?END[ A-Z0-9_-]*? PRIVATE KEY\s*?-----`) ) // 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{"private key"} } // FromData will find and optionally verify Privatekey secrets in a given set of bytes. func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]detectors.Result, error) { results := []detectors.Result{} dataStr := string(data) matches := keyPat.FindAllString(dataStr, -1) for _, match := range matches { token := normalize(match) if len(token) < 64 { continue } secret := detectors.Result{ DetectorType: detectorspb.DetectorType_PrivateKey, Raw: []byte(token), Redacted: token[0:64], } secret.ExtraData = make(map[string]string) var passphrase string parsedKey, err := ssh.ParseRawPrivateKey([]byte(token)) if err != nil && strings.Contains(err.Error(), "private key is passphrase protected") { secret.ExtraData["encrypted"] = "true" parsedKey, passphrase, err = crack([]byte(token)) if err != nil { secret.VerificationError = err continue } if passphrase != "" { secret.ExtraData["cracked_encryption_passphrase"] = "true" } } else if err != nil { // couldn't parse key, probably invalid continue } fingerprint, err := FingerprintPEMKey(parsedKey) if err != nil { continue } if verify { verificationErrors := []string{} data, err := lookupFingerprint(fingerprint, s.IncludeExpired) if err == nil { if data != nil { secret.Verified = true secret.ExtraData["certificate_urls"] = strings.Join(data.CertificateURLs, ", ") } } else { verificationErrors = append(verificationErrors, err.Error()) } user, err := verifyGitHubUser(parsedKey) if err != nil && !errors.Is(err, errPermissionDenied) { verificationErrors = append(verificationErrors, err.Error()) } if user != nil { secret.Verified = true secret.ExtraData["github_user"] = *user } user, err = verifyGitLabUser(parsedKey) if err != nil && !errors.Is(err, errPermissionDenied) { verificationErrors = append(verificationErrors, err.Error()) } if user != nil { secret.Verified = true secret.ExtraData["gitlab_user"] = *user } if !secret.Verified && len(verificationErrors) > 0 { secret.VerificationError = fmt.Errorf("verification failures: %s", strings.Join(verificationErrors, ", ")) } } if len(secret.ExtraData) == 0 { secret.ExtraData = nil } results = append(results, secret) } return results, nil } type result struct { CertificateURLs []string GitHubUsername string } func lookupFingerprint(publicKeyFingerprintInHex string, includeExpired bool) (*result, error) { req, err := http.NewRequest("GET", fmt.Sprintf("https://keychecker.trufflesecurity.com/fingerprint/%s", publicKeyFingerprintInHex), nil) if err != nil { return nil, err } res, err := client.Do(req) if err != nil { return nil, err } defer res.Body.Close() results := DriftwoodResult{} err = json.NewDecoder(res.Body).Decode(&results) if err != nil { return nil, err } var data *result seen := map[string]struct{}{} for _, r := range results.CertificateResults { if _, ok := seen[r.CertificateFingerprint]; ok { continue } if !includeExpired && time.Since(r.ExpirationTimestamp) > 0 { continue } if data == nil { data = &result{} } data.CertificateURLs = append(data.CertificateURLs, fmt.Sprintf("https://crt.sh/?q=%s", r.CertificateFingerprint)) seen[r.CertificateFingerprint] = struct{}{} } return data, nil } type DriftwoodResult struct { CertificateResults []struct { CertificateFingerprint string `json:"CertificateFingerprint"` ExpirationTimestamp time.Time `json:"ExpirationTimestamp"` } `json:"CertificateResults"` GitHubSSHResults []struct { Username string `json:"Username"` } `json:"GitHubSSHResults"` } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_PrivateKey } trufflehog-3.60.0/pkg/detectors/privatekey/privatekey_test.go000066400000000000000000000134011451332102400244500ustar00rootroot00000000000000//go:build detectors // +build detectors package privatekey import ( "context" "fmt" "os" "reflect" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestPrivatekey_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secretTLS := testSecrets.MustGetField("PRIVATEKEY_TLS") secretGitHub := testSecrets.MustGetField("PRIVATEKEY_GITHUB") secretGitHubEncrypted := testSecrets.MustGetField("PRIVATEKEY_GITHUB_ENCRYPTED") secretInactive := testSecrets.MustGetField("PRIVATEKEY_UNVERIFIED") 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, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find privatekey secret %s within", secretInactive)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PrivateKey, Verified: false, Redacted: "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYw", }, { DetectorType: detectorspb.DetectorType_PrivateKey, Verified: false, Redacted: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgw", }, }, wantErr: false, }, { name: "found TLS private key, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(secretTLS), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PrivateKey, Verified: true, Redacted: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgw", ExtraData: map[string]string{ "certificate_urls": "https://crt.sh/?q=1e20c40deb44a8539dd3ac3e8c53b72750cb19f9, https://crt.sh/?q=0e9de31fb2ee16465a4d5d93b227d54f870326d1", }, }, }, wantErr: false, }, { name: "found GitHub SSH private key, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(secretGitHub), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PrivateKey, Verified: true, Redacted: "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5v", ExtraData: map[string]string{ "github_user": "sirdetectsalot", }, }, }, wantErr: false, }, { name: "found encrypted GitHub SSH private key, verified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(secretGitHubEncrypted), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_PrivateKey, Verified: true, Redacted: "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAACmFl", ExtraData: map[string]string{ "github_user": "sirdetectsalot", "encrypted": "true", "cracked_encryption_passphrase": "true", }, }, }, 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{IncludeExpired: true} got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data) if (err != nil) != tt.wantErr { t.Errorf("PrivatekeyCI.FromData() error = %v, wantErr %v", err, tt.wantErr) return } if os.Getenv("FORCE_PASS_DIFF") == "true" { return } for i := range got { if len(got[i].Raw) == 0 { t.Fatal("no raw secret present") } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("PrivatekeyCI.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) } } }) } } func Test_lookupFingerprint(t *testing.T) { tests := []struct { name string publicKeyFingerprintInHex string wantFingerprints bool wantErr bool includeExpired bool }{ { name: "got some", publicKeyFingerprintInHex: "4c5da06caa1c81df9c8e1abe43bac385de1bda76", wantFingerprints: true, wantErr: false, includeExpired: true, }, { name: "got some", publicKeyFingerprintInHex: "none", wantFingerprints: false, wantErr: false, includeExpired: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotFingerprints, err := lookupFingerprint(tt.publicKeyFingerprintInHex, tt.includeExpired) if (err != nil) != tt.wantErr { t.Errorf("lookupFingerprint() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(gotFingerprints != nil && len(gotFingerprints.CertificateURLs) > 0, tt.wantFingerprints) { t.Errorf("lookupFingerprint() = %v, want %v", gotFingerprints, tt.wantFingerprints) } }) } } trufflehog-3.60.0/pkg/detectors/privatekey/ssh.go000066400000000000000000000064061451332102400220320ustar00rootroot00000000000000package privatekey import ( "bytes" "errors" "fmt" "net" "strings" "time" "golang.org/x/crypto/ssh" ) // https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints var githubFingerprints = map[string]string{ "SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s": "RSA", "SHA256:br9IjFspm1vxR3iA35FWE+4VTyz1hYVLIE2t1/CeyWQ": "DSA - deprecated", "SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM": "ECDSA", "SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU": "ED25519", } // https://docs.gitlab.com/ee/user/gitlab_com/index.html#ssh-host-keys-fingerprints var gitlabFingerprints = map[string]string{ "SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw": "ECDSA", "SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8": "ED25519", "SHA256:ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ": "RSA", } func firstResponseFromSSH(parsedKey any, username, hostport string) (string, error) { signer, err := ssh.NewSignerFromKey(parsedKey) if err != nil { return "", err } // Verify the server fingerprint to ensure that there is no MITM replay attack config := &ssh.ClientConfig{ Timeout: 3 * time.Second, User: username, Auth: []ssh.AuthMethod{ ssh.PublicKeys(signer), }, HostKeyCallback: func(hostname string, _ net.Addr, key ssh.PublicKey) error { switch hostname { case "github.com:22": fingerprint := fingerprintSSHPublicKey(key) if _, ok := githubFingerprints[fingerprint]; !ok { return fmt.Errorf("unknown host fingerprint for github.com, got %s", fingerprint) } case "gitlab.com:22": fingerprint := fingerprintSSHPublicKey(key) if _, ok := gitlabFingerprints[fingerprint]; !ok { return fmt.Errorf("unknown host fingerprint for gitlab.com, got %s", fingerprint) } default: return errors.New("unknown host in fingerprint db") } return nil }, } client, err := ssh.Dial("tcp", hostport, config) if err != nil { if strings.Contains(err.Error(), "unable to authenticate") { return "", errPermissionDenied } return "", err } defer client.Close() session, err := client.NewSession() if err != nil { return "", err } defer session.Close() var output bytes.Buffer session.Stderr = &output err = session.Shell() if err != nil { return "", err } _ = session.Wait() return output.String(), err } var errPermissionDenied = errors.New("permission denied") func verifyGitHubUser(parsedKey any) (*string, error) { output, err := firstResponseFromSSH(parsedKey, "git", "github.com:22") if err != nil { return nil, err } if strings.Contains(output, "Permission denied") { return nil, errPermissionDenied } if strings.Contains(output, "successfully authenticated") { username := strings.TrimSuffix(strings.Split(output, " ")[1], "!") return &username, nil } return nil, nil } func verifyGitLabUser(parsedKey any) (*string, error) { output, err := firstResponseFromSSH(parsedKey, "git", "gitlab.com:22") if err != nil { return nil, err } if strings.Contains(output, "Permission denied") { return nil, errPermissionDenied } if strings.Contains(output, "Welcome to GitLab") { split := strings.Split(output, " ") username := strings.TrimPrefix(strings.TrimSuffix(split[len(split)-1], "!"), "@") return &username, nil } return nil, nil } trufflehog-3.60.0/pkg/detectors/privatekey/ssh_test.go000066400000000000000000000014301451332102400230610ustar00rootroot00000000000000package privatekey import ( "context" "testing" "time" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "golang.org/x/crypto/ssh" ) func TestFirstResponseFromSSH(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secretGitHub := testSecrets.MustGetField("PRIVATEKEY_GITHUB") parsedKey, err := ssh.ParseRawPrivateKey([]byte(normalize(secretGitHub))) if err != nil { t.Fatalf("could not parse test secret: %s", err) } output, err := firstResponseFromSSH(parsedKey, "git", "github.com:22") if err != nil { t.Fail() } if len(output) == 0 { t.Fail() } } trufflehog-3.60.0/pkg/detectors/prodpad/000077500000000000000000000000001451332102400201465ustar00rootroot00000000000000trufflehog-3.60.0/pkg/detectors/prodpad/prodpad.go000066400000000000000000000042341451332102400221310ustar00rootroot00000000000000package prodpad import ( "context" "fmt" "net/http" "regexp" "strings" "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{} // Ensure the Scanner satisfies the interface at compile time var _ detectors.Detector = (*Scanner)(nil) var ( client = common.SaneHttpClient() // Make sure that your group is surrounded in boundary characters such as below to reduce false positives keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"prodpad"}) + `\b([a-f0-9]{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{"prodpad"} } // FromData will find and optionally verify Prodpad 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) for _, match := range matches { if len(match) != 2 { continue } resMatch := strings.TrimSpace(match[1]) s1 := detectors.Result{ DetectorType: detectorspb.DetectorType_Prodpad, Raw: []byte(resMatch), } if verify { req, err := http.NewRequestWithContext(ctx, "GET", "https://api.prodpad.com/v1/tags", nil) if err != nil { continue } req.Header.Add("accept", "application/json") req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch)) res, err := client.Do(req) if err == nil { defer res.Body.Close() if res.StatusCode >= 200 && res.StatusCode < 300 { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) { continue } } } } results = append(results, s1) } return results, nil } func (s Scanner) Type() detectorspb.DetectorType { return detectorspb.DetectorType_Prodpad } trufflehog-3.60.0/pkg/detectors/prodpad/prodpad_test.go000066400000000000000000000054061451332102400231720ustar00rootroot00000000000000package prodpad import ( "context" "fmt" "testing" "time" "github.com/kylelemons/godebug/pretty" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) func TestProdpad_FromChunk(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors3") if err != nil { t.Fatalf("could not get test secrets from GCP: %s", err) } secret := testSecrets.MustGetField("PRODPAD") inactiveSecret := testSecrets.MustGetField("PRODPAD_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 prodpad secret %s within", secret)), verify: true, }, want: []detectors.Result{ { DetectorType: detectorspb.DetectorType_Prodpad, Verified: true, }, }, wantErr: false, }, { name: "found, unverified", s: Scanner{}, args: args{ ctx: context.Background(), data: []byte(fmt.Sprintf("You can find a prodpad 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_Prodpad, 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("Prodpad.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]) } got[i].Raw = nil } if diff := pretty.Compare(got, tt.want); diff != "" { t.Errorf("Prodpad.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) } } }) } } trufflehog-3.60.0/pkg/detectors/programmingbooks.txt000066400000000000000000000643441451332102400226510ustar00rootroot00000000000000--+--+-- --> $${balance $curry(...args ${email $.getjson(url $ident $('
add(multiply(x addnextgrade add-one add_one addpage address add add(self add addstudents add/target addten add_text add-two adequate adhere adjust administrative admirable admonition advanced advent advertising advice advisable aesthetic affect aforementioned afraid a.get('team aggregates aimlessly a.iter ajaxcall ajax(url,cb alarms! albeit albert algebra algorithm all.empty all(false allocate all-or-nothing allthechildren all(true allupper almost alphabetic already alt="a although altogether _always_ always amalgamations a'].map($ amateur amazement amazing ambiguity ambition amd-style amenable amending amidst amoduleineed amount ampersand analog analysis anarchy anatomy @anaufalm ancestor anchor ancients and/or andrew andthisonetoo angels animal aniston annihilate annotate announcement annoyed anonymous another answer anti-class anxious any.empty any(false anyfunctor anyhow anymore anyone anything any(true anyway anywhere a.of(f apiendpoints api.flickr.com ap`ing apostrophe ap(other apparent app('cats appeal append appetizer apple” applicability appreciate approach app arabic arbitrarily arcane architectural arc> arc area(&self aren't aren’t args.length argstr arguably arguing argument arithmetic armstrong around arrangement arrcopy arr.entries arrived arr.length art4thesould arthur article artifact artist as_bytes ashamed asking askquestion asm.js as_mut_ptr a` aspect as_ref assemble assert! assign assist associate ‘associated assortment assume assure asterisk ast.ident astound ast)—to atomic attach attempt attend attitude attractions attribute audience audited augments august austin authenticate author auto-complete autocompletion automate autosave available a_value average awesome! awhile awkward azure
b${str babylonians baby_name backed background backing back_of_house backported backspace back-tick backtrace backward badidea bahasa balance balloons! banana bandwagoning b)).ap(f bar...it barren barrier bartenders baseless basename bathwater battle bearing beatnik beautiful became because become been!—should before #beginners behalf behave behind behold belabor believe belong bending beneath beneficial benkort besides betrays better between beware beyond bibliography bigger bigint big-integer bikeshedding billion binaries binding bind(this bioinformatics bitand bitten bitwise bitxor bizarrely bjarne blaring bleeding blogcontroller blog({}).fork blogpage blog’s bloody bludgeon blurp_blurp boasting bodies body)? boiler boldly bolster bonafide --book bookkeeping book book's boolean boring borrow bothering both_float bottle bottom bounce box