Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bbc35e5be |
@@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
@@ -15,9 +17,9 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.18'
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
@@ -28,10 +30,13 @@ jobs:
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
args: --enable bodyclose --enable exportloopref --timeout 10m
|
||||
args: --enable bodyclose --timeout 10m
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
only-new-issues: true
|
||||
|
||||
# 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
|
||||
# skip-build-cache: true
|
||||
@@ -33,9 +33,9 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.18'
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
|
||||
@@ -7,16 +7,16 @@ on:
|
||||
- v*
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.18'
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -28,4 +28,4 @@ jobs:
|
||||
with:
|
||||
path: ./
|
||||
base: ${{ github.event.repository.default_branch }}
|
||||
head: HEAD
|
||||
head: HEAD
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18'
|
||||
- name: Checkout code
|
||||
|
||||
@@ -16,9 +16,9 @@ jobs:
|
||||
name: Run Snifftest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.18'
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Snifftest
|
||||
run: make snifftest
|
||||
@@ -17,9 +17,9 @@ jobs:
|
||||
id-token: 'write'
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.18'
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- id: 'auth'
|
||||
@@ -36,9 +36,9 @@ jobs:
|
||||
id-token: 'write'
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.18'
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- id: 'auth'
|
||||
|
||||
@@ -22,7 +22,7 @@ check:
|
||||
go vet $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
lint:
|
||||
golangci-lint run --enable bodyclose --enable exportloopref --out-format=colored-line-number --timeout 10m
|
||||
golangci-lint run --enable bodyclose --out-format=colored-line-number --timeout 10m
|
||||
|
||||
test-failing:
|
||||
CGO_ENABLED=0 go test -timeout=5m $(shell go list ./... | grep -v /vendor/) | grep FAIL
|
||||
@@ -62,4 +62,4 @@ snifftest:
|
||||
./hack/snifftest/snifftest.sh
|
||||
|
||||
test-release:
|
||||
goreleaser release --rm-dist --skip-publish --snapshot
|
||||
goreleaser release --rm-dist --skip-publish --snapshot
|
||||
@@ -17,23 +17,13 @@
|
||||
|
||||
---
|
||||
|
||||
# :mag_right: _Now Scanning_
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="assets/scanning_logos.svg">
|
||||
|
||||
**...and more**
|
||||
|
||||
</div>
|
||||
|
||||
# :loudspeaker: Join Our Community
|
||||
## Join The Slack
|
||||
Have questions? Feedback? Jump in slack and hang out with us
|
||||
|
||||
https://join.slack.com/t/trufflehog-community/shared_invite/zt-pw2qbi43-Aa86hkiimstfdKH9UCpPzQ
|
||||
|
||||
|
||||
# :tv: Demo
|
||||
## Demo
|
||||
|
||||

|
||||
|
||||
@@ -41,30 +31,9 @@ https://join.slack.com/t/trufflehog-community/shared_invite/zt-pw2qbi43-Aa86hkii
|
||||
docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --org=trufflesecurity
|
||||
```
|
||||
|
||||
# :floppy_disk: Installation
|
||||
## Examples
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
# :rocket: Quick Start
|
||||
|
||||
## 1: Scan a repo for only verified secrets
|
||||
### Example 1: Scan a repo for only verified secrets
|
||||
|
||||
Command:
|
||||
|
||||
@@ -89,13 +58,13 @@ Timestamp: 2022-06-16 10:17:40 -0700 PDT
|
||||
...
|
||||
```
|
||||
|
||||
## 2: Scan a GitHub Org for only verified secrets
|
||||
### Example 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
|
||||
### Example 3: Scan a GitHub Repo for only verified keys and get JSON output
|
||||
|
||||
Command:
|
||||
|
||||
@@ -109,31 +78,13 @@ Expected output:
|
||||
...
|
||||
```
|
||||
|
||||
## 4: Scan an S3 bucket for verified keys
|
||||
### Example 4: Scan an S3 bucket for verified keys
|
||||
|
||||
```bash
|
||||
trufflehog s3 --bucket=<bucket name> --only-verified
|
||||
```
|
||||
|
||||
## 5: 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
|
||||
```
|
||||
|
||||
## 6: Scan individual files or directories
|
||||
|
||||
```bash
|
||||
trufflehog filesystem path/to/file1.txt path/to/file2.txt path/to/dir
|
||||
```
|
||||
|
||||
## 7: Scan GCS buckets for verified secrets.
|
||||
|
||||
```bash
|
||||
trufflehog gcs --project-id=<project-ID> --cloud-environment --only-verified
|
||||
```
|
||||
|
||||
# :question: FAQ
|
||||
### FAQ
|
||||
|
||||
+ All I see is `🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷` and the program exits, what gives?
|
||||
+ That means no secrets were detected
|
||||
@@ -143,21 +94,78 @@ trufflehog gcs --project-id=<project-ID> --cloud-environment --only-verified
|
||||
+ 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/)
|
||||
|
||||
|
||||
# :newspaper: What's new in v3?
|
||||
### Example 5: 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
|
||||
```
|
||||
|
||||
### Example 6: Scan individual files or directories
|
||||
|
||||
```bash
|
||||
trufflehog filesystem path/to/file1.txt path/to/file2.txt path/to/dir
|
||||
```
|
||||
|
||||
# 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**.
|
||||
- We've also added native **support for scanning GitHub, GitLab, filesystems, S3, 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
|
||||
## Installation
|
||||
|
||||
Several options:
|
||||
|
||||
### 1. Go
|
||||
```bash
|
||||
git clone https://github.com/trufflesecurity/trufflehog.git
|
||||
|
||||
cd trufflehog; go install
|
||||
```
|
||||
|
||||
### 2. [Release binaries](https://github.com/trufflesecurity/trufflehog/releases)
|
||||
|
||||
### 3. Docker
|
||||
|
||||
|
||||
> Note: Apple M1 hardware users should run with `docker run --rm --platform linux/arm64` for better performance.
|
||||
|
||||
#### **Most users**
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo https://github.com/trufflesecurity/test_keys
|
||||
```
|
||||
|
||||
#### **Apple M1 users**
|
||||
|
||||
The `linux/arm64` image is better to run on the M1 than the amd64 image.
|
||||
Even better is running the native darwin binary available, but there is no container image for that.
|
||||
|
||||
```bash
|
||||
docker run --rm --platform linux/arm64 -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --repo https://github.com/trufflesecurity/test_keys
|
||||
```
|
||||
|
||||
### 4. Pip (help wanted)
|
||||
|
||||
It's possible to distribute binaries in pip wheels.
|
||||
|
||||
Here is an example of a [project that does it](https://github.com/Yelp/dumb-init).
|
||||
|
||||
Help with setting up this packaging would be appreciated!
|
||||
|
||||
### 5. Brew
|
||||
|
||||
```bash
|
||||
brew install trufflesecurity/trufflehog/trufflehog
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
TruffleHog has a sub-command for each source of data that you may want to scan:
|
||||
|
||||
@@ -168,7 +176,6 @@ TruffleHog has a sub-command for each source of data that you may want to scan:
|
||||
- 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:
|
||||
@@ -211,10 +218,18 @@ Exit Codes:
|
||||
- 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
|
||||
#### Scanning an organization
|
||||
|
||||
Try scanning an entire GitHub organization with the following:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v "$PWD:/pwd" trufflesecurity/trufflehog:latest github --org=trufflesecurity
|
||||
```
|
||||
|
||||
### TruffleHog OSS Github Action
|
||||
|
||||
```yaml
|
||||
- name: TruffleHog
|
||||
- name: TruffleHog OSS
|
||||
uses: trufflesecurity/trufflehog@main
|
||||
with:
|
||||
# Repository path
|
||||
@@ -232,7 +247,7 @@ any results are found.
|
||||
|
||||
For example, to scan the contents of pull requests you could use the following workflow:
|
||||
```yaml
|
||||
name: TruffleHog Secrets Scan
|
||||
name: Leaked Secrets Scan
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
TruffleHog:
|
||||
@@ -251,7 +266,7 @@ jobs:
|
||||
extra_args: --debug --only-verified
|
||||
```
|
||||
|
||||
# Precommit Hook
|
||||
### Precommit Hook
|
||||
|
||||
Trufflehog can be used in a precommit 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).
|
||||
@@ -270,7 +285,7 @@ repos:
|
||||
stages: ["commit", "push"]
|
||||
```
|
||||
|
||||
# Regex Detector (alpha)
|
||||
## Regex Detector (alpha)
|
||||
|
||||
Trufflehog supports detection and verification of custom regular expressions.
|
||||
For detection, at least one **regular expression** and **keyword** is required.
|
||||
@@ -284,7 +299,7 @@ status code, the secret is considered verified.
|
||||
|
||||
**NB:** This feature is alpha and subject to change.
|
||||
|
||||
## Regex Detector Example
|
||||
### Regex Detector Example
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
@@ -313,7 +328,7 @@ Raw result: hogs are cool
|
||||
File: /tmp/hog-facts.txt
|
||||
```
|
||||
|
||||
## Verification Server Example (Python)
|
||||
#### 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
|
||||
@@ -363,7 +378,12 @@ with HTTPServer(('', 8000), Verifier) as server:
|
||||
pass
|
||||
```
|
||||
|
||||
# :heart: Contributors
|
||||
## 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.
|
||||
|
||||
## Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
||||
|
||||
@@ -373,28 +393,22 @@ This project exists thanks to all the people who contribute. [[Contribute](CONTR
|
||||
</a>
|
||||
|
||||
|
||||
# :computer: Contributing
|
||||
## 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
|
||||
### 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
|
||||
## 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
|
||||
## Enterprise product
|
||||
|
||||
Are you interested in continously 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/
|
||||
|
||||
|
||||
@@ -32,5 +32,4 @@ runs:
|
||||
- ${{ inputs.head }}
|
||||
- --fail
|
||||
- --no-update
|
||||
- --github-actions
|
||||
- ${{ inputs.extra_args }}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
|
||||
|
Before Width: | Height: | Size: 814 B |
@@ -1,167 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="173.78879mm"
|
||||
height="26.198219mm"
|
||||
viewBox="0 0 173.78879 26.198219"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
|
||||
sodipodi:docname="scanning_logos.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.67348485"
|
||||
inkscape:cx="376.40045"
|
||||
inkscape:cy="538.98763"
|
||||
inkscape:window-width="1250"
|
||||
inkscape:window-height="906"
|
||||
inkscape:window-x="1226"
|
||||
inkscape:window-y="265"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs2"><style
|
||||
id="style344">.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style><style
|
||||
id="style433">
|
||||
.cls-1 {
|
||||
fill: #e25444;
|
||||
}
|
||||
|
||||
.cls-1, .cls-2, .cls-3 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #7b1d13;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #58150d;
|
||||
}
|
||||
</style><path
|
||||
d="M 27.791,115.217 1.541,69.749 a 11.499,11.499 0 0 1 0,-11.499 l 26.25,-45.467 a 11.499,11.499 0 0 1 9.959,-5.75 h 52.5 a 11.5,11.5 0 0 1 9.96,5.75 l 26.25,45.467 a 11.499,11.499 0 0 1 0,11.5 l -26.25,45.466 a 11.499,11.499 0 0 1 -9.96,5.75 h -52.5 a 11.499,11.499 0 0 1 -9.959,-5.75 z"
|
||||
id="b" /></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-5.467857,-5.738096)"><g
|
||||
id="g706"><path
|
||||
fill="#f05133"
|
||||
d="M 88.304634,18.167061 77.689818,7.5527709 c -0.611187,-0.611452 -1.602581,-0.611452 -2.214298,0 l -2.203979,2.204508 2.795852,2.7958521 c 0.649817,-0.219604 1.394883,-0.07223 1.912673,0.445823 0.520965,0.521229 0.667015,1.272646 0.441854,1.924844 l 2.695045,2.694781 c 0.652198,-0.224895 1.404144,-0.07938 1.924844,0.442119 0.727604,0.727604 0.727604,1.906588 0,2.634721 -0.728134,0.727869 -1.907117,0.727869 -2.635515,0 -0.547157,-0.547688 -0.682624,-1.352021 -0.405077,-2.026179 l -2.513541,-2.513277 v 6.613789 c 0.177271,0.08784 0.344753,0.204788 0.49239,0.352425 0.727604,0.727604 0.727604,1.906588 0,2.634986 -0.727604,0.727339 -1.907381,0.727339 -2.634456,0 -0.727604,-0.728663 -0.727604,-1.907646 0,-2.634986 0.179916,-0.179652 0.388144,-0.315648 0.610394,-0.406664 v -6.675438 c -0.22225,-0.09102 -0.429948,-0.225689 -0.610394,-0.406664 -0.551127,-0.550863 -0.683684,-1.359959 -0.401109,-2.036763 l -2.756164,-2.756694 -7.278688,7.277894 c -0.611452,0.611981 -0.611452,1.603375 0,2.214827 l 10.615348,10.614554 c 0.611188,0.611452 1.602317,0.611452 2.214298,0 L 88.304634,20.381888 c 0.61145,-0.611452 0.61145,-1.603375 0,-2.214827 z"
|
||||
id="path79"
|
||||
style="stroke-width:0.264583" /><path
|
||||
d="m 17.874996,7.9032889 c -6.850808,0 -12.407139,5.3892601 -12.407139,12.0363631 0,5.318065 3.554648,9.829669 8.485453,11.421504 0.619321,0.111453 0.819902,-0.261967 0.819902,-0.578771 v -2.240776 c -3.451252,0.728214 -4.169831,-1.420293 -4.169831,-1.420293 -0.564526,-1.391177 -1.378228,-1.76129 -1.378228,-1.76129 -1.125947,-0.747278 0.08582,-0.731237 0.08582,-0.731237 1.245884,0.08414 1.901392,1.240753 1.901392,1.240753 1.106307,1.83957 2.902239,1.307954 3.610476,1.000022 0.110631,-0.777343 0.432184,-1.308957 0.787857,-1.608858 -2.75542,-0.305935 -5.652486,-1.338031 -5.652486,-5.948955 0,-1.314982 0.484911,-2.38821 1.277936,-3.230747 -0.128214,-0.304036 -0.553152,-1.528637 0.120985,-3.185643 0,0 1.0422,-0.322993 3.412995,1.233734 0.989472,-0.266877 2.050281,-0.400211 3.104887,-0.405253 1.054607,0.0033 2.116454,0.138376 3.107992,0.405253 2.368727,-1.556727 3.408859,-1.233734 3.408859,-1.233734 0.675156,1.657988 0.250214,2.882698 0.122,3.185643 0.796124,0.842537 1.276903,1.916758 1.276903,3.230747 0,4.62295 -2.902238,5.641012 -5.664896,5.938948 0.444591,0.373028 0.850923,1.105332 0.850923,2.228716 v 3.30297 c 0,0.319958 0.198515,0.696095 0.828178,0.577744 4.926667,-1.593809 8.477177,-6.104433 8.477177,-11.420477 0,-6.647103 -5.555295,-12.0363631 -12.407141,-12.0363631 z"
|
||||
id="path286"
|
||||
style="stroke-width:1.01836" /><g
|
||||
id="LOGO"
|
||||
transform="matrix(0.12753077,0,0,0.12866528,23.501297,-4.4119714)"><path
|
||||
class="cls-1"
|
||||
d="m 282.83,170.73 -0.27,-0.69 -26.14,-68.22 a 6.81,6.81 0 0 0 -2.69,-3.24 7,7 0 0 0 -8,0.43 7,7 0 0 0 -2.32,3.52 l -17.65,54 h -71.47 l -17.65,-54 a 6.86,6.86 0 0 0 -2.32,-3.53 7,7 0 0 0 -8,-0.43 6.87,6.87 0 0 0 -2.69,3.24 L 97.44,170 l -0.26,0.69 a 48.54,48.54 0 0 0 16.1,56.1 l 0.09,0.07 0.24,0.17 39.82,29.82 19.7,14.91 12,9.06 a 8.07,8.07 0 0 0 9.76,0 l 12,-9.06 19.7,-14.91 40.06,-30 0.1,-0.08 a 48.56,48.56 0 0 0 16.08,-56.04 z"
|
||||
id="path348" /><path
|
||||
class="cls-2"
|
||||
d="m 282.83,170.73 -0.27,-0.69 a 88.3,88.3 0 0 0 -35.15,15.8 L 190,229.25 c 19.55,14.79 36.57,27.64 36.57,27.64 l 40.06,-30 0.1,-0.08 a 48.56,48.56 0 0 0 16.1,-56.08 z"
|
||||
id="path350" /><path
|
||||
class="cls-3"
|
||||
d="m 153.43,256.89 19.7,14.91 12,9.06 a 8.07,8.07 0 0 0 9.76,0 l 12,-9.06 19.7,-14.91 c 0,0 -17.04,-12.89 -36.59,-27.64 -19.55,14.75 -36.57,27.64 -36.57,27.64 z"
|
||||
id="path352" /><path
|
||||
class="cls-2"
|
||||
d="M 132.58,185.84 A 88.19,88.19 0 0 0 97.44,170 l -0.26,0.69 a 48.54,48.54 0 0 0 16.1,56.1 l 0.09,0.07 0.24,0.17 39.82,29.82 c 0,0 17,-12.85 36.57,-27.64 z"
|
||||
id="path354" /></g><g
|
||||
id="g481"
|
||||
transform="matrix(0.06220237,0,0,0.05342262,92.880356,5.1809529)"><path
|
||||
class="cls-1"
|
||||
d="m 378,99 -83,158 83,158 34,-19 V 118 Z"
|
||||
id="path437" /><path
|
||||
class="cls-2"
|
||||
d="M 378,99 212,118 127.5,257 212,396 378,415 Z"
|
||||
id="path439" /><path
|
||||
class="cls-3"
|
||||
d="M 43,99 16,111 V 403 L 43,415 212,257 Z"
|
||||
id="path441" /><path
|
||||
class="cls-1"
|
||||
d="m 42.637,98.667 169.587,47.111 V 372.444 L 42.637,415.111 Z"
|
||||
id="path443" /><path
|
||||
class="cls-3"
|
||||
d="m 212.313,170.667 -72.008,-11.556 72.008,-81.778 71.83,81.778 z"
|
||||
id="path445" /><path
|
||||
class="cls-3"
|
||||
d="m 284.143,159.111 -71.919,11.733 -71.919,-11.733 V 77.333"
|
||||
id="path447" /><path
|
||||
class="cls-3"
|
||||
d="m 212.313,342.222 -72.008,13.334 72.008,70.222 71.83,-70.222 z"
|
||||
id="path449" /><path
|
||||
class="cls-2"
|
||||
d="m 212,16 -72,38 v 105 l 72.224,-20.333 z"
|
||||
id="path451" /><path
|
||||
class="cls-2"
|
||||
d="m 212.224,196.444 -71.919,7.823 v 104.838 l 71.919,8.228 z"
|
||||
id="path453" /><path
|
||||
class="cls-2"
|
||||
d="M 212.224,373.333 140.305,355.3 V 458.363 L 212.224,496 Z"
|
||||
id="path455" /><path
|
||||
class="cls-1"
|
||||
d="m 284.143,355.3 -71.919,18.038 V 496 l 71.919,-37.637 z"
|
||||
id="path457" /><path
|
||||
class="cls-1"
|
||||
d="m 212.224,196.444 71.919,7.823 v 104.838 l -71.919,8.228 z"
|
||||
id="path459" /><path
|
||||
class="cls-1"
|
||||
d="m 212,16 72,38 v 105 l -72,-20 z"
|
||||
id="path461" /></g><g
|
||||
id="g583"
|
||||
transform="matrix(0.20423993,0,0,0.21790562,123.68704,6.5803506)"><g
|
||||
transform="translate(0,-7.034)"
|
||||
id="g549"><linearGradient
|
||||
y2="120.789"
|
||||
x2="64"
|
||||
y1="7.0339999"
|
||||
x1="64"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="a"><stop
|
||||
offset="0"
|
||||
stop-color="#4387fd"
|
||||
id="stop542" /><stop
|
||||
offset="1"
|
||||
stop-color="#4683ea"
|
||||
id="stop544" /></linearGradient><path
|
||||
d="M 27.79,115.217 1.54,69.749 a 11.499,11.499 0 0 1 0,-11.499 L 27.79,12.783 a 11.5,11.5 0 0 1 9.96,-5.75 h 52.5 a 11.5,11.5 0 0 1 9.959,5.75 l 26.25,45.467 a 11.499,11.499 0 0 1 0,11.5 l -26.25,45.466 a 11.5,11.5 0 0 1 -9.959,5.75 h -52.5 a 11.499,11.499 0 0 1 -9.96,-5.75 z"
|
||||
fill="url(#a)"
|
||||
id="path547"
|
||||
style="fill:url(#a)" /></g><g
|
||||
transform="translate(0,-7.034)"
|
||||
id="g559"><defs
|
||||
id="defs552"><path
|
||||
d="M 27.791,115.217 1.541,69.749 a 11.499,11.499 0 0 1 0,-11.499 l 26.25,-45.467 a 11.499,11.499 0 0 1 9.959,-5.75 h 52.5 a 11.5,11.5 0 0 1 9.96,5.75 l 26.25,45.467 a 11.499,11.499 0 0 1 0,11.5 l -26.25,45.466 a 11.499,11.499 0 0 1 -9.96,5.75 h -52.5 a 11.499,11.499 0 0 1 -9.959,-5.75 z"
|
||||
id="path575" /></defs><clipPath
|
||||
id="c"><use
|
||||
height="100%"
|
||||
width="100%"
|
||||
xlink:href="#b"
|
||||
overflow="visible"
|
||||
id="use554" /></clipPath><path
|
||||
clip-path="url(#c)"
|
||||
opacity="0.07"
|
||||
d="m 49.313,53.875 -7.01,6.99 5.957,5.958 -5.898,10.476 44.635,44.636 10.816,0.002 L 118.936,84 85.489,50.55 Z"
|
||||
id="path557" /></g><path
|
||||
d="M 84.7,43.236 H 43.264 c -0.667,0 -1.212,0.546 -1.212,1.214 v 8.566 c 0,0.666 0.546,1.212 1.212,1.212 H 84.7 c 0.667,0 1.213,-0.546 1.213,-1.212 v -8.568 c 0,-0.666 -0.545,-1.213 -1.212,-1.213 m -6.416,7.976 a 2.484,2.484 0 0 1 -2.477,-2.48 2.475,2.475 0 0 1 2.477,-2.477 c 1.37,0 2.48,1.103 2.48,2.477 a 2.48,2.48 0 0 1 -2.48,2.48 m 6.415,8.491 -41.436,0.002 c -0.667,0 -1.212,0.546 -1.212,1.214 v 8.565 c 0,0.666 0.546,1.213 1.212,1.213 H 84.7 c 0.667,0 1.213,-0.547 1.213,-1.213 v -8.567 c 0,-0.666 -0.545,-1.214 -1.212,-1.214 m -6.416,7.976 a 2.483,2.483 0 0 1 -2.477,-2.48 2.475,2.475 0 0 1 2.477,-2.477 2.48,2.48 0 1 1 0,4.956"
|
||||
fill="#ffffff"
|
||||
id="path561" /></g><g
|
||||
style="fill:#000000"
|
||||
id="g668"
|
||||
transform="matrix(1.0414114,0,0,1.0768849,154.45231,5.738096)"><path
|
||||
d="m 8.963,12 c 0,-1.584 1.284,-2.855 2.855,-2.855 1.572,0 2.856,1.284 2.856,2.855 0,1.572 -1.284,2.856 -2.856,2.856 -1.57,0 -2.855,-1.284 -2.855,-2.856 z M 11.818,0 C 6.215,0 1.522,3.84 0.19,9.025 0.18,9.061 0.18,9.095 0.18,9.145 c 0,0.313 0.252,0.576 0.575,0.576 H 5.59 c 0.23,0 0.433,-0.13 0.517,-0.333 0.997,-2.16 3.18,-3.672 5.712,-3.672 3.466,0 6.286,2.82 6.286,6.287 0,3.47 -2.82,6.29 -6.29,6.29 -2.53,0 -4.714,-1.5 -5.71,-3.673 C 6.008,14.43 5.815,14.284 5.588,14.284 H 0.755 c -0.312,0 -0.575,0.253 -0.575,0.576 0,0.037 0.014,0.072 0.014,0.12 1.32,5.18 6.02,9.02 11.624,9.02 6.624,0 12,-5.375 12,-12 0,-6.623 -5.376,-12 -12,-12 z"
|
||||
id="path659" /></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 11 KiB |
+1
-1
@@ -3,4 +3,4 @@
|
||||
# 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[@]}"
|
||||
/usr/bin/trufflehog "${@: 1: $#-1}" "${extra_args[@]}"
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/trufflesecurity/trufflehog/v3
|
||||
|
||||
go 1.20
|
||||
go 1.18
|
||||
|
||||
replace github.com/jpillora/overseer => github.com/trufflesecurity/overseer v1.1.7-custom5
|
||||
|
||||
@@ -10,24 +10,23 @@ replace github.com/gitleaks/go-gitdiff => github.com/trufflesecurity/go-gitdiff
|
||||
|
||||
require (
|
||||
cloud.google.com/go/secretmanager v1.10.0
|
||||
cloud.google.com/go/storage v1.30.1
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
|
||||
github.com/TheZeroSlave/zapsentry v1.15.0
|
||||
github.com/TheZeroSlave/zapsentry v1.14.0
|
||||
github.com/aws/aws-sdk-go v1.44.83
|
||||
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.3.0
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.1.0
|
||||
github.com/crewjam/rfc5424 v0.1.0
|
||||
github.com/denisenkom/go-mssqldb v0.12.3
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/envoyproxy/protoc-gen-validate v0.9.1
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/felixge/fgprof v0.9.3
|
||||
github.com/getsentry/sentry-go v0.20.0
|
||||
github.com/getsentry/sentry-go v0.18.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/go-git/go-git/v5 v5.6.1
|
||||
github.com/go-git/go-git/v5 v5.5.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.4
|
||||
github.com/go-logr/logr v1.2.4
|
||||
github.com/go-logr/logr v1.2.3
|
||||
github.com/go-logr/zapr v1.2.3
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/go-sql-driver/mysql v1.7.0
|
||||
@@ -35,44 +34,39 @@ require (
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/go-github/v42 v42.0.0
|
||||
github.com/googleapis/gax-go/v2 v2.8.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2
|
||||
github.com/jlaffaye/ftp v0.1.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/jpillora/overseer v1.1.6
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/lib/pq v1.10.8
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/paulbellamy/ratecounter v0.2.0
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rabbitmq/amqp091-go v1.8.0
|
||||
github.com/rabbitmq/amqp091-go v1.7.0
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502
|
||||
github.com/xanzy/go-gitlab v0.83.0
|
||||
go.mongodb.org/mongo-driver v1.11.4
|
||||
github.com/xanzy/go-gitlab v0.80.2
|
||||
go.mongodb.org/mongo-driver v1.11.2
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/exp v0.0.0-20221018205818-5c77f4b2bbd7
|
||||
golang.org/x/oauth2 v0.7.0
|
||||
golang.org/x/oauth2 v0.5.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/text v0.9.0
|
||||
google.golang.org/api v0.118.0
|
||||
google.golang.org/protobuf v1.30.0
|
||||
golang.org/x/text v0.7.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
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.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
cloud.google.com/go/compute v1.12.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.1 // indirect
|
||||
cloud.google.com/go/iam v0.8.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
|
||||
@@ -82,11 +76,11 @@ require (
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // 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/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/benbjohnson/clock v1.1.0 // indirect
|
||||
github.com/cloudflare/circl v1.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -95,20 +89,19 @@ require (
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.4.1 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.4.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1 // 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-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-github/v50 v50.2.0 // indirect
|
||||
github.com/google/go-github/v45 v45.2.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.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 // 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
|
||||
@@ -118,17 +111,18 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jpillora/s3 v1.1.4 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.16.0 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.23.0 // indirect
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.2.3 // indirect
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/skeema/knownhosts v1.1.0 // indirect
|
||||
@@ -143,15 +137,15 @@ require (
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
golang.org/x/tools v0.3.0 // indirect
|
||||
google.golang.org/api v0.103.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c // indirect
|
||||
google.golang.org/grpc v1.51.0 // 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
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
|
||||
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
|
||||
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
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/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
|
||||
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
|
||||
cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0=
|
||||
cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
|
||||
cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48=
|
||||
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||
cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk=
|
||||
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
|
||||
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
|
||||
cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60=
|
||||
cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU=
|
||||
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
|
||||
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
|
||||
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/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
@@ -40,21 +35,20 @@ github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzS
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
||||
github.com/TheZeroSlave/zapsentry v1.15.0 h1:w/YglG4Hc2L2VoEH6JKC+3YzhUL18/OZRjJRSvyXZp4=
|
||||
github.com/TheZeroSlave/zapsentry v1.15.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/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
|
||||
github.com/TheZeroSlave/zapsentry v1.14.0 h1:z6H0lIyKVpdXsP5K8J11b4/+V+QCDLDslZmtXsnduMk=
|
||||
github.com/TheZeroSlave/zapsentry v1.14.0/go.mod h1:0hUR1Et5tSroEYhWOZcxE2iBqcFkuG7jGGlUmOA7XAs=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
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/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/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/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/aws/aws-sdk-go v1.44.83 h1:7+Rtc2Eio6EKUNoZeMV/IVxzVrY5oBQcNPtCcgIHYJA=
|
||||
@@ -67,11 +61,10 @@ github.com/bill-rich/go-syslog v0.0.0-20220413021637-49edb52a574c h1:tSME5FDS02q
|
||||
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/bradleyfalzon/ghinstallation/v2 v2.3.0 h1:RRGTqFWOe++1YmvYmO0PvMGzYTaZ6f8X3Su8WKmM+Ds=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.3.0/go.mod h1:8rdGt82ERhM7sjY5FLx2gV4aPhxn6YUE8XlDMKG1z/Y=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.1.0 h1:5+NghM1Zred9Z078QEZtm28G/kfDfZN/92gkDlLwGVA=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.1.0/go.mod h1:Xg3xPRN5Mcq6GDqeUVhFbjEWMb4JHCyWEeeBGEYQoTU=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/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/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=
|
||||
@@ -79,11 +72,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
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/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=
|
||||
@@ -103,21 +91,18 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM
|
||||
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 v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||
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/getsentry/sentry-go v0.20.0 h1:bwXW98iMRIWxn+4FgPW7vMrjmbym6HblXALmhjHmQaQ=
|
||||
github.com/getsentry/sentry-go v0.20.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
|
||||
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
|
||||
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.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
@@ -127,17 +112,17 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
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-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE=
|
||||
github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||
github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk=
|
||||
github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8=
|
||||
github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw=
|
||||
github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
|
||||
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
@@ -156,8 +141,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
|
||||
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/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
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=
|
||||
@@ -169,7 +154,6 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
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.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=
|
||||
@@ -179,9 +163,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
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=
|
||||
@@ -198,26 +181,20 @@ 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-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/v50 v50.2.0 h1:j2FyongEHlO9nxXLc+LP3wuBSVU9mVxfpdYUexMpIfk=
|
||||
github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q=
|
||||
github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
|
||||
github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28=
|
||||
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/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
|
||||
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/s2a-go v0.1.0 h1:3Qm0liEiCErViKERO2Su5wp+9PfMRiuS6XB5FvpKnYQ=
|
||||
github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
|
||||
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
|
||||
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
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=
|
||||
@@ -251,8 +228,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
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.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
||||
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -265,22 +242,23 @@ 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/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE=
|
||||
github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7 h1:xzByj8G8tj0Oq7ZYYU4+ixL/CVb5ruWCm0EZQ1PjOkE=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7/go.mod h1:Fs8qUkO74HHaidabihzYephJH8qmGD/nCP6tE5xC9BM=
|
||||
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/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
@@ -301,8 +279,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||
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/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/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 h1:lL+y4Xv20pVlCGyLzNHRC0I0rIHhIL1lTvHizoS/dU8=
|
||||
@@ -310,8 +286,8 @@ github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9/go.
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
|
||||
github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
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=
|
||||
@@ -321,9 +297,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rabbitmq/amqp091-go v1.8.0 h1:GBFy5PpLQ5jSVVSYv8ecHGqeX7UTLYR4ItQbDCss9MM=
|
||||
github.com/rabbitmq/amqp091-go v1.8.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rabbitmq/amqp091-go v1.7.0 h1:V5CF5qPem5OGSnEo8BoSbsDGwejg6VUJsKEdneaoTUo=
|
||||
github.com/rabbitmq/amqp091-go v1.7.0/go.mod h1:wfClAtY0C7bOHxd3GjmF26jEHn+rR/0B3+YV+Vn9/NI=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
@@ -361,8 +336,8 @@ github.com/trufflesecurity/overseer v1.1.7-custom5/go.mod h1:nT9w37AiO1Nop2VhVhN
|
||||
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/xanzy/go-gitlab v0.83.0 h1:37p0MpTPNbsTMKX/JnmJtY8Ch1sFiJzVF342+RvZEGw=
|
||||
github.com/xanzy/go-gitlab v0.83.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
|
||||
github.com/xanzy/go-gitlab v0.80.2 h1:CH1Q7NDklqZllox4ICVF4PwlhQGfPtE+w08Jsb74ZX0=
|
||||
github.com/xanzy/go-gitlab v0.80.2/go.mod h1:DlByVTSXhPsJMYL6+cm8e8fTJjeBmhrXdC/yvkKKt6M=
|
||||
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=
|
||||
@@ -374,39 +349,37 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk
|
||||
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/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
|
||||
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNHCw=
|
||||
go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
||||
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.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
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/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
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.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
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-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20221018205818-5c77f4b2bbd7 h1:lhh9BuEmjIBOdOhMIGSQzdW5LbTtFbUV1k9O/Rlq3SE=
|
||||
golang.org/x/exp v0.0.0-20221018205818-5c77f4b2bbd7/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
@@ -416,45 +389,36 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
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.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.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-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-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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.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.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
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-20190423024810-112230192c58/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=
|
||||
@@ -470,6 +434,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/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=
|
||||
@@ -478,10 +444,12 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-20210320140829-1e4c9ba3b0c4/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-20210630005230-0f9fa26af87c/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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -489,29 +457,23 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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-20220825204002-c680a09ffe64/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 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.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.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/text v0.3.0/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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
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 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
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=
|
||||
@@ -523,39 +485,32 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
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.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
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=
|
||||
google.golang.org/api v0.118.0 h1:FNfHq9Z2GKULxu7cEhCaB0wWQHg43UpomrrN+24ZRdE=
|
||||
google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E=
|
||||
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
|
||||
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
|
||||
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.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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
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-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU=
|
||||
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
|
||||
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c h1:S34D59DS2GWOEwWNt4fYmTcFrtlOgukG2k9WsomZ7tg=
|
||||
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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.27.0/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.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
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=
|
||||
@@ -567,9 +522,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||
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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/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=
|
||||
@@ -586,7 +540,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
||||
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=
|
||||
@@ -599,6 +552,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -34,24 +34,22 @@ import (
|
||||
)
|
||||
|
||||
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()
|
||||
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()
|
||||
configFilename = cli.Flag("config", "Path to configuration file.").ExistingFile()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
@@ -62,7 +60,6 @@ var (
|
||||
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()
|
||||
@@ -99,24 +96,11 @@ var (
|
||||
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()
|
||||
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()
|
||||
|
||||
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 seperated 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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
|
||||
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()
|
||||
@@ -221,8 +205,6 @@ func run(state overseer.State) {
|
||||
}
|
||||
}
|
||||
|
||||
urls := splitVerifierURLs(*verifiers)
|
||||
|
||||
if *archiveMaxSize != 0 {
|
||||
handlers.SetArchiveMaxSize(int(*archiveMaxSize))
|
||||
}
|
||||
@@ -285,7 +267,7 @@ func run(state overseer.State) {
|
||||
e := engine.Start(ctx,
|
||||
engine.WithConcurrency(*concurrency),
|
||||
engine.WithDecoders(decoders.DefaultDecoders()...),
|
||||
engine.WithDetectors(!*noVerification, engine.CustomDetectors(ctx, urls)...),
|
||||
engine.WithDetectors(!*noVerification, engine.DefaultDetectors()...),
|
||||
engine.WithDetectors(!*noVerification, conf.Detectors...),
|
||||
engine.WithFilterDetectors(includeFilter),
|
||||
engine.WithFilterDetectors(excludeFilter),
|
||||
@@ -307,18 +289,13 @@ func run(state overseer.State) {
|
||||
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,
|
||||
Filter: filter,
|
||||
ExcludeGlobs: excludedGlobs,
|
||||
RepoPath: repoPath,
|
||||
HeadRef: *gitScanBranch,
|
||||
BaseRef: *gitScanSinceCommit,
|
||||
MaxDepth: *gitScanMaxDepth,
|
||||
Filter: filter,
|
||||
}
|
||||
if err = e.ScanGit(ctx, cfg); err != nil {
|
||||
logFatal(err, "Failed to scan Git.")
|
||||
@@ -382,11 +359,10 @@ func run(state overseer.State) {
|
||||
}
|
||||
case s3Scan.FullCommand():
|
||||
cfg := sources.S3Config{
|
||||
Key: *s3ScanKey,
|
||||
Secret: *s3ScanSecret,
|
||||
SessionToken: *s3ScanSessionToken,
|
||||
Buckets: *s3ScanBuckets,
|
||||
CloudCred: *s3ScanCloudEnv,
|
||||
Key: *s3ScanKey,
|
||||
Secret: *s3ScanSecret,
|
||||
Buckets: *s3ScanBuckets,
|
||||
CloudCred: *s3ScanCloudEnv,
|
||||
}
|
||||
if err := e.ScanS3(ctx, cfg); err != nil {
|
||||
logFatal(err, "Failed to scan S3.")
|
||||
@@ -407,23 +383,6 @@ func run(state overseer.State) {
|
||||
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: commaSeperatedToSlice(*gcsIncludeBuckets),
|
||||
ExcludeBuckets: commaSeperatedToSlice(*gcsExcludeBuckets),
|
||||
IncludeObjects: commaSeperatedToSlice(*gcsIncludeObjects),
|
||||
ExcludeObjects: commaSeperatedToSlice(*gcsExcludeObjects),
|
||||
Concurrency: *concurrency,
|
||||
MaxObjectSize: int64(*gcsMaxObjectSize),
|
||||
}
|
||||
if err := e.ScanGCS(ctx, cfg); err != nil {
|
||||
logFatal(err, "Failed to scan GCS.")
|
||||
}
|
||||
}
|
||||
// asynchronously wait for scanning to finish and cleanup
|
||||
go e.Finish(ctx)
|
||||
@@ -447,8 +406,6 @@ func run(state overseer.State) {
|
||||
err = output.PrintLegacyJSON(ctx, &r)
|
||||
case *jsonOut:
|
||||
err = output.PrintJSON(&r)
|
||||
case *gitHubActionsFormat:
|
||||
err = output.PrintGitHubActionsOutput(&r)
|
||||
default:
|
||||
err = output.PrintPlainOutput(&r)
|
||||
}
|
||||
@@ -471,20 +428,6 @@ func run(state overseer.State) {
|
||||
}
|
||||
}
|
||||
|
||||
func commaSeperatedToSlice(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, durations := range e.DetectorAvgTime() {
|
||||
@@ -497,19 +440,6 @@ func printAverageDetectorTime(e *engine.Engine) {
|
||||
}
|
||||
}
|
||||
|
||||
func splitVerifierURLs(verifierURLs map[string]string) map[string][]string {
|
||||
verifiers := make(map[string][]string, len(verifierURLs))
|
||||
for k, v := range verifierURLs {
|
||||
key := strings.ToLower(k)
|
||||
sliceOfValues := strings.Split(v, ",")
|
||||
for i, s := range sliceOfValues {
|
||||
sliceOfValues[i] = strings.TrimSpace(s)
|
||||
}
|
||||
verifiers[key] = sliceOfValues
|
||||
}
|
||||
return verifiers
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
// 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
|
||||
// Contents returns all keys in the cache encoded as a string.
|
||||
Contents() string
|
||||
}
|
||||
Vendored
-85
@@ -1,85 +0,0 @@
|
||||
package 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()
|
||||
}
|
||||
|
||||
// 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, ",")
|
||||
}
|
||||
Vendored
-153
@@ -1,153 +0,0 @@
|
||||
package 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 contents.
|
||||
keys := []string{"key1", "key2", "key3"}
|
||||
for _, k := range keys {
|
||||
c.Set(k, k)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+3
-4
@@ -2,17 +2,16 @@ package 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"}
|
||||
IGNORED_EXTENSIONS = []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 {
|
||||
for _, ext := range IGNORED_EXTENSIONS {
|
||||
if filepath.Ext(filename) == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package 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")
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
dpb "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
@@ -37,17 +36,6 @@ type DetectorID struct {
|
||||
Version int
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -94,8 +94,9 @@ func (c *customRegexWebhook) FromData(ctx context.Context, verify bool, data []b
|
||||
})
|
||||
}
|
||||
|
||||
// Ignore any errors and collect as many of the results as we can.
|
||||
_ = g.Wait()
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
close(resultsCh)
|
||||
|
||||
for result := range resultsCh {
|
||||
@@ -117,7 +118,6 @@ func (c *customRegexWebhook) createResults(ctx context.Context, match map[string
|
||||
}
|
||||
result := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_CustomRegex,
|
||||
DetectorName: c.GetName(),
|
||||
Raw: []byte(raw),
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ func DefaultDecoders() []Decoder {
|
||||
return []Decoder{
|
||||
&UTF8{},
|
||||
&Base64{},
|
||||
&UTF16{},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package decoders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"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 {
|
||||
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) {
|
||||
endianness, err := guessUTF16Endianness(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for i := 0; i < len(b); i += 2 {
|
||||
r := rune(endianness.Uint16(b[i:]))
|
||||
if utf8.ValidRune(r) {
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func guessUTF16Endianness(b []byte) (binary.ByteOrder, error) {
|
||||
if len(b) < 2 || len(b)%2 != 0 {
|
||||
return nil, fmt.Errorf("input length must be even and at least 2 bytes long")
|
||||
}
|
||||
|
||||
var evenNullBytes, oddNullBytes int
|
||||
|
||||
for i := 0; i < len(b); i += 2 {
|
||||
if b[i] == 0 {
|
||||
oddNullBytes++
|
||||
}
|
||||
if b[i+1] == 0 {
|
||||
evenNullBytes++
|
||||
}
|
||||
}
|
||||
|
||||
if evenNullBytes > oddNullBytes {
|
||||
return binary.LittleEndian, nil
|
||||
}
|
||||
if oddNullBytes > evenNullBytes {
|
||||
return binary.BigEndian, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not determine endianness")
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package decoders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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: nil,
|
||||
expectNil: true,
|
||||
},
|
||||
}
|
||||
|
||||
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: %v, got: %v", tc.expected, decodedChunk.Data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package alchemy
|
||||
|
||||
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{"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 {
|
||||
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 {
|
||||
// This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key.
|
||||
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Alchemy
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//go:build detectors
|
||||
// +build detectors
|
||||
|
||||
package alchemy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"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", "detectors4")
|
||||
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
|
||||
}{
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte("You cannot find the secret within"),
|
||||
verify: true,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := Scanner{}
|
||||
got, err := 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])
|
||||
}
|
||||
got[i].Raw = nil
|
||||
}
|
||||
if diff := pretty.Compare(got, tt.want); 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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package alibaba
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -38,9 +38,10 @@ func (s Scanner) Keywords() []string {
|
||||
}
|
||||
|
||||
func randString(n int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
const alphanum = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
var bytes = make([]byte, n)
|
||||
_, _ = rand.Read(bytes)
|
||||
rand.Read(bytes)
|
||||
for i, b := range bytes {
|
||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@ func TestAlibaba_FromChunk(t *testing.T) {
|
||||
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)
|
||||
|
||||
@@ -56,11 +56,11 @@ func TestAWS_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AWS,
|
||||
Verified: true,
|
||||
Redacted: "AKIASP2TPHJSQH3FJRUX",
|
||||
Redacted: "AKIAWARWQKZNHMZBLY4I",
|
||||
ExtraData: map[string]string{
|
||||
"account": "171436882533",
|
||||
"arn": "arn:aws:iam::171436882533:user/canarytokens.com@@4dxkh0pdeop3bzu9zx5wob793",
|
||||
"user_id": "AIDASP2TPHJSUFRSTTZX4",
|
||||
"account": "413504919130",
|
||||
"arn": "arn:aws:iam::413504919130:root",
|
||||
"user_id": "413504919130",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -78,7 +78,7 @@ func TestAWS_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AWS,
|
||||
Verified: false,
|
||||
Redacted: "AKIASP2TPHJSQH3FJRUX",
|
||||
Redacted: "AKIAWARWQKZNHMZBLY4I",
|
||||
ExtraData: nil,
|
||||
},
|
||||
},
|
||||
@@ -106,18 +106,18 @@ func TestAWS_FromChunk(t *testing.T) {
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AWS,
|
||||
Verified: false,
|
||||
Redacted: "AKIASP2TPHJSQH3FJXYZ",
|
||||
Verified: true,
|
||||
Redacted: id,
|
||||
ExtraData: map[string]string{
|
||||
"account": "413504919130",
|
||||
"arn": "arn:aws:iam::413504919130:root",
|
||||
"user_id": "413504919130",
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
Verified: false,
|
||||
Redacted: inactiveID,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -145,11 +145,11 @@ func TestAWS_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AWS,
|
||||
Verified: true,
|
||||
Redacted: "AKIASP2TPHJSQH3FJRUX",
|
||||
Redacted: id,
|
||||
ExtraData: map[string]string{
|
||||
"account": "171436882533",
|
||||
"arn": "arn:aws:iam::171436882533:user/canarytokens.com@@4dxkh0pdeop3bzu9zx5wob793",
|
||||
"user_id": "AIDASP2TPHJSUFRSTTZX4",
|
||||
"account": "413504919130",
|
||||
"arn": "arn:aws:iam::413504919130:root",
|
||||
"user_id": "413504919130",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -172,7 +172,7 @@ func TestAWS_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_AWS,
|
||||
Verified: false,
|
||||
Redacted: "AKIASP2TPHJSQH3FJRUX",
|
||||
Redacted: "AKIAWARWQKZNHMZBLY4I",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -181,7 +181,7 @@ func TestAWS_FromChunk(t *testing.T) {
|
||||
name: "skipped",
|
||||
s: scanner{
|
||||
skipIDs: map[string]struct{}{
|
||||
"AKIASP2TPHJSQH3FJRUX": {},
|
||||
"AKIAWARWQKZNHMZBLY4I": {},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package 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
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ var (
|
||||
client = common.SaneHttpClient()
|
||||
|
||||
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"https://", "http://", "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{"https://", "http://", "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})\b`)
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"browserstack", "key", "automate", "local"}) + `\b([0-9a-zA-Z]{20})\b`)
|
||||
userPat = regexp.MustCompile(detectors.PrefixRegex([]string{"browserstack", "user", "automate", "local"}) + `\b([a-zA-Z\d]{3,18}[._-]?[a-zA-Z\d]{6})\b`)
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
@@ -56,7 +56,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api-cloud.browserstack.com/automate/plan.json", nil)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api-cloud.browserstack.com/app-automate/espresso/v2/apps", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package 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
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package 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
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,6 @@ type Versioner interface {
|
||||
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
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package 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
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -79,16 +79,14 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
raw = []byte(key)
|
||||
}
|
||||
|
||||
credBytes, _ := json.Marshal(creds)
|
||||
|
||||
s := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_GCP,
|
||||
Raw: raw,
|
||||
RawV2: credBytes,
|
||||
Redacted: creds.ClientEmail,
|
||||
}
|
||||
|
||||
if verify {
|
||||
credBytes, _ := json.Marshal(creds)
|
||||
credentials, err := google.CredentialsFromJSON(ctx, credBytes, "https://www.googleapis.com/auth/cloud-platform")
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
@@ -13,34 +13,7 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
)
|
||||
|
||||
const defaultURL = "https://api.github.com"
|
||||
|
||||
type Scanner struct {
|
||||
verifierURLs []string
|
||||
}
|
||||
|
||||
// New creates a new Scanner with the given options.
|
||||
func New(opts ...func(*Scanner)) *Scanner {
|
||||
scanner := &Scanner{
|
||||
verifierURLs: make([]string, 0),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(scanner)
|
||||
}
|
||||
|
||||
return scanner
|
||||
}
|
||||
|
||||
// WithVerifierURLs adds the given URLs to the list of URLs to check for
|
||||
// verification of secrets.
|
||||
func WithVerifierURLs(urls []string, includeDefault bool) func(*Scanner) {
|
||||
return func(s *Scanner) {
|
||||
if includeDefault {
|
||||
urls = append(urls, defaultURL)
|
||||
}
|
||||
s.verifierURLs = append(s.verifierURLs, urls...)
|
||||
}
|
||||
}
|
||||
type Scanner struct{}
|
||||
|
||||
// Ensure the Scanner satisfies the interfaces at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
@@ -56,7 +29,7 @@ var (
|
||||
// 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
|
||||
//TODO: Oauth2 client_id and client_secret
|
||||
// https://developer.github.com/v3/#oauth2-keysecret
|
||||
)
|
||||
|
||||
@@ -82,10 +55,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
|
||||
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
|
||||
if len(s.verifierURLs) == 0 {
|
||||
s.verifierURLs = append(s.verifierURLs, defaultURL)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
// First match is entire regex, second is the first group.
|
||||
if len(match) != 2 {
|
||||
@@ -102,29 +71,27 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
if verify {
|
||||
client := common.SaneHttpClient()
|
||||
// https://developer.github.com/v3/users/#get-the-authenticated-user
|
||||
for _, url := range s.verifierURLs {
|
||||
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 = map[string]string{
|
||||
"username": userResponse.Login,
|
||||
"url": userResponse.UserURL,
|
||||
"account_type": userResponse.Type,
|
||||
"site_admin": fmt.Sprintf("%t", userResponse.SiteAdmin),
|
||||
"name": userResponse.Name,
|
||||
"company": userResponse.Company,
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/user", 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 = map[string]string{
|
||||
"username": userResponse.Login,
|
||||
"url": userResponse.UserURL,
|
||||
"account_type": userResponse.Type,
|
||||
"site_admin": fmt.Sprintf("%t", userResponse.SiteAdmin),
|
||||
"name": userResponse.Name,
|
||||
"company": userResponse.Company,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,34 +12,7 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
const defaultURL = "https://api.github.com"
|
||||
|
||||
type Scanner struct {
|
||||
verifierURLs []string
|
||||
}
|
||||
|
||||
// New creates a new Scanner with the given options.
|
||||
func New(opts ...func(*Scanner)) *Scanner {
|
||||
scanner := &Scanner{
|
||||
verifierURLs: make([]string, 0),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(scanner)
|
||||
}
|
||||
|
||||
return scanner
|
||||
}
|
||||
|
||||
// WithVerifierURLs adds the given URLs to the list of URLs to check for
|
||||
// verification of secrets.
|
||||
func WithVerifierURLs(urls []string, includeDefault bool) func(*Scanner) {
|
||||
return func(s *Scanner) {
|
||||
if includeDefault {
|
||||
urls = append(urls, defaultURL)
|
||||
}
|
||||
s.verifierURLs = append(s.verifierURLs, urls...)
|
||||
}
|
||||
}
|
||||
type Scanner struct{}
|
||||
|
||||
// Ensure the Scanner satisfies the interfaces at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
@@ -90,7 +63,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
continue
|
||||
}
|
||||
|
||||
s1 := detectors.Result{
|
||||
s := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Github,
|
||||
Raw: []byte(token),
|
||||
}
|
||||
@@ -98,32 +71,30 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
if verify {
|
||||
client := common.SaneHttpClient()
|
||||
// https://developer.github.com/v3/users/#get-the-authenticated-user
|
||||
for _, url := range s.verifierURLs {
|
||||
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
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/user", 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 {
|
||||
s.Verified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !s1.Verified && detectors.IsKnownFalsePositive(token, detectors.DefaultFalsePositives, true) {
|
||||
if !s.Verified && detectors.IsKnownFalsePositive(token, detectors.DefaultFalsePositives, true) {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
results = append(results, s)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
||||
@@ -12,34 +12,7 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
const defaultURL = "https://gitlab.com"
|
||||
|
||||
type Scanner struct {
|
||||
verifierURLs []string
|
||||
}
|
||||
|
||||
// New creates a new Scanner with the given options.
|
||||
func New(opts ...func(*Scanner)) *Scanner {
|
||||
scanner := &Scanner{
|
||||
verifierURLs: make([]string, 0),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(scanner)
|
||||
}
|
||||
|
||||
return scanner
|
||||
}
|
||||
|
||||
// WithVerifierURLs adds the given URLs to the list of URLs to check for
|
||||
// verification of secrets.
|
||||
func WithVerifierURLs(urls []string, includeDefault bool) func(*Scanner) {
|
||||
return func(s *Scanner) {
|
||||
if includeDefault {
|
||||
urls = append(urls, defaultURL)
|
||||
}
|
||||
s.verifierURLs = append(s.verifierURLs, urls...)
|
||||
}
|
||||
}
|
||||
type Scanner struct{}
|
||||
|
||||
// Ensure the Scanner satisfies the interfaces at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
@@ -63,10 +36,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
|
||||
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
|
||||
if len(s.verifierURLs) == 0 {
|
||||
s.verifierURLs = append(s.verifierURLs, defaultURL)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) != 2 {
|
||||
continue
|
||||
@@ -87,25 +56,24 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
// 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
|
||||
baseURL := "https://gitlab.com/api/v4"
|
||||
|
||||
client := common.SaneHttpClient()
|
||||
for _, baseURL := range s.verifierURLs {
|
||||
// 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 {
|
||||
res.Body.Close() // The request body is unused.
|
||||
// test `read_user` scope
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/user", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
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
|
||||
if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusForbidden {
|
||||
secret.Verified = true
|
||||
}
|
||||
// 200 means good key and has `read_user` scope
|
||||
// 403 means good key but not the right scope
|
||||
// 401 is bad key
|
||||
if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusForbidden {
|
||||
secret.Verified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,34 +11,7 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
const defaultURL = "https://gitlab.com"
|
||||
|
||||
type Scanner struct {
|
||||
verifierURLs []string
|
||||
}
|
||||
|
||||
// New creates a new Scanner with the given options.
|
||||
func New(opts ...func(*Scanner)) *Scanner {
|
||||
scanner := &Scanner{
|
||||
verifierURLs: make([]string, 0),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(scanner)
|
||||
}
|
||||
|
||||
return scanner
|
||||
}
|
||||
|
||||
// WithVerifierURLs adds the given URLs to the list of URLs to check for
|
||||
// verification of secrets.
|
||||
func WithVerifierURLs(urls []string, includeDefault bool) func(*Scanner) {
|
||||
return func(s *Scanner) {
|
||||
if includeDefault {
|
||||
urls = append(urls, defaultURL)
|
||||
}
|
||||
s.verifierURLs = append(s.verifierURLs, urls...)
|
||||
}
|
||||
}
|
||||
type Scanner struct{}
|
||||
|
||||
// Ensure the Scanner satisfies the interfaces at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
@@ -62,10 +35,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
|
||||
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
|
||||
if len(s.verifierURLs) == 0 {
|
||||
s.verifierURLs = append(s.verifierURLs, defaultURL)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) != 2 {
|
||||
continue
|
||||
@@ -81,27 +50,27 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
// 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 := common.SaneHttpClient()
|
||||
for _, baseURL := range s.verifierURLs {
|
||||
// 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", match[1]))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
res.Body.Close() // The request body is unused.
|
||||
baseURL := "https://gitlab.com/api/v4"
|
||||
|
||||
// 200 means good key and has `read_user` scope
|
||||
// 403 means good key but not the right scope
|
||||
// 401 is bad key
|
||||
if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusForbidden {
|
||||
secret.Verified = true
|
||||
}
|
||||
client := common.SaneHttpClient()
|
||||
|
||||
// test `read_user` scope
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/user", 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 and has `read_user` scope
|
||||
// 403 means good key but not the right scope
|
||||
// 401 is bad key
|
||||
if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusForbidden {
|
||||
secret.Verified = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !secret.Verified && detectors.IsKnownFalsePositive(string(secret.Raw), detectors.DefaultFalsePositives, true) {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package 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
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -13,35 +12,7 @@ import (
|
||||
"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
|
||||
}
|
||||
}
|
||||
type Scanner struct{}
|
||||
|
||||
// Ensure the Scanner satisfies the interface at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
@@ -61,15 +32,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
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{
|
||||
|
||||
@@ -156,57 +156,6 @@ func TestJdbc_FromChunk(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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 BenchmarkFromData(benchmark *testing.B) {
|
||||
ctx := context.Background()
|
||||
s := Scanner{}
|
||||
|
||||
@@ -60,7 +60,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_JiraToken,
|
||||
Raw: []byte(resToken),
|
||||
RawV2: []byte(fmt.Sprintf("%s:%s:%s", resEmail, resToken, resDomain)),
|
||||
}
|
||||
|
||||
if verify {
|
||||
|
||||
@@ -97,7 +97,6 @@ func TestJiraToken_FromChunk(t *testing.T) {
|
||||
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("JiraToken.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type Scanner struct{}
|
||||
|
||||
// Ensure the Scanner satisfies the interface at compile time.
|
||||
var _ detectors.Detector = (*Scanner)(nil)
|
||||
|
||||
@@ -60,7 +61,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
|
||||
if !strings.Contains(body, "AUTHFAIL") {
|
||||
if !strings.Contains(body, "INVALIDTOKEN") {
|
||||
s1.Verified = true
|
||||
} else {
|
||||
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package 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
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
s := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Okta,
|
||||
Raw: []byte(token),
|
||||
RawV2: []byte(fmt.Sprintf("%s:%s", domain, token)),
|
||||
}
|
||||
|
||||
if verify {
|
||||
|
||||
@@ -50,7 +50,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
s := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_OneLogin,
|
||||
Raw: []byte(clientID[1]),
|
||||
RawV2: []byte(fmt.Sprintf("%s:%s", clientID[1], clientSecret[1])),
|
||||
Redacted: clientID[1],
|
||||
}
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
package 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)
|
||||
|
||||
var (
|
||||
keyPat = regexp.MustCompile(`\b((?:sk)-[a-zA-Z0-9]{48})\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{"sk-"}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
//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) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := s.FromData(ctx, false, data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ 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://hooks\.slack\.com/services/T[A-Z0-9]+/B[A-Z0-9]+/[A-Za-z0-9]{23,25})`)
|
||||
keyPat = regexp.MustCompile(`(https://hooks\.slack\.com/services/[A-Za-z0-9+/]{44,46})`)
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
@@ -62,7 +62,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
continue
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
if (res.StatusCode >= 200 && res.StatusCode < 300) || (res.StatusCode == 400 && strings.Contains(body, "no_text")) {
|
||||
if (res.StatusCode >= 200 && res.StatusCode < 300) || (res.StatusCode == 400 && strings.Contains(body, "text")) {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,15 +66,12 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
rawURL, _ := url.Parse(urlMatch)
|
||||
rawURLStr := rawURL.String()
|
||||
// Removing the path causes possible deduplication issues if some paths have basic auth and some do not.
|
||||
rawURL.Path = ""
|
||||
redact := strings.TrimSpace(strings.Replace(rawURL.String(), password, "********", -1))
|
||||
|
||||
s := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_URI,
|
||||
Raw: []byte(rawURL.String()),
|
||||
RawV2: []byte(rawURLStr),
|
||||
Redacted: redact,
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestURI_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_URI,
|
||||
Verified: false,
|
||||
Redacted: "https://user:********@www.httpwatch.com",
|
||||
Redacted: "https://user:****@www.httpwatch.com",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -55,7 +55,7 @@ func TestURI_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_URI,
|
||||
Verified: true,
|
||||
Redacted: "https://httpwatch:********@www.httpwatch.com",
|
||||
Redacted: "https://httpwatch:****@www.httpwatch.com",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -72,7 +72,7 @@ func TestURI_FromChunk(t *testing.T) {
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_URI,
|
||||
Verified: true,
|
||||
Redacted: "https://httpwatch:********@www.httpwatch.com",
|
||||
Redacted: "https://httpwatch:****@www.httpwatch.com",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -101,7 +101,6 @@ func TestURI_FromChunk(t *testing.T) {
|
||||
// }
|
||||
for i := range got {
|
||||
got[i].Raw = nil
|
||||
got[i].RawV2 = nil
|
||||
}
|
||||
if diff := pretty.Compare(got, tt.want); diff != "" {
|
||||
t.Errorf("URI.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/abbysale"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/abuseipdb"
|
||||
@@ -443,7 +442,6 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/onesignal"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/onwaterio"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/oopspam"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/openai"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/opencagedata"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/opengraphr"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/openuv"
|
||||
@@ -732,75 +730,8 @@ import (
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/zipcodebase"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/zonkafeedback"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/zulipchat"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/etherscan"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/infura"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/alchemy"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/blocknative"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/moralis"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/bscscan"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/coinmarketcap"
|
||||
)
|
||||
|
||||
// CustomDetectors returns a list of detectors that are enabled by default, but
|
||||
// can be overridden by the user.
|
||||
func CustomDetectors(ctx context.Context, urls map[string][]string) []detectors.Detector {
|
||||
defaultDetectors := DefaultDetectors()
|
||||
if len(urls) == 0 {
|
||||
return defaultDetectors
|
||||
}
|
||||
|
||||
for i, detector := range defaultDetectors {
|
||||
|
||||
switch detector.Type() {
|
||||
case detectorspb.DetectorType_Github:
|
||||
githubUrls, ok := urls["github"]
|
||||
if !ok {
|
||||
ctx.Logger().V(2).Info("no GitHub urls to ignore")
|
||||
continue
|
||||
}
|
||||
ctx.Logger().V(2).Info("ignoring GitHub urls: %v", githubUrls)
|
||||
|
||||
versioner, ok := detector.(detectors.Versioner)
|
||||
if !ok {
|
||||
ctx.Logger().V(2).Info("Failed to get version for GitHub detector")
|
||||
}
|
||||
switch versioner.Version() {
|
||||
case 1:
|
||||
defaultDetectors[i] = github_old.New(github_old.WithVerifierURLs(githubUrls, true))
|
||||
default:
|
||||
defaultDetectors[i] = github.New(github.WithVerifierURLs(githubUrls, true))
|
||||
}
|
||||
|
||||
case detectorspb.DetectorType_Gitlab:
|
||||
gitlabUrls, ok := urls["gitlab"]
|
||||
if !ok {
|
||||
ctx.Logger().V(2).Info("no GitLab urls to ignore")
|
||||
continue
|
||||
}
|
||||
ctx.Logger().V(2).Info("ignoring GitLab urls: %v", gitlabUrls)
|
||||
|
||||
versioner, ok := detector.(detectors.Versioner)
|
||||
if !ok {
|
||||
ctx.Logger().V(2).Info("Failed to get version for Gitlab detector")
|
||||
}
|
||||
switch versioner.Version() {
|
||||
case 1:
|
||||
defaultDetectors[i] = gitlab.New(gitlab.WithVerifierURLs(gitlabUrls, true))
|
||||
default:
|
||||
defaultDetectors[i] = gitlabv2.New(gitlabv2.WithVerifierURLs(gitlabUrls, true))
|
||||
}
|
||||
|
||||
case detectorspb.DetectorType_JiraToken:
|
||||
// TODO(ahrav): Double check that we need to do this.
|
||||
default:
|
||||
ctx.Logger().V(5).Info("ignoring custom detector", "type", detector.Type())
|
||||
continue
|
||||
}
|
||||
}
|
||||
return defaultDetectors
|
||||
}
|
||||
|
||||
func DefaultDetectors() []detectors.Detector {
|
||||
return []detectors.Detector{
|
||||
&heroku.Scanner{},
|
||||
@@ -1235,7 +1166,6 @@ func DefaultDetectors() []detectors.Detector {
|
||||
signupgenius.Scanner{},
|
||||
streak.Scanner{},
|
||||
route4me.Scanner{},
|
||||
openai.Scanner{},
|
||||
opencagedata.Scanner{},
|
||||
positionstack.Scanner{},
|
||||
upcdatabase.Scanner{},
|
||||
@@ -1558,13 +1488,5 @@ func DefaultDetectors() []detectors.Detector {
|
||||
ftp.Scanner{},
|
||||
ldap.Scanner{},
|
||||
shopify.Scanner{},
|
||||
etherscan.Scanner{},
|
||||
infura.Scanner{},
|
||||
alchemy.Scanner{},
|
||||
blocknative.Scanner{},
|
||||
moralis.Scanner{},
|
||||
bscscan.Scanner{},
|
||||
coinmarketcap.Scanner{},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-25
@@ -13,7 +13,6 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"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"
|
||||
@@ -150,29 +149,14 @@ func Start(ctx context.Context, options ...EngineOption) *Engine {
|
||||
})
|
||||
e.prefilter = builder.Build(keywords)
|
||||
|
||||
ctx.Logger().Info("loaded decoders", "count", len(e.decoders))
|
||||
ctx.Logger().Info("loaded detectors",
|
||||
ctx.Logger().V(2).Info("loaded decoders", "count", len(e.decoders))
|
||||
ctx.Logger().V(2).Info("loaded detectors",
|
||||
"total", len(e.detectors[true])+len(e.detectors[false]),
|
||||
"verification_enabled", len(e.detectors[true]),
|
||||
"verification_disabled", len(e.detectors[false]),
|
||||
)
|
||||
|
||||
// Sanity check detectors for duplicate configuration. Only log in case
|
||||
// a detector has been configured in a way that isn't represented by
|
||||
// the DetectorID (type and version).
|
||||
{
|
||||
dets := append(e.detectors[true], e.detectors[false]...)
|
||||
seenDetectors := make(map[config.DetectorID]struct{}, len(dets))
|
||||
for _, det := range dets {
|
||||
id := config.GetDetectorID(det)
|
||||
if _, ok := seenDetectors[id]; ok {
|
||||
ctx.Logger().Info("possible duplicate detector configured", "detector", id)
|
||||
}
|
||||
seenDetectors[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the workers.
|
||||
// start the workers
|
||||
for i := 0; i < e.concurrency; i++ {
|
||||
e.workersWg.Add(1)
|
||||
go func() {
|
||||
@@ -254,8 +238,6 @@ func (e *Engine) detectorWorker(ctx context.Context) {
|
||||
decoderType = detectorspb.DecoderType_PLAIN
|
||||
case *decoders.Base64:
|
||||
decoderType = detectorspb.DecoderType_BASE64
|
||||
case *decoders.UTF16:
|
||||
decoderType = detectorspb.DecoderType_UTF16
|
||||
default:
|
||||
ctx.Logger().Info("unknown decoder type", "type", reflect.TypeOf(decoder).String())
|
||||
decoderType = detectorspb.DecoderType_UNKNOWN
|
||||
@@ -265,9 +247,11 @@ func (e *Engine) detectorWorker(ctx context.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
// build a map of all keywords that were matched in the chunk
|
||||
for _, m := range e.prefilter.FindAll(string(decoded.Data)) {
|
||||
matchedKeywords[strings.ToLower(string(decoded.Data[m.Start():m.End()]))] = struct{}{}
|
||||
dataLower := strings.ToLower(string(decoded.Data))
|
||||
matches := e.prefilter.FindAll(dataLower)
|
||||
|
||||
for _, m := range matches {
|
||||
matchedKeywords[dataLower[m.Start():m.End()]] = struct{}{}
|
||||
}
|
||||
|
||||
for verify, detectorsSet := range e.detectors {
|
||||
@@ -276,7 +260,6 @@ func (e *Engine) detectorWorker(ctx context.Context) {
|
||||
for _, kw := range detector.Keywords() {
|
||||
if _, ok := matchedKeywords[strings.ToLower(kw)]; ok {
|
||||
chunkContainsKeyword = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources/gcs"
|
||||
)
|
||||
|
||||
// ScanGCS with the provided options.
|
||||
func (e *Engine) ScanGCS(ctx context.Context, c sources.GCSConfig) error {
|
||||
// Project ID is required if using any authenticated access.
|
||||
if c.ProjectID == "" && !c.WithoutAuth {
|
||||
return fmt.Errorf("project ID is required")
|
||||
}
|
||||
|
||||
// If using unauthenticated access, the project ID is not used.
|
||||
if c.ProjectID != "" && c.WithoutAuth {
|
||||
c.ProjectID = ""
|
||||
ctx.Logger().Info("project ID is not used when using unauthenticated access, ignoring provided project ID")
|
||||
}
|
||||
|
||||
connection := &sourcespb.GCS{
|
||||
ProjectId: c.ProjectID,
|
||||
IncludeBuckets: c.IncludeBuckets,
|
||||
ExcludeBuckets: c.ExcludeBuckets,
|
||||
IncludeObjects: c.IncludeObjects,
|
||||
ExcludeObjects: c.ExcludeObjects,
|
||||
}
|
||||
|
||||
// Make sure only one auth method is selected.
|
||||
if !isAuthValid(ctx, c, connection) {
|
||||
return fmt.Errorf("multiple auth methods selected, please select only one")
|
||||
}
|
||||
|
||||
var conn anypb.Any
|
||||
err := anypb.MarshalFrom(&conn, connection, proto.MarshalOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal GCS connection: %w", err)
|
||||
}
|
||||
|
||||
source := gcs.Source{}
|
||||
ctx = context.WithValues(ctx,
|
||||
"source_type", source.Type().String(),
|
||||
"source_name", "gcs",
|
||||
)
|
||||
if err = source.Init(ctx, "trufflehog - GCS", 0, 0, true, &conn, int(c.Concurrency)); err != nil {
|
||||
return fmt.Errorf("failed to initialize GCS source: %w", err)
|
||||
}
|
||||
|
||||
e.sourcesWg.Add(1)
|
||||
go func() {
|
||||
defer common.RecoverWithExit(ctx)
|
||||
defer e.sourcesWg.Done()
|
||||
if err := source.Chunks(ctx, e.ChunksChan()); err != nil {
|
||||
ctx.Logger().Error(err, "could not scan GCS")
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func isAuthValid(ctx context.Context, c sources.GCSConfig, connection *sourcespb.GCS) bool {
|
||||
var isAuthSelected bool
|
||||
|
||||
if c.WithoutAuth {
|
||||
isAuthSelected = true
|
||||
connection.Credential = &sourcespb.GCS_Unauthenticated{}
|
||||
}
|
||||
if c.CloudCred {
|
||||
if isAuthSelected {
|
||||
return false
|
||||
}
|
||||
isAuthSelected = true
|
||||
connection.Credential = &sourcespb.GCS_Adc{}
|
||||
}
|
||||
if c.ServiceAccount != "" {
|
||||
if isAuthSelected {
|
||||
return false
|
||||
}
|
||||
isAuthSelected = true
|
||||
connection.Credential = &sourcespb.GCS_ServiceAccountFile{
|
||||
ServiceAccountFile: c.ServiceAccount,
|
||||
}
|
||||
}
|
||||
if c.ApiKey != "" {
|
||||
if isAuthSelected {
|
||||
return false
|
||||
}
|
||||
isAuthSelected = true
|
||||
connection.Credential = &sourcespb.GCS_ApiKey{
|
||||
ApiKey: c.ApiKey,
|
||||
}
|
||||
}
|
||||
if !isAuthSelected {
|
||||
ctx.Logger().Info("no auth method selected, using unauthenticated")
|
||||
connection.Credential = &sourcespb.GCS_Unauthenticated{}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
func TestScanGCS(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
gcsConfig sources.GCSConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "scanned GCS",
|
||||
gcsConfig: sources.GCSConfig{
|
||||
ApiKey: "abc123",
|
||||
ProjectID: "test-project",
|
||||
CloudCred: false,
|
||||
WithoutAuth: false,
|
||||
ServiceAccount: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing project ID, with auth",
|
||||
gcsConfig: sources.GCSConfig{ApiKey: "abc123"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing project ID, without auth, public scan",
|
||||
gcsConfig: sources.GCSConfig{WithoutAuth: true},
|
||||
},
|
||||
{
|
||||
name: "multiple selected auth methods",
|
||||
gcsConfig: sources.GCSConfig{
|
||||
ApiKey: "abc123",
|
||||
ProjectID: "test-project",
|
||||
CloudCred: true,
|
||||
WithoutAuth: false,
|
||||
ServiceAccount: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no auth method selected",
|
||||
gcsConfig: sources.GCSConfig{
|
||||
ProjectID: "test-project",
|
||||
MaxObjectSize: 10 * 1024 * 1024,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
e := &Engine{}
|
||||
err := e.ScanGCS(context.Background(), test.gcsConfig)
|
||||
if err != nil && !test.wantErr && !strings.Contains(err.Error(), "googleapi: Error 400: Bad Request") {
|
||||
t.Errorf("ScanGCS() got: %v, want: %v", err, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil && test.wantErr {
|
||||
t.Errorf("ScanGCS() got: %v, want: %v", err, "error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,6 @@ func (e *Engine) ScanGit(ctx context.Context, c sources.GitConfig) error {
|
||||
if c.HeadRef != "" {
|
||||
opts = append(opts, git.ScanOptionHeadCommit(c.HeadRef))
|
||||
}
|
||||
if c.ExcludeGlobs != nil {
|
||||
opts = append(opts, git.ScanOptionExcludeGlobs(c.ExcludeGlobs))
|
||||
}
|
||||
scanOptions := git.NewScanOptions(opts...)
|
||||
|
||||
gitSource := git.NewGit(sourcespb.SourceType_SOURCE_TYPE_GIT, 0, 0, "trufflehog - git", true, runtime.NumCPU(),
|
||||
|
||||
+7
-17
@@ -22,27 +22,17 @@ func (e *Engine) ScanS3(ctx context.Context, c sources.S3Config) error {
|
||||
Credential: &sourcespb.S3_Unauthenticated{},
|
||||
}
|
||||
if c.CloudCred {
|
||||
if len(c.Key) > 0 || len(c.Secret) > 0 || len(c.SessionToken) > 0 {
|
||||
return fmt.Errorf("cannot use cloud environment and static credentials together")
|
||||
if len(c.Key) > 0 || len(c.Secret) > 0 {
|
||||
return fmt.Errorf("cannot use cloud credentials and basic auth together")
|
||||
}
|
||||
connection.Credential = &sourcespb.S3_CloudEnvironment{}
|
||||
}
|
||||
if len(c.Key) > 0 && len(c.Secret) > 0 {
|
||||
if len(c.SessionToken) > 0 {
|
||||
connection.Credential = &sourcespb.S3_SessionToken{
|
||||
SessionToken: &credentialspb.AWSSessionTokenSecret{
|
||||
Key: c.Key,
|
||||
Secret: c.Secret,
|
||||
SessionToken: c.SessionToken,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
connection.Credential = &sourcespb.S3_AccessKey{
|
||||
AccessKey: &credentialspb.KeySecret{
|
||||
Key: c.Key,
|
||||
Secret: c.Secret,
|
||||
},
|
||||
}
|
||||
connection.Credential = &sourcespb.S3_AccessKey{
|
||||
AccessKey: &credentialspb.KeySecret{
|
||||
Key: c.Key,
|
||||
Secret: c.Secret,
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(c.Buckets) > 0 {
|
||||
|
||||
+147
-19
@@ -116,27 +116,22 @@ func (c1 *Commit) Equal(c2 *Commit) bool {
|
||||
}
|
||||
|
||||
// RepoPath parses the output of the `git log` command for the `source` path.
|
||||
func (c *Parser) RepoPath(ctx context.Context, source string, head string, abbreviatedLog bool, excludedGlobs []string) (chan Commit, error) {
|
||||
args := []string{"-C", source, "log", "-p", "-U5", "--full-history", "--date=format:%a %b %d %H:%M:%S %Y %z"}
|
||||
if abbreviatedLog {
|
||||
args = append(args, "--diff-filter=AM")
|
||||
}
|
||||
func (c *Parser) RepoPath(ctx context.Context, source string, head string, abbreviatedLog bool) (chan Commit, error) {
|
||||
args := []string{"-C", source, "log", "--full-history", "--date=format:%a %b %d %H:%M:%S %Y %z"}
|
||||
if head != "" {
|
||||
args = append(args, head)
|
||||
} else {
|
||||
args = append(args, "--all")
|
||||
}
|
||||
for _, glob := range excludedGlobs {
|
||||
args = append(args, "--", ".", fmt.Sprintf(":(exclude)%s", glob))
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", args...)
|
||||
|
||||
absPath, err := filepath.Abs(source)
|
||||
if err == nil {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GIT_DIR=%s", filepath.Join(absPath, ".git")))
|
||||
}
|
||||
|
||||
return c.executeCommand(ctx, cmd)
|
||||
return c.executeCommand(ctx, source, cmd)
|
||||
}
|
||||
|
||||
// Unstaged parses the output of the `git diff` command for the `source` path.
|
||||
@@ -151,11 +146,11 @@ func (c *Parser) Unstaged(ctx context.Context, source string) (chan Commit, erro
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GIT_DIR=%s", filepath.Join(absPath, ".git")))
|
||||
}
|
||||
|
||||
return c.executeCommand(ctx, cmd)
|
||||
return c.executeCommand(ctx, source, cmd)
|
||||
}
|
||||
|
||||
// executeCommand runs an exec.Cmd, reads stdout and stderr, and waits for the Cmd to complete.
|
||||
func (c *Parser) executeCommand(ctx context.Context, cmd *exec.Cmd) (chan Commit, error) {
|
||||
func (c *Parser) executeCommand(ctx context.Context, source string, cmd *exec.Cmd) (chan Commit, error) {
|
||||
commitChan := make(chan Commit, 64)
|
||||
|
||||
stdOut, err := cmd.StdoutPipe()
|
||||
@@ -180,7 +175,7 @@ func (c *Parser) executeCommand(ctx context.Context, cmd *exec.Cmd) (chan Commit
|
||||
}()
|
||||
|
||||
go func() {
|
||||
c.FromReader(ctx, stdOut, commitChan)
|
||||
c.fromReader(ctx, source, stdOut, commitChan)
|
||||
if err := cmd.Wait(); err != nil {
|
||||
ctx.Logger().V(2).Info("Error waiting for git command to complete.", "error", err)
|
||||
}
|
||||
@@ -189,12 +184,12 @@ func (c *Parser) executeCommand(ctx context.Context, cmd *exec.Cmd) (chan Commit
|
||||
return commitChan, nil
|
||||
}
|
||||
|
||||
func (c *Parser) FromReader(ctx context.Context, stdOut io.Reader, commitChan chan Commit) {
|
||||
func (c *Parser) fromReader(ctx context.Context, source string, stdOut io.Reader, commitChan chan Commit) {
|
||||
outReader := bufio.NewReader(stdOut)
|
||||
var currentCommit *Commit
|
||||
var currentDiff *Diff
|
||||
var recentlyPassedAuthor bool
|
||||
|
||||
lastCommit := ""
|
||||
defer common.RecoverWithExit(ctx)
|
||||
defer close(commitChan)
|
||||
for {
|
||||
@@ -205,6 +200,143 @@ func (c *Parser) FromReader(ctx context.Context, stdOut io.Reader, commitChan ch
|
||||
if err != nil && len(line) == 0 {
|
||||
break
|
||||
}
|
||||
// for i := 0; i < len(line); i++ {
|
||||
// b := make([]byte, 1)
|
||||
// n, err := stdOut.Read(b)
|
||||
// if errors.Is(err, io.EOF) || n == 0 {
|
||||
// return
|
||||
// }
|
||||
// line[i] = b[0]
|
||||
// if b[0] == byte('\n') {
|
||||
// line = line[:0]
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
switch {
|
||||
case isCommitLine(line):
|
||||
// If there is a currentDiff, add it to currentCommit.
|
||||
if currentDiff != nil && currentDiff.Content.Len() > 0 {
|
||||
currentCommit.Diffs = append(currentCommit.Diffs, *currentDiff)
|
||||
}
|
||||
// If there is a currentCommit, send it to the channel.
|
||||
if currentCommit != nil {
|
||||
args := []string{"-C", source, "diff", "--date=format:%a %b %d %H:%M:%S %Y %z", currentCommit.Hash}
|
||||
if lastCommit != "" {
|
||||
args = append(args, lastCommit)
|
||||
}
|
||||
lastCommit = currentCommit.Hash
|
||||
|
||||
cmd := exec.Command("git", args...)
|
||||
|
||||
absPath, err := filepath.Abs(source)
|
||||
if err == nil {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GIT_DIR=%s", filepath.Join(absPath, ".git")))
|
||||
}
|
||||
|
||||
// stdnOut, err := cmd.StdoutPipe()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_ = cmd.Wait()
|
||||
//c.funkReader(ctx, stdnOut, commitChan, currentCommit)
|
||||
}
|
||||
// Create a new currentDiff and currentCommit
|
||||
currentDiff = &Diff{}
|
||||
currentCommit = &Commit{
|
||||
Message: strings.Builder{},
|
||||
}
|
||||
// Check that the commit line contains a hash and set it.
|
||||
if len(line) >= 47 {
|
||||
currentCommit.Hash = string(line[7:47])
|
||||
}
|
||||
case isAuthorLine(line):
|
||||
currentCommit.Author = strings.TrimRight(string(line[8:]), "\n")
|
||||
case isDateLine(line):
|
||||
date, err := time.Parse(c.dateFormat, strings.TrimSpace(string(line[6:])))
|
||||
if err != nil {
|
||||
ctx.Logger().V(2).Info("Could not parse date from git stream.", "error", err)
|
||||
}
|
||||
currentCommit.Date = date
|
||||
case isModeLine(line):
|
||||
// NoOp
|
||||
case isIndexLine(line):
|
||||
// NoOp
|
||||
case isPlusFileLine(line):
|
||||
currentDiff.PathB = strings.TrimRight(strings.TrimRight(string(line[6:]), "\n"), "\t") // Trim the newline and tab characters. https://github.com/trufflesecurity/trufflehog/issues/1060
|
||||
case isMinusFileLine(line):
|
||||
// NoOp
|
||||
case isPlusDiffLine(line):
|
||||
currentDiff.Content.Write(line[1:])
|
||||
case isMinusDiffLine(line):
|
||||
// NoOp. We only care about additions.
|
||||
case isMessageLine(line):
|
||||
currentCommit.Message.Write(line[4:])
|
||||
case isContextDiffLine(line):
|
||||
currentDiff.Content.Write([]byte("\n"))
|
||||
case isBinaryLine(line):
|
||||
currentDiff.IsBinary = true
|
||||
currentDiff.PathB = pathFromBinaryLine(line)
|
||||
case isLineNumberDiffLine(line):
|
||||
if currentDiff != nil && currentDiff.Content.Len() > 0 {
|
||||
currentCommit.Diffs = append(currentCommit.Diffs, *currentDiff)
|
||||
}
|
||||
newDiff := &Diff{
|
||||
PathB: currentDiff.PathB,
|
||||
}
|
||||
|
||||
currentDiff = newDiff
|
||||
|
||||
words := bytes.Split(line, []byte(" "))
|
||||
if len(words) >= 3 {
|
||||
startSlice := bytes.Split(words[2], []byte(","))
|
||||
lineStart, err := strconv.Atoi(string(startSlice[0]))
|
||||
if err == nil {
|
||||
currentDiff.LineStart = lineStart
|
||||
}
|
||||
}
|
||||
}
|
||||
if currentDiff.Content.Len() > c.maxDiffSize {
|
||||
ctx.Logger().V(2).Info(fmt.Sprintf(
|
||||
"Diff for %s exceeded MaxDiffSize(%d)", currentDiff.PathB, c.maxDiffSize,
|
||||
))
|
||||
break
|
||||
}
|
||||
}
|
||||
cleanupParse(currentCommit, currentDiff, commitChan)
|
||||
}
|
||||
|
||||
func (c *Parser) funkReader(ctx context.Context, stdOut io.Reader, commitChan chan Commit, currentCommit *Commit) {
|
||||
outReader := bufio.NewReader(stdOut)
|
||||
var currentDiff *Diff
|
||||
|
||||
defer common.RecoverWithExit(ctx)
|
||||
for {
|
||||
if common.IsDone(ctx) {
|
||||
break
|
||||
}
|
||||
line, err := outReader.ReadBytes([]byte("\n")[0])
|
||||
if err != nil && len(line) == 0 {
|
||||
break
|
||||
}
|
||||
// for i := 0; i < len(line); i++ {
|
||||
// b := make([]byte, 1)
|
||||
// n, err := stdOut.Read(b)
|
||||
// if errors.Is(err, io.EOF) || n == 0 {
|
||||
// return
|
||||
// }
|
||||
// line[i] = b[0]
|
||||
// if b[0] == byte('\n') {
|
||||
// line = line[:0]
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
switch {
|
||||
case isCommitLine(line):
|
||||
// If there is a currentDiff, add it to currentCommit.
|
||||
@@ -226,7 +358,6 @@ func (c *Parser) FromReader(ctx context.Context, stdOut io.Reader, commitChan ch
|
||||
}
|
||||
case isAuthorLine(line):
|
||||
currentCommit.Author = strings.TrimRight(string(line[8:]), "\n")
|
||||
recentlyPassedAuthor = true
|
||||
case isDateLine(line):
|
||||
date, err := time.Parse(c.dateFormat, strings.TrimSpace(string(line[6:])))
|
||||
if err != nil {
|
||||
@@ -263,7 +394,6 @@ func (c *Parser) FromReader(ctx context.Context, stdOut io.Reader, commitChan ch
|
||||
case isModeLine(line):
|
||||
// NoOp
|
||||
case isIndexLine(line):
|
||||
recentlyPassedAuthor = false
|
||||
// NoOp
|
||||
case isPlusFileLine(line):
|
||||
currentDiff.PathB = strings.TrimRight(strings.TrimRight(string(line[6:]), "\n"), "\t") // Trim the newline and tab characters. https://github.com/trufflesecurity/trufflehog/issues/1060
|
||||
@@ -274,9 +404,7 @@ func (c *Parser) FromReader(ctx context.Context, stdOut io.Reader, commitChan ch
|
||||
case isMinusDiffLine(line):
|
||||
// NoOp. We only care about additions.
|
||||
case isMessageLine(line):
|
||||
if recentlyPassedAuthor {
|
||||
currentCommit.Message.Write(line[4:])
|
||||
}
|
||||
currentCommit.Message.Write(line[4:])
|
||||
case isContextDiffLine(line):
|
||||
currentDiff.Content.Write([]byte("\n"))
|
||||
case isBinaryLine(line):
|
||||
|
||||
@@ -2,7 +2,6 @@ package gitparse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -110,7 +109,7 @@ func TestSingleCommitSingleDiff(t *testing.T) {
|
||||
},
|
||||
}
|
||||
go func() {
|
||||
parser.FromReader(context.TODO(), r, commitChan)
|
||||
parser.fromReader(context.TODO(), r, commitChan)
|
||||
}()
|
||||
i := 0
|
||||
for commit := range commitChan {
|
||||
@@ -169,7 +168,7 @@ func TestMultiCommitContextDiff(t *testing.T) {
|
||||
},
|
||||
}
|
||||
go func() {
|
||||
NewParser().FromReader(context.TODO(), r, commitChan)
|
||||
NewParser().fromReader(context.TODO(), r, commitChan)
|
||||
}()
|
||||
i := 0
|
||||
for commit := range commitChan {
|
||||
@@ -199,7 +198,7 @@ func TestMaxDiffSize(t *testing.T) {
|
||||
|
||||
commitChan := make(chan Commit)
|
||||
go func() {
|
||||
parser.FromReader(context.TODO(), bigReader, commitChan)
|
||||
parser.fromReader(context.TODO(), bigReader, commitChan)
|
||||
}()
|
||||
|
||||
commit := <-commitChan
|
||||
@@ -217,7 +216,7 @@ func TestMaxCommitSize(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(1)*time.Second)
|
||||
defer cancel()
|
||||
go func() {
|
||||
parser.FromReader(ctx, &commitText, commitChan)
|
||||
parser.fromReader(ctx, &commitText, commitChan)
|
||||
}()
|
||||
commitCount := 0
|
||||
for range commitChan {
|
||||
@@ -229,38 +228,6 @@ func TestMaxCommitSize(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestMessageParsing(t *testing.T) {
|
||||
r := bytes.NewReader([]byte(fmt.Sprintf(singleCommitTabbedContent, ` `)))
|
||||
commitChan := make(chan Commit)
|
||||
parser := NewParser()
|
||||
expectedMessage := singleCommitSingleDiffMessage
|
||||
|
||||
go func() {
|
||||
parser.FromReader(context.TODO(), r, commitChan)
|
||||
}()
|
||||
for commit := range commitChan {
|
||||
if commit.Message.String() != expectedMessage {
|
||||
t.Errorf("Message does not match. Got:\n%s\nexpected:\n%s", commit.Message.String(), expectedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const singleCommitTabbedContent = `commit 70001020fab32b1fcf2f1f0e5c66424eae649826 (HEAD -> master, origin/master, origin/HEAD)
|
||||
Author: Dustin Decker <humanatcomputer@gmail.com>
|
||||
Date: Mon Mar 15 23:27:16 2021 -0700
|
||||
|
||||
Update aws
|
||||
|
||||
diff --git a/aws b/aws
|
||||
index 2ee133b..12b4843 100644
|
||||
--- a/aws
|
||||
+++ b/aws
|
||||
@@ -1,7 +1,5 @@
|
||||
+[default]
|
||||
%saws_access_key_id = AKIAXYZDQCEN4B6JSJQI
|
||||
+aws_secret_access_key = Tg0pz8Jii8hkLx4+PnUisM8GmKs3a2DK+9qz/lie
|
||||
`
|
||||
|
||||
const singleCommitSingleDiff = `commit 70001020fab32b1fcf2f1f0e5c66424eae649826 (HEAD -> master, origin/master, origin/HEAD)
|
||||
Author: Dustin Decker <humanatcomputer@gmail.com>
|
||||
Date: Mon Mar 15 23:27:16 2021 -0700
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
)
|
||||
|
||||
var dedupeCache = make(map[string]struct{})
|
||||
|
||||
func PrintGitHubActionsOutput(r *detectors.ResultWithMetadata) error {
|
||||
out := gitHubActionsOutputFormat{
|
||||
DetectorType: r.Result.DetectorType.String(),
|
||||
DecoderType: r.Result.DecoderType.String(),
|
||||
Verified: r.Result.Verified,
|
||||
}
|
||||
|
||||
meta, err := structToMap(r.SourceMetadata.Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not marshal result: %w", err)
|
||||
}
|
||||
|
||||
for _, data := range meta {
|
||||
for k, v := range data {
|
||||
if k == "line" {
|
||||
if line, ok := v.(float64); ok {
|
||||
out.StartLine = int64(line)
|
||||
}
|
||||
}
|
||||
if k == "file" {
|
||||
if filename, ok := v.(string); ok {
|
||||
out.Filename = filename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verifiedStatus := "unverified"
|
||||
if out.Verified {
|
||||
verifiedStatus = "verified"
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("%s:%s:%s:%s:%d", out.DecoderType, out.DetectorType, verifiedStatus, out.Filename, out.StartLine)
|
||||
h := sha256.New()
|
||||
h.Write([]byte(key))
|
||||
key = hex.EncodeToString(h.Sum(nil))
|
||||
if _, ok := dedupeCache[key]; ok {
|
||||
return nil
|
||||
}
|
||||
dedupeCache[key] = struct{}{}
|
||||
|
||||
message := fmt.Sprintf("Found %s %s result 🐷🔑\n", verifiedStatus, out.DetectorType)
|
||||
if r.Result.DecoderType != detectorspb.DecoderType_PLAIN {
|
||||
message = fmt.Sprintf("Found %s %s result with %s encoding 🐷🔑\n", verifiedStatus, out.DetectorType, out.DecoderType)
|
||||
}
|
||||
|
||||
fmt.Printf("::warning file=%s,line=%d,endLine=%d::%s",
|
||||
out.Filename, out.StartLine, out.StartLine, message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type gitHubActionsOutputFormat struct {
|
||||
DetectorType,
|
||||
DecoderType string
|
||||
Verified bool
|
||||
StartLine int64
|
||||
Filename string
|
||||
}
|
||||
@@ -386,7 +386,6 @@ type Oauth2 struct {
|
||||
RefreshToken string `protobuf:"bytes,1,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
|
||||
ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
||||
ClientSecret string `protobuf:"bytes,3,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"`
|
||||
AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Oauth2) Reset() {
|
||||
@@ -442,13 +441,6 @@ func (x *Oauth2) GetClientSecret() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Oauth2) GetAccessToken() string {
|
||||
if x != nil {
|
||||
return x.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type KeySecret struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -504,69 +496,6 @@ func (x *KeySecret) GetSecret() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type AWSSessionTokenSecret struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
|
||||
SessionToken string `protobuf:"bytes,3,opt,name=sessionToken,proto3" json:"sessionToken,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AWSSessionTokenSecret) Reset() {
|
||||
*x = AWSSessionTokenSecret{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AWSSessionTokenSecret) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AWSSessionTokenSecret) ProtoMessage() {}
|
||||
|
||||
func (x *AWSSessionTokenSecret) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AWSSessionTokenSecret.ProtoReflect.Descriptor instead.
|
||||
func (*AWSSessionTokenSecret) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *AWSSessionTokenSecret) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AWSSessionTokenSecret) GetSecret() string {
|
||||
if x != nil {
|
||||
return x.Secret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AWSSessionTokenSecret) GetSessionToken() string {
|
||||
if x != nil {
|
||||
return x.SessionToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AWS struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -580,7 +509,7 @@ type AWS struct {
|
||||
func (x *AWS) Reset() {
|
||||
*x = AWS{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[10]
|
||||
mi := &file_credentials_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -593,7 +522,7 @@ func (x *AWS) String() string {
|
||||
func (*AWS) ProtoMessage() {}
|
||||
|
||||
func (x *AWS) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[10]
|
||||
mi := &file_credentials_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -606,7 +535,7 @@ func (x *AWS) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AWS.ProtoReflect.Descriptor instead.
|
||||
func (*AWS) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{10}
|
||||
return file_credentials_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *AWS) GetKey() string {
|
||||
@@ -643,7 +572,7 @@ type SES struct {
|
||||
func (x *SES) Reset() {
|
||||
*x = SES{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[11]
|
||||
mi := &file_credentials_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -656,7 +585,7 @@ func (x *SES) String() string {
|
||||
func (*SES) ProtoMessage() {}
|
||||
|
||||
func (x *SES) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[11]
|
||||
mi := &file_credentials_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -669,7 +598,7 @@ func (x *SES) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SES.ProtoReflect.Descriptor instead.
|
||||
func (*SES) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{11}
|
||||
return file_credentials_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *SES) GetCreds() *AWS {
|
||||
@@ -706,7 +635,7 @@ type GitHubApp struct {
|
||||
func (x *GitHubApp) Reset() {
|
||||
*x = GitHubApp{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[12]
|
||||
mi := &file_credentials_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -719,7 +648,7 @@ func (x *GitHubApp) String() string {
|
||||
func (*GitHubApp) ProtoMessage() {}
|
||||
|
||||
func (x *GitHubApp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[12]
|
||||
mi := &file_credentials_proto_msgTypes[11]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -732,7 +661,7 @@ func (x *GitHubApp) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use GitHubApp.ProtoReflect.Descriptor instead.
|
||||
func (*GitHubApp) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{12}
|
||||
return file_credentials_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *GitHubApp) GetPrivateKey() string {
|
||||
@@ -769,7 +698,7 @@ type SlackTokens struct {
|
||||
func (x *SlackTokens) Reset() {
|
||||
*x = SlackTokens{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_credentials_proto_msgTypes[13]
|
||||
mi := &file_credentials_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -782,7 +711,7 @@ func (x *SlackTokens) String() string {
|
||||
func (*SlackTokens) ProtoMessage() {}
|
||||
|
||||
func (x *SlackTokens) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_credentials_proto_msgTypes[13]
|
||||
mi := &file_credentials_proto_msgTypes[12]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -795,7 +724,7 @@ func (x *SlackTokens) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SlackTokens.ProtoReflect.Descriptor instead.
|
||||
func (*SlackTokens) Descriptor() ([]byte, []int) {
|
||||
return file_credentials_proto_rawDescGZIP(), []int{13}
|
||||
return file_credentials_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *SlackTokens) GetAppToken() string {
|
||||
@@ -852,56 +781,47 @@ var file_credentials_proto_rawDesc = []byte{
|
||||
0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x65, 0x72, 0x74,
|
||||
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x92, 0x01, 0x0a, 0x06,
|
||||
0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73,
|
||||
0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72,
|
||||
0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x21, 0x0a,
|
||||
0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
|
||||
0x22, 0x35, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x65, 0x0a, 0x15, 0x41, 0x57, 0x53, 0x53, 0x65,
|
||||
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x65,
|
||||
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x47,
|
||||
0x0a, 0x03, 0x41, 0x57, 0x53, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22, 0x65, 0x0a, 0x03, 0x53, 0x45, 0x53, 0x12, 0x26,
|
||||
0x0a, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e,
|
||||
0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x41, 0x57, 0x53, 0x52,
|
||||
0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x1e,
|
||||
0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x6c,
|
||||
0x0a, 0x09, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x41, 0x70, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x70,
|
||||
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x0f,
|
||||
0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x70, 0x70, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x22, 0x6a, 0x0a, 0x0b,
|
||||
0x53, 0x6c, 0x61, 0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61,
|
||||
0x70, 0x70, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x61, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6f, 0x74, 0x5f,
|
||||
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x6f, 0x74,
|
||||
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x73, 0x65,
|
||||
0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x68, 0x6f,
|
||||
0x67, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x72, 0x65, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x6f, 0x0a, 0x06, 0x4f,
|
||||
0x61, 0x75, 0x74, 0x68, 0x32, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
|
||||
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65,
|
||||
0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
|
||||
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x35, 0x0a, 0x09,
|
||||
0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73,
|
||||
0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63,
|
||||
0x72, 0x65, 0x74, 0x22, 0x47, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65,
|
||||
0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22, 0x65, 0x0a, 0x03,
|
||||
0x53, 0x45, 0x53, 0x12, 0x26, 0x0a, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
|
||||
0x2e, 0x41, 0x57, 0x53, 0x52, 0x05, 0x63, 0x72, 0x65, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73,
|
||||
0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x73, 0x22, 0x6c, 0x0a, 0x09, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x41, 0x70, 0x70,
|
||||
0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65,
|
||||
0x79, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74,
|
||||
0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x70,
|
||||
0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49,
|
||||
0x64, 0x22, 0x6a, 0x0a, 0x0b, 0x53, 0x6c, 0x61, 0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73,
|
||||
0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x70, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x62, 0x6f, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x62, 0x6f, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x3f, 0x5a,
|
||||
0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x75, 0x66,
|
||||
0x66, 0x6c, 0x65, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x75, 0x66,
|
||||
0x66, 0x6c, 0x65, 0x68, 0x6f, 0x67, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62,
|
||||
0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x70, 0x62, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -916,30 +836,29 @@ func file_credentials_proto_rawDescGZIP() []byte {
|
||||
return file_credentials_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_credentials_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_credentials_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_credentials_proto_goTypes = []interface{}{
|
||||
(*Unauthenticated)(nil), // 0: credentials.Unauthenticated
|
||||
(*SSHAuth)(nil), // 1: credentials.SSHAuth
|
||||
(*CloudEnvironment)(nil), // 2: credentials.CloudEnvironment
|
||||
(*BasicAuth)(nil), // 3: credentials.BasicAuth
|
||||
(*Header)(nil), // 4: credentials.Header
|
||||
(*ClientCredentials)(nil), // 5: credentials.ClientCredentials
|
||||
(*ClientCertificate)(nil), // 6: credentials.ClientCertificate
|
||||
(*Oauth2)(nil), // 7: credentials.Oauth2
|
||||
(*KeySecret)(nil), // 8: credentials.KeySecret
|
||||
(*AWSSessionTokenSecret)(nil), // 9: credentials.AWSSessionTokenSecret
|
||||
(*AWS)(nil), // 10: credentials.AWS
|
||||
(*SES)(nil), // 11: credentials.SES
|
||||
(*GitHubApp)(nil), // 12: credentials.GitHubApp
|
||||
(*SlackTokens)(nil), // 13: credentials.SlackTokens
|
||||
(*Unauthenticated)(nil), // 0: credentials.Unauthenticated
|
||||
(*SSHAuth)(nil), // 1: credentials.SSHAuth
|
||||
(*CloudEnvironment)(nil), // 2: credentials.CloudEnvironment
|
||||
(*BasicAuth)(nil), // 3: credentials.BasicAuth
|
||||
(*Header)(nil), // 4: credentials.Header
|
||||
(*ClientCredentials)(nil), // 5: credentials.ClientCredentials
|
||||
(*ClientCertificate)(nil), // 6: credentials.ClientCertificate
|
||||
(*Oauth2)(nil), // 7: credentials.Oauth2
|
||||
(*KeySecret)(nil), // 8: credentials.KeySecret
|
||||
(*AWS)(nil), // 9: credentials.AWS
|
||||
(*SES)(nil), // 10: credentials.SES
|
||||
(*GitHubApp)(nil), // 11: credentials.GitHubApp
|
||||
(*SlackTokens)(nil), // 12: credentials.SlackTokens
|
||||
}
|
||||
var file_credentials_proto_depIdxs = []int32{
|
||||
10, // 0: credentials.SES.creds:type_name -> credentials.AWS
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
9, // 0: credentials.SES.creds:type_name -> credentials.AWS
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_credentials_proto_init() }
|
||||
@@ -1057,18 +976,6 @@ func file_credentials_proto_init() {
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AWSSessionTokenSecret); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AWS); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1080,7 +987,7 @@ func file_credentials_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_credentials_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SES); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1092,7 +999,7 @@ func file_credentials_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_credentials_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GitHubApp); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1104,7 +1011,7 @@ func file_credentials_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_credentials_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_credentials_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SlackTokens); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -1123,7 +1030,7 @@ func file_credentials_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_credentials_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 14,
|
||||
NumMessages: 13,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -783,8 +783,6 @@ func (m *Oauth2) validate(all bool) error {
|
||||
|
||||
// no validation rules for ClientSecret
|
||||
|
||||
// no validation rules for AccessToken
|
||||
|
||||
if len(errors) > 0 {
|
||||
return Oauth2MultiError(errors)
|
||||
}
|
||||
@@ -965,114 +963,6 @@ var _ interface {
|
||||
ErrorName() string
|
||||
} = KeySecretValidationError{}
|
||||
|
||||
// Validate checks the field values on AWSSessionTokenSecret with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the first error encountered is returned, or nil if there are no violations.
|
||||
func (m *AWSSessionTokenSecret) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on AWSSessionTokenSecret with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the result is a list of violation errors wrapped in
|
||||
// AWSSessionTokenSecretMultiError, or nil if none found.
|
||||
func (m *AWSSessionTokenSecret) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *AWSSessionTokenSecret) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for Key
|
||||
|
||||
// no validation rules for Secret
|
||||
|
||||
// no validation rules for SessionToken
|
||||
|
||||
if len(errors) > 0 {
|
||||
return AWSSessionTokenSecretMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AWSSessionTokenSecretMultiError is an error wrapping multiple validation
|
||||
// errors returned by AWSSessionTokenSecret.ValidateAll() if the designated
|
||||
// constraints aren't met.
|
||||
type AWSSessionTokenSecretMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AWSSessionTokenSecretMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m AWSSessionTokenSecretMultiError) AllErrors() []error { return m }
|
||||
|
||||
// AWSSessionTokenSecretValidationError is the validation error returned by
|
||||
// AWSSessionTokenSecret.Validate if the designated constraints aren't met.
|
||||
type AWSSessionTokenSecretValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e AWSSessionTokenSecretValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e AWSSessionTokenSecretValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e AWSSessionTokenSecretValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e AWSSessionTokenSecretValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e AWSSessionTokenSecretValidationError) ErrorName() string {
|
||||
return "AWSSessionTokenSecretValidationError"
|
||||
}
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e AWSSessionTokenSecretValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sAWSSessionTokenSecret.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = AWSSessionTokenSecretValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = AWSSessionTokenSecretValidationError{}
|
||||
|
||||
// Validate checks the field values on AWS with the rules defined in the proto
|
||||
// definition for this message. If any rules are violated, the first error
|
||||
// encountered is returned, or nil if there are no violations.
|
||||
|
||||
+911
-943
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1267,6 +1267,117 @@ var _ interface {
|
||||
ErrorName() string
|
||||
} = GitlabValidationError{}
|
||||
|
||||
// Validate checks the field values on GoogleDrive with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *GoogleDrive) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on GoogleDrive with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in GoogleDriveMultiError, or
|
||||
// nil if none found.
|
||||
func (m *GoogleDrive) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *GoogleDrive) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for File
|
||||
|
||||
// no validation rules for Link
|
||||
|
||||
// no validation rules for Email
|
||||
|
||||
// no validation rules for Timestamp
|
||||
|
||||
// no validation rules for Shared
|
||||
|
||||
// no validation rules for LastModifiedBy
|
||||
|
||||
if len(errors) > 0 {
|
||||
return GoogleDriveMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoogleDriveMultiError is an error wrapping multiple validation errors
|
||||
// returned by GoogleDrive.ValidateAll() if the designated constraints aren't met.
|
||||
type GoogleDriveMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m GoogleDriveMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m GoogleDriveMultiError) AllErrors() []error { return m }
|
||||
|
||||
// GoogleDriveValidationError is the validation error returned by
|
||||
// GoogleDrive.Validate if the designated constraints aren't met.
|
||||
type GoogleDriveValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e GoogleDriveValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e GoogleDriveValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e GoogleDriveValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e GoogleDriveValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e GoogleDriveValidationError) ErrorName() string { return "GoogleDriveValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e GoogleDriveValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sGoogleDrive.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = GoogleDriveValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = GoogleDriveValidationError{}
|
||||
|
||||
// Validate checks the field values on GCS with the rules defined in the proto
|
||||
// definition for this message. If any rules are violated, the first error
|
||||
// encountered is returned, or nil if there are no violations.
|
||||
@@ -1290,18 +1401,12 @@ func (m *GCS) validate(all bool) error {
|
||||
|
||||
// no validation rules for Bucket
|
||||
|
||||
// no validation rules for Filename
|
||||
// no validation rules for File
|
||||
|
||||
// no validation rules for Link
|
||||
|
||||
// no validation rules for Email
|
||||
|
||||
// no validation rules for CreatedAt
|
||||
|
||||
// no validation rules for UpdatedAt
|
||||
|
||||
// no validation rules for ContentType
|
||||
|
||||
if len(errors) > 0 {
|
||||
return GCSMultiError(errors)
|
||||
}
|
||||
@@ -2282,10 +2387,6 @@ func (m *Teams) validate(all bool) error {
|
||||
|
||||
// no validation rules for Location
|
||||
|
||||
// no validation rules for TeamName
|
||||
|
||||
// no validation rules for TeamId
|
||||
|
||||
if len(errors) > 0 {
|
||||
return TeamsMultiError(errors)
|
||||
}
|
||||
@@ -2721,230 +2822,6 @@ var _ interface {
|
||||
ErrorName() string
|
||||
} = PublicEventMonitoringValidationError{}
|
||||
|
||||
// Validate checks the field values on SharePoint with the rules defined in the
|
||||
// proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *SharePoint) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on SharePoint with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in SharePointMultiError, or
|
||||
// nil if none found.
|
||||
func (m *SharePoint) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *SharePoint) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for Link
|
||||
|
||||
// no validation rules for Timestamp
|
||||
|
||||
// no validation rules for Author
|
||||
|
||||
// no validation rules for Title
|
||||
|
||||
// no validation rules for Views
|
||||
|
||||
// no validation rules for Docid
|
||||
|
||||
// no validation rules for Email
|
||||
|
||||
if len(errors) > 0 {
|
||||
return SharePointMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SharePointMultiError is an error wrapping multiple validation errors
|
||||
// returned by SharePoint.ValidateAll() if the designated constraints aren't met.
|
||||
type SharePointMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m SharePointMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m SharePointMultiError) AllErrors() []error { return m }
|
||||
|
||||
// SharePointValidationError is the validation error returned by
|
||||
// SharePoint.Validate if the designated constraints aren't met.
|
||||
type SharePointValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e SharePointValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e SharePointValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e SharePointValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e SharePointValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e SharePointValidationError) ErrorName() string { return "SharePointValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e SharePointValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sSharePoint.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = SharePointValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = SharePointValidationError{}
|
||||
|
||||
// Validate checks the field values on GoogleDrive with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *GoogleDrive) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on GoogleDrive with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in GoogleDriveMultiError, or
|
||||
// nil if none found.
|
||||
func (m *GoogleDrive) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *GoogleDrive) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for File
|
||||
|
||||
// no validation rules for Link
|
||||
|
||||
// no validation rules for Email
|
||||
|
||||
// no validation rules for Timestamp
|
||||
|
||||
// no validation rules for Shared
|
||||
|
||||
// no validation rules for LastModifiedBy
|
||||
|
||||
if len(errors) > 0 {
|
||||
return GoogleDriveMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoogleDriveMultiError is an error wrapping multiple validation errors
|
||||
// returned by GoogleDrive.ValidateAll() if the designated constraints aren't met.
|
||||
type GoogleDriveMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m GoogleDriveMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m GoogleDriveMultiError) AllErrors() []error { return m }
|
||||
|
||||
// GoogleDriveValidationError is the validation error returned by
|
||||
// GoogleDrive.Validate if the designated constraints aren't met.
|
||||
type GoogleDriveValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e GoogleDriveValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e GoogleDriveValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e GoogleDriveValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e GoogleDriveValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e GoogleDriveValidationError) ErrorName() string { return "GoogleDriveValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e GoogleDriveValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sGoogleDrive.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = GoogleDriveValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = GoogleDriveValidationError{}
|
||||
|
||||
// Validate checks the field values on MetaData with the rules defined in the
|
||||
// proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
@@ -3713,68 +3590,6 @@ func (m *MetaData) validate(all bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
case *MetaData_Sharepoint:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetSharepoint()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, MetaDataValidationError{
|
||||
field: "Sharepoint",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, MetaDataValidationError{
|
||||
field: "Sharepoint",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetSharepoint()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return MetaDataValidationError{
|
||||
field: "Sharepoint",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *MetaData_GoogleDrive:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetGoogleDrive()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, MetaDataValidationError{
|
||||
field: "GoogleDrive",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, MetaDataValidationError{
|
||||
field: "GoogleDrive",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetGoogleDrive()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return MetaDataValidationError{
|
||||
field: "GoogleDrive",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
|
||||
+361
-656
File diff suppressed because it is too large
Load Diff
@@ -1242,113 +1242,10 @@ func (m *GCS) validate(all bool) error {
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for ProjectId
|
||||
|
||||
// no validation rules for MaxObjectSize
|
||||
|
||||
switch m.Credential.(type) {
|
||||
|
||||
case *GCS_JsonServiceAccount:
|
||||
// no validation rules for JsonServiceAccount
|
||||
|
||||
case *GCS_ApiKey:
|
||||
// no validation rules for ApiKey
|
||||
|
||||
case *GCS_Unauthenticated:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetUnauthenticated()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GCSValidationError{
|
||||
field: "Unauthenticated",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GCSValidationError{
|
||||
field: "Unauthenticated",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetUnauthenticated()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GCSValidationError{
|
||||
field: "Unauthenticated",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *GCS_Adc:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetAdc()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GCSValidationError{
|
||||
field: "Adc",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GCSValidationError{
|
||||
field: "Adc",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetAdc()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GCSValidationError{
|
||||
field: "Adc",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *GCS_ServiceAccountFile:
|
||||
// no validation rules for ServiceAccountFile
|
||||
|
||||
case *GCS_Oauth:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetOauth()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GCSValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GCSValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetOauth()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GCSValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
case *GCS_JsonSa:
|
||||
// no validation rules for JsonSa
|
||||
|
||||
}
|
||||
|
||||
@@ -2696,37 +2593,6 @@ func (m *S3) validate(all bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
case *S3_SessionToken:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetSessionToken()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, S3ValidationError{
|
||||
field: "SessionToken",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, S3ValidationError{
|
||||
field: "SessionToken",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetSessionToken()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return S3ValidationError{
|
||||
field: "SessionToken",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
@@ -3543,6 +3409,8 @@ func (m *Teams) validate(all bool) error {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
// no validation rules for TeamId
|
||||
|
||||
switch m.Credential.(type) {
|
||||
|
||||
case *Teams_Token:
|
||||
@@ -3579,37 +3447,6 @@ func (m *Teams) validate(all bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
case *Teams_Oauth:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetOauth()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, TeamsValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, TeamsValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetOauth()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return TeamsValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
@@ -4250,139 +4087,3 @@ var _ interface {
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = SlackRealtimeValidationError{}
|
||||
|
||||
// Validate checks the field values on Sharepoint with the rules defined in the
|
||||
// proto definition for this message. If any rules are violated, the first
|
||||
// error encountered is returned, or nil if there are no violations.
|
||||
func (m *Sharepoint) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on Sharepoint with the rules defined in
|
||||
// the proto definition for this message. If any rules are violated, the
|
||||
// result is a list of violation errors wrapped in SharepointMultiError, or
|
||||
// nil if none found.
|
||||
func (m *Sharepoint) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *Sharepoint) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for SiteUrl
|
||||
|
||||
switch m.Credential.(type) {
|
||||
|
||||
case *Sharepoint_Oauth:
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetOauth()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, SharepointValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, SharepointValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetOauth()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return SharepointValidationError{
|
||||
field: "Oauth",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return SharepointMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SharepointMultiError is an error wrapping multiple validation errors
|
||||
// returned by Sharepoint.ValidateAll() if the designated constraints aren't met.
|
||||
type SharepointMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m SharepointMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m SharepointMultiError) AllErrors() []error { return m }
|
||||
|
||||
// SharepointValidationError is the validation error returned by
|
||||
// Sharepoint.Validate if the designated constraints aren't met.
|
||||
type SharepointValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e SharepointValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e SharepointValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e SharepointValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e SharepointValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e SharepointValidationError) ErrorName() string { return "SharepointValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e SharepointValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sSharepoint.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = SharepointValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = SharepointValidationError{}
|
||||
|
||||
@@ -50,10 +50,12 @@ func TestSource_Scan(t *testing.T) {
|
||||
wantSourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Circleci{
|
||||
Circleci: &source_metadatapb.CircleCI{
|
||||
VcsType: "github",
|
||||
Username: "dustin-decker",
|
||||
Repository: "circle-ci",
|
||||
BuildStep: "Spin up environment",
|
||||
VcsType: "github",
|
||||
Username: "dustin-decker",
|
||||
Repository: "circle-ci",
|
||||
BuildNumber: 2,
|
||||
BuildStep: "Spin up environment",
|
||||
Link: "https://app.circleci.com/pipelines/github/dustin-decker/circle-ci/2",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -83,8 +85,6 @@ func TestSource_Scan(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
gotChunk := <-chunksCh
|
||||
gotChunk.SourceMetadata.Data.(*source_metadatapb.MetaData_Circleci).Circleci.BuildNumber = 0 // override this because we need to periodically re-run the builds
|
||||
gotChunk.SourceMetadata.Data.(*source_metadatapb.MetaData_Circleci).Circleci.Link = "" // override this because we need to periodically re-run the builds
|
||||
if diff := pretty.Compare(gotChunk.SourceMetadata, tt.wantSourceMetadata); diff != "" {
|
||||
t.Errorf("Source.Chunks() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
|
||||
@@ -1,382 +0,0 @@
|
||||
package gcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
diskbufferreader "github.com/bill-rich/disk-buffer-reader"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/go-logr/logr"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/endpoints"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/cache"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/cache/memory"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/handlers"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
const defaultCachePersistIncrement = 2500
|
||||
|
||||
// Ensure the Source satisfies the interface at compile time.
|
||||
var _ sources.Source = (*Source)(nil)
|
||||
|
||||
// Type returns the type of source.
|
||||
// It is used for matching source types in configuration and job input.
|
||||
func (s *Source) Type() sourcespb.SourceType {
|
||||
return sourcespb.SourceType_SOURCE_TYPE_GCS
|
||||
}
|
||||
|
||||
// SourceID number for GCS Source.
|
||||
func (s *Source) SourceID() int64 {
|
||||
return s.sourceId
|
||||
}
|
||||
|
||||
// JobID number for GCS Source.
|
||||
func (s *Source) JobID() int64 {
|
||||
return s.jobId
|
||||
}
|
||||
|
||||
// Source represents a GCS source.
|
||||
type Source struct {
|
||||
name string
|
||||
jobId int64
|
||||
sourceId int64
|
||||
concurrency int
|
||||
verify bool
|
||||
|
||||
gcsManager objectManager
|
||||
stats *attributes
|
||||
log logr.Logger
|
||||
chunksCh chan *sources.Chunk
|
||||
|
||||
mu sync.Mutex
|
||||
sources.Progress // progress is not thread safe
|
||||
}
|
||||
|
||||
// persistableCache is a wrapper around cache.Cache that allows
|
||||
// for the persistence of the cache contents in the Progress of the source
|
||||
// at given increments.
|
||||
type persistableCache struct {
|
||||
persistIncrement int
|
||||
cache.Cache
|
||||
*sources.Progress
|
||||
}
|
||||
|
||||
func newPersistableCache(increment int, cache cache.Cache, p *sources.Progress) *persistableCache {
|
||||
return &persistableCache{
|
||||
persistIncrement: increment,
|
||||
Cache: cache,
|
||||
Progress: p,
|
||||
}
|
||||
}
|
||||
|
||||
// Set overrides the cache Set method of the cache to enable the persistence
|
||||
// of the cache contents the Progress of the source at given increments.
|
||||
func (c *persistableCache) Set(key, val string) {
|
||||
c.Cache.Set(key, val)
|
||||
if ok, contents := c.shouldPersist(); ok {
|
||||
c.Progress.EncodedResumeInfo = contents
|
||||
}
|
||||
}
|
||||
|
||||
func (c *persistableCache) shouldPersist() (bool, string) {
|
||||
if c.Count()%c.persistIncrement != 0 {
|
||||
return false, ""
|
||||
}
|
||||
return true, c.Contents()
|
||||
}
|
||||
|
||||
// Init returns an initialized GCS source.
|
||||
func (s *Source) Init(aCtx context.Context, name string, id int64, sourceID int64, verify bool, connection *anypb.Any, concurrency int) error {
|
||||
s.log = aCtx.Logger()
|
||||
|
||||
s.name = name
|
||||
s.verify = verify
|
||||
s.sourceId = sourceID
|
||||
s.jobId = id
|
||||
s.concurrency = concurrency
|
||||
|
||||
var conn sourcespb.GCS
|
||||
err := anypb.UnmarshalTo(connection, &conn, proto.UnmarshalOptions{})
|
||||
if err != nil {
|
||||
return errors.WrapPrefix(err, "error unmarshalling connection", 0)
|
||||
}
|
||||
|
||||
gcsManager, err := configureGCSManager(aCtx, &conn, concurrency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.gcsManager = gcsManager
|
||||
|
||||
s.log.V(2).Info("enumerating buckets and objects")
|
||||
if err := s.enumerate(aCtx); err != nil {
|
||||
return fmt.Errorf("error enumerating buckets and objects: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureGCSManager(aCtx context.Context, conn *sourcespb.GCS, concurrency int) (*gcsManager, error) {
|
||||
if conn == nil {
|
||||
return nil, fmt.Errorf("GCS connection is nil, cannot configure GCS manager")
|
||||
}
|
||||
|
||||
var gcsManagerAuthOption gcsManagerOption
|
||||
|
||||
switch conn.Credential.(type) {
|
||||
case *sourcespb.GCS_ApiKey:
|
||||
gcsManagerAuthOption = withAPIKey(aCtx, conn.GetApiKey())
|
||||
case *sourcespb.GCS_ServiceAccountFile:
|
||||
b, err := os.ReadFile(conn.GetServiceAccountFile())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading GCS JSON Service Account file: %w", err)
|
||||
}
|
||||
gcsManagerAuthOption = withJSONServiceAccount(aCtx, b)
|
||||
case *sourcespb.GCS_JsonServiceAccount:
|
||||
gcsManagerAuthOption = withJSONServiceAccount(aCtx, []byte(conn.GetJsonServiceAccount()))
|
||||
case *sourcespb.GCS_Adc:
|
||||
gcsManagerAuthOption = withDefaultADC(aCtx)
|
||||
case *sourcespb.GCS_Unauthenticated:
|
||||
gcsManagerAuthOption = withoutAuthentication()
|
||||
case *sourcespb.GCS_Oauth:
|
||||
client, err := oauth2Client(aCtx, conn.GetOauth())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating oauth2 client: %w", err)
|
||||
}
|
||||
gcsManagerAuthOption = withHTTPClient(aCtx, client)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown GCS authentication type: %T", conn.Credential)
|
||||
|
||||
}
|
||||
|
||||
gcsManagerOpts := []gcsManagerOption{
|
||||
withConcurrency(concurrency),
|
||||
withMaxObjectSize(conn.MaxObjectSize),
|
||||
gcsManagerAuthOption,
|
||||
}
|
||||
if setGCSManagerBucketOptions(conn) != nil {
|
||||
gcsManagerOpts = append(gcsManagerOpts, setGCSManagerBucketOptions(conn))
|
||||
}
|
||||
if setGCSManagerObjectOptions(conn) != nil {
|
||||
gcsManagerOpts = append(gcsManagerOpts, setGCSManagerObjectOptions(conn))
|
||||
}
|
||||
|
||||
gcsManager, err := newGCSManager(conn.ProjectId, gcsManagerOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating GCS manager: %w", err)
|
||||
}
|
||||
|
||||
return gcsManager, nil
|
||||
}
|
||||
|
||||
func oauth2Client(ctx context.Context, creds *credentialspb.Oauth2) (*http.Client, error) {
|
||||
if creds == nil {
|
||||
return nil, fmt.Errorf("oauth2 credentials are required")
|
||||
}
|
||||
if creds.GetClientId() == "" || creds.GetRefreshToken() == "" || creds.GetAccessToken() == "" {
|
||||
return nil, fmt.Errorf("oauth2 credentials are incomplete, client_id, refresh_token, and access_token are required")
|
||||
}
|
||||
|
||||
conf := &oauth2.Config{
|
||||
ClientID: creds.GetClientId(),
|
||||
Scopes: []string{storage.ScopeReadOnly},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: endpoints.Google.AuthURL,
|
||||
TokenURL: endpoints.Google.TokenURL,
|
||||
},
|
||||
}
|
||||
|
||||
tok := &oauth2.Token{
|
||||
AccessToken: creds.GetAccessToken(),
|
||||
RefreshToken: creds.GetRefreshToken(),
|
||||
}
|
||||
|
||||
return conf.Client(ctx, tok), nil
|
||||
}
|
||||
|
||||
func setGCSManagerBucketOptions(conn *sourcespb.GCS) gcsManagerOption {
|
||||
return setGCSManagerOptions(conn.GetIncludeBuckets(), conn.GetExcludeBuckets(), withIncludeBuckets, withExcludeBuckets)
|
||||
}
|
||||
|
||||
func setGCSManagerObjectOptions(conn *sourcespb.GCS) gcsManagerOption {
|
||||
return setGCSManagerOptions(conn.GetIncludeObjects(), conn.GetExcludeObjects(), withIncludeObjects, withExcludeObjects)
|
||||
}
|
||||
|
||||
func setGCSManagerOptions(include, exclude []string, includeFn, excludeFn func([]string) gcsManagerOption) gcsManagerOption {
|
||||
// Only allow one of include/exclude to be set.
|
||||
// If both are set, include takes precedence.
|
||||
if len(include) > 0 {
|
||||
return includeFn(include)
|
||||
}
|
||||
if len(exclude) > 0 {
|
||||
return excludeFn(exclude)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// enumerate all the objects and buckets in the source.
|
||||
// This will be used to calculate progress.
|
||||
func (s *Source) enumerate(ctx context.Context) error {
|
||||
stats, err := s.gcsManager.attributes(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting attributes during enumeration: %w", err)
|
||||
}
|
||||
s.stats = stats
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunks emits chunks of bytes over a channel.
|
||||
func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk) error {
|
||||
persistableCache := s.setupCache(ctx)
|
||||
|
||||
objectCh, err := s.gcsManager.listObjects(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing objects: %w", err)
|
||||
}
|
||||
s.chunksCh = chunksChan
|
||||
s.Progress.Message = "starting to process objects..."
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for obj := range objectCh {
|
||||
obj := obj
|
||||
o, ok := obj.(object)
|
||||
if !ok {
|
||||
ctx.Logger().Error(fmt.Errorf("unexpected object type: %T", obj), "GCS source unexpected object type", "name", s.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if persistableCache.Exists(o.name) {
|
||||
ctx.Logger().V(5).Info("skipping object, object already processed", "name", o.name)
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(obj object) {
|
||||
defer wg.Done()
|
||||
|
||||
if err := s.processObject(ctx, o); err != nil {
|
||||
ctx.Logger().V(1).Info("error setting start progress progress", "name", o.name, "error", err)
|
||||
return
|
||||
}
|
||||
s.setProgress(ctx, o.md5, o.name, persistableCache)
|
||||
}(o)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
s.completeProgress(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) setupCache(ctx context.Context) *persistableCache {
|
||||
var c cache.Cache
|
||||
if s.Progress.EncodedResumeInfo != "" {
|
||||
c = memory.NewWithData(ctx, strings.Split(s.Progress.EncodedResumeInfo, ","))
|
||||
} else {
|
||||
c = memory.New()
|
||||
}
|
||||
|
||||
// TODO (ahrav): Make this configurable via conn.
|
||||
persistCache := newPersistableCache(defaultCachePersistIncrement, c, &s.Progress)
|
||||
return persistCache
|
||||
}
|
||||
|
||||
func (s *Source) setProgress(ctx context.Context, md5, objName string, cache cache.Cache) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
ctx.Logger().V(5).Info("setting progress for object", "object-name", objName)
|
||||
s.SectionsCompleted++
|
||||
|
||||
cache.Set(md5, md5)
|
||||
s.Progress.SectionsRemaining = int32(s.stats.numObjects)
|
||||
s.Progress.PercentComplete = int64(float64(s.SectionsCompleted) / float64(s.stats.numObjects) * 100)
|
||||
}
|
||||
|
||||
func (s *Source) completeProgress(ctx context.Context) {
|
||||
msg := fmt.Sprintf("GCS source finished processing %d objects", s.stats.numObjects)
|
||||
ctx.Logger().Info(msg)
|
||||
s.Progress.Message = msg
|
||||
}
|
||||
|
||||
func (s *Source) processObject(ctx context.Context, o object) error {
|
||||
chunkSkel := &sources.Chunk{
|
||||
SourceName: s.name,
|
||||
SourceType: s.Type(),
|
||||
SourceID: s.sourceId,
|
||||
Verify: s.verify,
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Gcs{
|
||||
Gcs: &source_metadatapb.GCS{
|
||||
Bucket: o.bucket,
|
||||
Filename: o.name,
|
||||
Link: o.link,
|
||||
Email: o.owner,
|
||||
ContentType: o.contentType,
|
||||
Acls: o.acl,
|
||||
CreatedAt: strconv.FormatInt(o.createdAt.Unix(), 10), // Unix time as string
|
||||
UpdatedAt: o.updatedAt.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
data, err := s.readObjectData(ctx, o, chunkSkel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading object data: %w", err)
|
||||
}
|
||||
|
||||
// If data is nil, it means that the file was handled by a handler.
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
chunkSkel.Data = data
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case s.chunksCh <- chunkSkel:
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) readObjectData(ctx context.Context, o object, chunk *sources.Chunk) ([]byte, error) {
|
||||
reader, err := diskbufferreader.New(o)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating disk buffer reader: %w", err)
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
if handlers.HandleFile(ctx, reader, chunk, s.chunksCh) {
|
||||
ctx.Logger().V(3).Info("File was handled", "name", s.name, "bucket", o.bucket, "object", o.name)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := reader.Reset(); err != nil {
|
||||
return nil, fmt.Errorf("error resetting reader: %w", err)
|
||||
}
|
||||
|
||||
reader.Stop()
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading object: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package gcs
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
func TestChunks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
source, conn := createTestSource(&sourcespb.GCS{
|
||||
ProjectId: testProjectID,
|
||||
Credential: &sourcespb.GCS_Adc{},
|
||||
ExcludeBuckets: []string{perfTestBucketGlob, publicBucket},
|
||||
})
|
||||
|
||||
err := source.Init(ctx, "test", 1, 1, true, conn, 8)
|
||||
assert.Nil(t, err)
|
||||
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
|
||||
go func() {
|
||||
defer close(chunksCh)
|
||||
err := source.Chunks(ctx, chunksCh)
|
||||
assert.Nil(t, err)
|
||||
}()
|
||||
|
||||
want := createTestChunks()
|
||||
|
||||
got := make([]*sources.Chunk, 0, len(want))
|
||||
for chunk := range chunksCh {
|
||||
got = append(got, chunk)
|
||||
}
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].SourceMetadata.GetGcs().Filename < got[j].SourceMetadata.GetGcs().Filename
|
||||
})
|
||||
|
||||
assert.Equal(t, len(want), len(got))
|
||||
|
||||
for i, chunk := range got {
|
||||
if diff := cmp.Diff(want[i].SourceMetadata.GetGcs(), chunk.SourceMetadata.GetGcs(),
|
||||
cmpopts.IgnoreFields(source_metadatapb.GCS{}, "state", "sizeCache", "unknownFields", "CreatedAt", "UpdatedAt"),
|
||||
); diff != "" {
|
||||
t.Errorf("chunk mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChunks_PublicBucket(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
source, conn := createTestSource(&sourcespb.GCS{
|
||||
Credential: &sourcespb.GCS_Unauthenticated{},
|
||||
IncludeBuckets: []string{publicBucket},
|
||||
})
|
||||
|
||||
err := source.Init(ctx, "test", 1, 1, true, conn, 8)
|
||||
assert.Nil(t, err)
|
||||
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
|
||||
go func() {
|
||||
defer close(chunksCh)
|
||||
err := source.Chunks(ctx, chunksCh)
|
||||
assert.Nil(t, err)
|
||||
}()
|
||||
|
||||
want := []*sources.Chunk{
|
||||
{
|
||||
SourceName: "test",
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GCS,
|
||||
SourceID: 0,
|
||||
Verify: true,
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Gcs{
|
||||
Gcs: &source_metadatapb.GCS{
|
||||
Filename: "aws1.txt",
|
||||
Bucket: publicBucket,
|
||||
ContentType: "text/plain",
|
||||
Email: "",
|
||||
Link: "https://storage.googleapis.com/download/storage/v1/b/public-trufflehog-test-bucket/o/aws1.txt?generation=1678334408999764&alt=media",
|
||||
Acls: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := make([]*sources.Chunk, 0, len(want))
|
||||
for chunk := range chunksCh {
|
||||
got = append(got, chunk)
|
||||
}
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].SourceMetadata.GetGcs().Filename < got[j].SourceMetadata.GetGcs().Filename
|
||||
})
|
||||
|
||||
assert.Equal(t, len(want), len(got))
|
||||
|
||||
for i, chunk := range got {
|
||||
if diff := cmp.Diff(want[i].SourceMetadata.GetGcs(), chunk.SourceMetadata.GetGcs(),
|
||||
cmpopts.IgnoreFields(source_metadatapb.GCS{}, "state", "sizeCache", "unknownFields", "CreatedAt", "UpdatedAt"),
|
||||
); diff != "" {
|
||||
t.Errorf("chunk mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createTestChunks() []*sources.Chunk {
|
||||
objects := []object{
|
||||
{
|
||||
name: "aws1.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/aws1.txt?generation=1677870994890594&alt=media",
|
||||
acl: []string{},
|
||||
},
|
||||
{
|
||||
name: "moar2.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 12,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/moar2.txt?generation=1677871000378542&alt=media",
|
||||
acl: []string{},
|
||||
},
|
||||
{
|
||||
name: "aws3.txt",
|
||||
bucket: testBucket2,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th2/o/aws3.txt?generation=1677871022489611&alt=media",
|
||||
acl: []string{},
|
||||
},
|
||||
{
|
||||
name: "moar.txt",
|
||||
bucket: testBucket3,
|
||||
contentType: "text/plain",
|
||||
size: 6,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th3/o/moar.txt?generation=1677871042896804&alt=media",
|
||||
acl: []string{},
|
||||
},
|
||||
{
|
||||
name: "AMAZON_FASHION_5.json",
|
||||
bucket: testBucket4,
|
||||
contentType: "application/json",
|
||||
size: 1413469,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th4/o/AMAZON_FASHION_5.json?generation=1677871063457469&alt=media",
|
||||
acl: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
chunks := make([]*sources.Chunk, 0, len(objects))
|
||||
for _, o := range objects {
|
||||
chunks = append(chunks, &sources.Chunk{
|
||||
SourceName: "test",
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GCS,
|
||||
SourceID: 0,
|
||||
Verify: true,
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Gcs{
|
||||
Gcs: &source_metadatapb.GCS{
|
||||
Filename: o.name,
|
||||
Bucket: o.bucket,
|
||||
ContentType: o.contentType,
|
||||
Email: o.owner,
|
||||
Link: o.link,
|
||||
Acls: o.acl,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
sort.Slice(chunks, func(i, j int) bool {
|
||||
return chunks[i].SourceMetadata.GetGcs().Filename < chunks[j].SourceMetadata.GetGcs().Filename
|
||||
})
|
||||
|
||||
return chunks
|
||||
}
|
||||
@@ -1,736 +0,0 @@
|
||||
package gcs
|
||||
|
||||
import (
|
||||
aCtx "context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/api/iterator"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMaxObjectSize = 10 * 1024 * 1024 // 10MB
|
||||
maxObjectSizeLimit = 50 * 1024 * 1024 // 50MB
|
||||
)
|
||||
|
||||
var (
|
||||
defaultConcurrency = runtime.NumCPU()
|
||||
)
|
||||
|
||||
type objectManager interface {
|
||||
listObjects(context.Context) (chan io.Reader, error)
|
||||
attributes(ctx context.Context) (*attributes, error)
|
||||
}
|
||||
|
||||
// bucketManager is a simplified *storage.Client wrapper.
|
||||
// It provides only a subset of methods that are needed by the gcsManager.
|
||||
type bucketManager interface {
|
||||
// Bucket returns a BucketHandle for the given bucket name.
|
||||
Bucket(name string) *storage.BucketHandle
|
||||
// Buckets returns an iterator over the buckets in the project.
|
||||
Buckets(ctx aCtx.Context, projectID string) *storage.BucketIterator
|
||||
}
|
||||
|
||||
// gcsManager serves as simple facade for interacting with GCS.
|
||||
// It's main purpose is to retrieve objects from GCS.
|
||||
type gcsManager struct {
|
||||
projectID string
|
||||
withoutAuth,
|
||||
hasEnumerated bool
|
||||
|
||||
concurrency int
|
||||
workerPool *errgroup.Group
|
||||
|
||||
maxObjectSize int64
|
||||
numBuckets uint32
|
||||
numObjects uint64
|
||||
|
||||
includeBuckets,
|
||||
excludeBuckets,
|
||||
includeObjects,
|
||||
excludeObjects map[string]struct{}
|
||||
|
||||
buckets map[string]bucket
|
||||
attr *attributes
|
||||
|
||||
client bucketManager
|
||||
}
|
||||
|
||||
// bucket is a simplified *storage.BucketHandle wrapper.
|
||||
// It also provides a mechanism for resuming an interrupted iteration.
|
||||
// This reduces the number of objects that need to be re-processed.
|
||||
type bucket struct {
|
||||
shouldInclude bool
|
||||
name string
|
||||
// startOffset is the name of the object to resume from. (inclusive)
|
||||
// This works because GCS objects are sorted lexicographically.
|
||||
// If this is empty, the iteration will start from the beginning.
|
||||
startOffset string
|
||||
*storage.BucketHandle
|
||||
}
|
||||
|
||||
// offsetInfo is used to resume an interrupted iteration.
|
||||
type offsetInfo struct {
|
||||
isBucketProcessed bool
|
||||
lastProcessedObject string
|
||||
}
|
||||
|
||||
// attributes contains metadata about the GCS source.
|
||||
// This will be collected during the initial scan.
|
||||
type attributes struct {
|
||||
numBuckets uint32
|
||||
numObjects uint64
|
||||
mu sync.RWMutex
|
||||
bucketObjects map[string]uint64
|
||||
}
|
||||
|
||||
func newStats(numBkts int) *attributes {
|
||||
return &attributes{
|
||||
numBuckets: uint32(numBkts),
|
||||
bucketObjects: make(map[string]uint64, numBkts),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *attributes) incObjects() {
|
||||
s.mu.Lock()
|
||||
s.numObjects++
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *attributes) setBucketCnt(bkt string, cnt uint64) {
|
||||
s.mu.Lock()
|
||||
s.bucketObjects[bkt] = cnt
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
type gcsManagerOption func(*gcsManager) error
|
||||
|
||||
// withHTTPClient uses the provided HTTP client when creating a new GCS client.
|
||||
func withHTTPClient(ctx context.Context, httpClient *http.Client) gcsManagerOption {
|
||||
client, err := storage.NewClient(ctx, option.WithHTTPClient(httpClient), option.WithScopes(storage.ScopeReadOnly))
|
||||
return func(m *gcsManager) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.client = client
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withAPIKey uses the provided API key when creating a new GCS client.
|
||||
// This can ONLY be used for public buckets.
|
||||
func withAPIKey(ctx context.Context, apiKey string) gcsManagerOption {
|
||||
client, err := storage.NewClient(ctx, option.WithAPIKey(apiKey), option.WithScopes(storage.ScopeReadOnly))
|
||||
return func(m *gcsManager) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.client = client
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withJSONServiceAccount uses the provided JSON service account when creating a new GCS client.
|
||||
func withJSONServiceAccount(ctx context.Context, jsonServiceAccount []byte) gcsManagerOption {
|
||||
client, err := storage.NewClient(ctx, option.WithCredentialsJSON(jsonServiceAccount), option.WithScopes(storage.ScopeReadOnly))
|
||||
return func(m *gcsManager) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.client = client
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withDefaultADC uses the default application credentials when creating a new GCS client.
|
||||
func withDefaultADC(ctx context.Context) gcsManagerOption {
|
||||
client, err := defaultADC(ctx)
|
||||
return func(m *gcsManager) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.client = client
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withoutAuthentication uses an unauthenticated client when creating a new GCS client.
|
||||
// This can ONLY be used for public buckets.
|
||||
func withoutAuthentication() gcsManagerOption {
|
||||
client, err := storage.NewClient(aCtx.Background(), option.WithoutAuthentication(), option.WithScopes(storage.ScopeReadOnly))
|
||||
return func(m *gcsManager) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.client = client
|
||||
m.withoutAuth = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func defaultADC(ctx context.Context) (*storage.Client, error) {
|
||||
client, err := storage.NewClient(ctx, option.WithScopes(storage.ScopeReadOnly))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// withIncludeBuckets sets the buckets that should be included in the scan.
|
||||
// If used in conjunction with withExcludeBuckets, the include buckets will
|
||||
// take precedence.
|
||||
func withIncludeBuckets(buckets []string) gcsManagerOption {
|
||||
return func(m *gcsManager) error {
|
||||
if len(buckets) == 0 {
|
||||
return nil
|
||||
}
|
||||
if m.excludeBuckets != nil {
|
||||
m.excludeBuckets = nil
|
||||
}
|
||||
|
||||
m.includeBuckets = make(map[string]struct{}, len(buckets))
|
||||
for _, bucket := range buckets {
|
||||
m.includeBuckets[bucket] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withExcludeBuckets sets the buckets that should be excluded from the scan.
|
||||
// If used in conjunction with withIncludeBuckets, the include buckets will
|
||||
// take precedence.
|
||||
func withExcludeBuckets(buckets []string) gcsManagerOption {
|
||||
return func(m *gcsManager) error {
|
||||
if len(buckets) == 0 {
|
||||
return nil
|
||||
}
|
||||
if m.includeBuckets != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.excludeBuckets = make(map[string]struct{}, len(buckets))
|
||||
for _, bucket := range buckets {
|
||||
m.excludeBuckets[bucket] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withIncludeObjects sets the objects that should be included in the scan.
|
||||
// Using this option in conjuection with withIncludeBuckets will result in
|
||||
// only specific buckets and objects being scanned.
|
||||
// If the bucket(s) are known, it is recommended to also use withIncludeBuckets
|
||||
// to increase the performance of the scan.
|
||||
// If used in conjunction with any of the withExclude* or withIncludeBuckets options, this
|
||||
// option will take precedence.
|
||||
func withIncludeObjects(objects []string) gcsManagerOption {
|
||||
return func(m *gcsManager) error {
|
||||
if len(objects) == 0 {
|
||||
return nil
|
||||
}
|
||||
if m.excludeObjects != nil {
|
||||
m.excludeObjects = nil
|
||||
}
|
||||
|
||||
m.includeObjects = make(map[string]struct{}, len(objects))
|
||||
for _, object := range objects {
|
||||
m.includeObjects[object] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withExcludeObjects sets the objects that should be excluded from the scan.
|
||||
// If used in conjunction with withIncludeObjects, the include objects will
|
||||
// take precedence.
|
||||
func withExcludeObjects(objects []string) gcsManagerOption {
|
||||
return func(m *gcsManager) error {
|
||||
if len(objects) == 0 {
|
||||
return nil
|
||||
}
|
||||
if m.includeObjects != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.excludeObjects = make(map[string]struct{}, len(objects))
|
||||
for _, object := range objects {
|
||||
m.excludeObjects[object] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withConcurrency sets the number of concurrent workers that will be used
|
||||
// to process objects.
|
||||
// If not set, or set to a negative number the default value is runtime.NumCPU().
|
||||
func withConcurrency(concurrency int) gcsManagerOption {
|
||||
return func(m *gcsManager) error {
|
||||
if concurrency <= 0 {
|
||||
m.concurrency = defaultConcurrency
|
||||
} else {
|
||||
m.concurrency = concurrency
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withMaxObjectSize sets the maximum size of objects that will be scanned.
|
||||
// If not set, set to a negative number, or set larger than 1GB,
|
||||
// the default value of 500MB will be used.
|
||||
func withMaxObjectSize(maxObjectSize int64) gcsManagerOption {
|
||||
return func(m *gcsManager) error {
|
||||
if maxObjectSize <= 0 || maxObjectSize > maxObjectSizeLimit {
|
||||
m.maxObjectSize = defaultMaxObjectSize
|
||||
} else {
|
||||
m.maxObjectSize = maxObjectSize
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withBucketOffsets sets the offset for each bucket.
|
||||
// This is used to resume listing objects for a bucket.
|
||||
func withBucketOffsets(offsets map[string]offsetInfo) gcsManagerOption {
|
||||
bkts := make(map[string]bucket, len(offsets))
|
||||
for bkt, offst := range offsets {
|
||||
bkts[bkt] = bucket{
|
||||
shouldInclude: !offst.isBucketProcessed,
|
||||
name: bkt,
|
||||
startOffset: offst.lastProcessedObject,
|
||||
}
|
||||
}
|
||||
return func(m *gcsManager) error {
|
||||
m.hasEnumerated = len(offsets) > 0 // offset means we are resuming.
|
||||
m.buckets = bkts
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func newGCSManager(projectID string, opts ...gcsManagerOption) (*gcsManager, error) {
|
||||
// Default values for the manager.
|
||||
gcs := &gcsManager{
|
||||
projectID: projectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
buckets: make(map[string]bucket),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(gcs); err != nil {
|
||||
return nil, fmt.Errorf("failed to apply option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if projectID == "" && !gcs.withoutAuth {
|
||||
return nil, fmt.Errorf("project ID is required, when using authentication")
|
||||
}
|
||||
|
||||
// If no client was provided, use the default application credentials.
|
||||
// A client is required to perform any operations.
|
||||
if gcs.client == nil {
|
||||
c, err := defaultADC(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcs.client = c
|
||||
}
|
||||
configureWorkers(gcs)
|
||||
|
||||
return gcs, nil
|
||||
}
|
||||
|
||||
func configureWorkers(gcs *gcsManager) {
|
||||
gcs.workerPool = new(errgroup.Group)
|
||||
gcs.workerPool.SetLimit(gcs.concurrency)
|
||||
}
|
||||
|
||||
// object is a representation of a GCS object.
|
||||
type object struct {
|
||||
name string
|
||||
bucket string
|
||||
contentType string
|
||||
owner string
|
||||
link string
|
||||
md5 string
|
||||
// acl represents an ACLEntities.
|
||||
// https://pkg.go.dev/cloud.google.com/go/storage#ACLEntity
|
||||
acl []string
|
||||
size int64
|
||||
createdAt time.Time
|
||||
updatedAt time.Time
|
||||
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (g *gcsManager) attributes(ctx context.Context) (*attributes, error) {
|
||||
// Get all the buckets in the project.
|
||||
buckets, err := g.listBuckets(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list buckets: %w", err)
|
||||
}
|
||||
|
||||
return g.enumerate(ctx, buckets)
|
||||
}
|
||||
|
||||
func (g *gcsManager) enumerate(ctx context.Context, bkts []bucket) (*attributes, error) {
|
||||
logger := ctx.Logger().WithValues("phase", "enumeration")
|
||||
|
||||
logger.V(5).Info("enumerating buckets", "numBuckets", len(bkts))
|
||||
stats := newStats(len(bkts))
|
||||
|
||||
defer func(start time.Time) {
|
||||
logger.V(5).Info("finished enumerating buckets", "duration-seconds", fmt.Sprintf("%.1f", time.Since(start).Seconds()), "num-buckets", len(bkts), "num-objects", stats.numObjects)
|
||||
}(time.Now())
|
||||
|
||||
for _, bkt := range bkts {
|
||||
bkt := bkt
|
||||
g.workerPool.Go(func() error {
|
||||
// List all the objects in the bucket and calculate a attributes.
|
||||
g.setupBktHandle(&bkt)
|
||||
|
||||
q, err := setObjectQuery(&bkt)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to set object query", "bucket", bkt.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
var count uint64
|
||||
objs := bkt.Objects(ctx, q)
|
||||
for {
|
||||
obj, err := objs.Next()
|
||||
if errors.Is(err, iterator.Done) {
|
||||
logger.V(5).Info("finished listing objects in bucket")
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
logger.V(1).Info("failed to list objects", "bucket", bkt.name, "error", err)
|
||||
return nil
|
||||
}
|
||||
if obj == nil {
|
||||
logger.V(5).Info("object is nil")
|
||||
continue
|
||||
}
|
||||
|
||||
if !g.shouldIncludeObject(ctx, obj.Name) || g.shouldExcludeObject(ctx, obj.Name) {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
stats.incObjects()
|
||||
}
|
||||
|
||||
stats.setBucketCnt(bkt.name, count)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = g.workerPool.Wait()
|
||||
g.attr = stats
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (g *gcsManager) listObjects(ctx context.Context) (chan io.Reader, error) {
|
||||
ch := make(chan io.Reader, 100) // TODO (ahrav): Determine optimal buffer size.
|
||||
|
||||
// Get all the buckets in the project.
|
||||
buckets, err := g.listBuckets(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list buckets: %w", err)
|
||||
}
|
||||
|
||||
// TODO (ahrav): This can be optimized if we set the buckets from within
|
||||
// the listBuckets method. This way seems a little more clear as to the intent.
|
||||
|
||||
// Update the gcsManager with all the buckets to list objects from.
|
||||
// This is in addition to any buckets that were provided via the
|
||||
// withBucketOffsets option.
|
||||
for _, b := range buckets {
|
||||
g.buckets[b.name] = b
|
||||
}
|
||||
|
||||
go func() {
|
||||
gcsErrs := sources.NewScanErrors()
|
||||
for _, bucket := range g.buckets {
|
||||
g.numBuckets++
|
||||
bucket := bucket
|
||||
g.workerPool.Go(func() error {
|
||||
objCh, errCh := g.listBucketObjects(ctx, &bucket)
|
||||
for {
|
||||
select {
|
||||
case obj, ok := <-objCh:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ch <- obj
|
||||
case err := <-errCh:
|
||||
gcsErrs.Add(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_ = g.workerPool.Wait()
|
||||
if gcsErrs.Count() > 0 {
|
||||
ctx.Logger().V(2).Info("encountered gcsErrs while scanning GCS buckets", "error-count", gcsErrs.Count(), "gcsErrs", gcsErrs.String())
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (g *gcsManager) listBuckets(ctx context.Context) ([]bucket, error) {
|
||||
var buckets []bucket
|
||||
if g.withoutAuth {
|
||||
for name := range g.includeBuckets {
|
||||
buckets = append(buckets, bucket{name: name})
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
|
||||
bkts := g.client.Buckets(ctx, g.projectID)
|
||||
for {
|
||||
bkt, err := bkts.Next()
|
||||
if errors.Is(err, iterator.Done) {
|
||||
ctx.Logger().V(5).Info("finished listing buckets", "num_buckets", len(buckets))
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve bucket: %w", err)
|
||||
}
|
||||
|
||||
// If the bucket is already in the map, skip it, it's already accounted for.
|
||||
// This is used to resume listing objects for a bucket.
|
||||
if _, ok := g.buckets[bkt.Name]; ok {
|
||||
continue
|
||||
}
|
||||
if !g.shouldIncludeBucket(ctx, bkt.Name) || g.shouldExcludeBucket(ctx, bkt.Name) {
|
||||
continue
|
||||
}
|
||||
buckets = append(buckets, bucket{name: bkt.Name})
|
||||
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
|
||||
func (g *gcsManager) listBucketObjects(ctx context.Context, bkt *bucket) (chan io.Reader, chan error) {
|
||||
ch := make(chan io.Reader, 100)
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer close(ch)
|
||||
|
||||
logger := ctx.Logger().WithValues("bucket", bkt.name)
|
||||
logger.V(5).Info("listing object(s) in bucket")
|
||||
|
||||
g.setupBktHandle(bkt)
|
||||
|
||||
// TODO (ahrav): Look to extend gcsManager to allow for exact buckets/objects
|
||||
// include filters. This will increase performance substantially
|
||||
|
||||
if err := g.bucketObjects(ctx, bkt, ch); err != nil {
|
||||
errCh <- fmt.Errorf("failed to list bucket objects: %w", err)
|
||||
}
|
||||
}()
|
||||
return ch, errCh
|
||||
}
|
||||
|
||||
func (g *gcsManager) setupBktHandle(bkt *bucket) {
|
||||
b := g.client.Bucket(bkt.name).Retryer(
|
||||
storage.WithBackoff(gax.Backoff{
|
||||
Initial: 2 * time.Second,
|
||||
Max: 30 * time.Second,
|
||||
Multiplier: 1.5,
|
||||
}),
|
||||
storage.WithPolicy(storage.RetryAlways),
|
||||
)
|
||||
bkt.BucketHandle = b
|
||||
}
|
||||
|
||||
func (g *gcsManager) bucketObjects(ctx context.Context, bkt *bucket, ch chan<- io.Reader) error {
|
||||
q, err := setObjectQuery(bkt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set object query: %w", err)
|
||||
}
|
||||
|
||||
objs := bkt.Objects(ctx, q)
|
||||
for {
|
||||
obj, err := objs.Next()
|
||||
if errors.Is(err, iterator.Done) {
|
||||
ctx.Logger().V(5).Info("finished listing objects in bucket")
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve object iterator: %w", err)
|
||||
}
|
||||
if obj == nil {
|
||||
ctx.Logger().V(5).Info("object is nil")
|
||||
continue
|
||||
}
|
||||
|
||||
if !g.shouldIncludeObject(ctx, obj.Name) || g.shouldExcludeObject(ctx, obj.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
o, err := g.constructObject(ctx, bkt.Object(obj.Name))
|
||||
if err != nil {
|
||||
ctx.Logger().V(1).Info("failed to create object", "object-name", obj.Name, "error", err)
|
||||
continue
|
||||
}
|
||||
ch <- o
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setObjectQuery(bkt *bucket) (*storage.Query, error) {
|
||||
// Setting the attribute selection is a performance optimization.
|
||||
// https://pkg.go.dev/cloud.google.com/go/storage#Query.SetAttrSelection
|
||||
q := new(storage.Query)
|
||||
err := q.SetAttrSelection([]string{
|
||||
"Name",
|
||||
"ContentType",
|
||||
"Owner",
|
||||
"Size",
|
||||
"Updated",
|
||||
"Created",
|
||||
"ACL",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set attribute selection: %w", err)
|
||||
}
|
||||
|
||||
// If a start offset is provided, then we need to set it on the query.
|
||||
// This will begin listing objects from the start offset.
|
||||
if bkt.startOffset != "" {
|
||||
q.StartOffset = bkt.startOffset
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (g *gcsManager) constructObject(ctx context.Context, obj *storage.ObjectHandle) (object, error) {
|
||||
o := object{}
|
||||
attrs, err := obj.Attrs(ctx)
|
||||
if err != nil {
|
||||
return o, fmt.Errorf("failed to retrieve object attributes: %w", err)
|
||||
}
|
||||
|
||||
if !isObjectTypeValid(ctx, attrs.Name) || !g.isObjectSizeValid(ctx, attrs.Size) {
|
||||
return o, fmt.Errorf("object is not valid")
|
||||
}
|
||||
|
||||
rc, err := obj.NewReader(ctx)
|
||||
if err != nil {
|
||||
return o, fmt.Errorf("failed to retrieve object reader: %w", err)
|
||||
}
|
||||
|
||||
o.name = attrs.Name
|
||||
o.bucket = attrs.Bucket
|
||||
o.contentType = attrs.ContentType
|
||||
o.owner = attrs.Owner
|
||||
o.link = attrs.MediaLink
|
||||
o.md5 = hex.EncodeToString(attrs.MD5)
|
||||
o.createdAt = attrs.Created
|
||||
o.updatedAt = attrs.Updated
|
||||
o.acl = objectACLs(attrs.ACL)
|
||||
o.size = attrs.Size
|
||||
o.Reader = rc
|
||||
|
||||
atomic.AddUint64(&g.numObjects, 1)
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func objectACLs(acl []storage.ACLRule) []string {
|
||||
acls := make([]string, 0, len(acl))
|
||||
for _, rule := range acl {
|
||||
acls = append(acls, string(rule.Entity))
|
||||
}
|
||||
return acls
|
||||
}
|
||||
|
||||
func isObjectTypeValid(ctx context.Context, name string) bool {
|
||||
isValid := !common.SkipFile(name)
|
||||
if !isValid {
|
||||
ctx.Logger().V(2).Info("object type is invalid", "object-name", name)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *gcsManager) isObjectSizeValid(ctx context.Context, size int64) bool {
|
||||
isValid := size > 0 && size <= g.maxObjectSize
|
||||
if !isValid {
|
||||
ctx.Logger().V(2).Info("object size is invalid", "object-size", size)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *gcsManager) shouldIncludeBucket(ctx context.Context, bkt string) bool {
|
||||
if len(g.includeBuckets) == 0 {
|
||||
return true
|
||||
}
|
||||
return shouldProcess(ctx, bkt, g.includeBuckets, globMatches)
|
||||
}
|
||||
|
||||
func (g *gcsManager) shouldExcludeBucket(ctx context.Context, bkt string) bool {
|
||||
return shouldProcess(ctx, bkt, g.excludeBuckets, globMatches)
|
||||
}
|
||||
|
||||
func (g *gcsManager) shouldIncludeObject(ctx context.Context, obj string) bool {
|
||||
if len(g.includeObjects) == 0 {
|
||||
return true
|
||||
}
|
||||
return shouldProcess(ctx, obj, g.includeObjects, globMatches)
|
||||
}
|
||||
|
||||
func (g *gcsManager) shouldExcludeObject(ctx context.Context, obj string) bool {
|
||||
return shouldProcess(ctx, obj, g.excludeObjects, globMatches)
|
||||
}
|
||||
|
||||
type globMatcherFn func(string, glob.Glob) bool
|
||||
|
||||
func shouldProcess(ctx context.Context, s string, matchers map[string]struct{}, matcherFn globMatcherFn) bool {
|
||||
if len(matchers) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for m := range matchers {
|
||||
g, err := glob.Compile(m)
|
||||
if err != nil {
|
||||
ctx.Logger().V(1).Info("failed to compile glob", "glob", m, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if matcherFn(s, g) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func globMatches(s string, g glob.Glob) bool {
|
||||
return g.Match(s)
|
||||
}
|
||||
@@ -1,913 +0,0 @@
|
||||
package gcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
)
|
||||
|
||||
const (
|
||||
publicBucket = "public-trufflehog-test-bucket"
|
||||
testProjectID = "trufflehog-testing"
|
||||
testAPIKey = "somekeys"
|
||||
testBucket = "test-bkt-th"
|
||||
testBucket2 = "test-bkt-th2"
|
||||
testBucket3 = "test-bkt-th3"
|
||||
testBucket4 = "test-bkt-th4"
|
||||
perfTestBucketGlob = "perf-test-bkt-th*"
|
||||
bucket1 = "bucket1"
|
||||
bucket2 = "bucket2"
|
||||
object1 = "object1"
|
||||
object2 = "object2"
|
||||
)
|
||||
|
||||
func TestNewGcsManager(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
projID string
|
||||
opts []gcsManagerOption
|
||||
want *gcsManager
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "new gcs manager, no options",
|
||||
projID: testProjectID,
|
||||
want: &gcsManager{projectID: testProjectID, concurrency: defaultConcurrency, maxObjectSize: defaultMaxObjectSize},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, no project id",
|
||||
projID: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, without auth",
|
||||
projID: "",
|
||||
opts: []gcsManagerOption{withoutAuthentication()},
|
||||
want: &gcsManager{
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
withoutAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with api key",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withAPIKey(ctx, testAPIKey)},
|
||||
want: &gcsManager{projectID: testProjectID, concurrency: defaultConcurrency, maxObjectSize: defaultMaxObjectSize},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with json service account",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withJSONServiceAccount(ctx, []byte(`{
|
||||
"type": "service_account",
|
||||
"project_id": "test-project"}`,
|
||||
))},
|
||||
want: &gcsManager{projectID: testProjectID, concurrency: defaultConcurrency, maxObjectSize: defaultMaxObjectSize},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with default ADC account",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx)},
|
||||
want: &gcsManager{projectID: testProjectID, concurrency: defaultConcurrency, maxObjectSize: defaultMaxObjectSize},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with bucket offsets",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withBucketOffsets(map[string]offsetInfo{
|
||||
testBucket: {lastProcessedObject: "moar1.txt", isBucketProcessed: false},
|
||||
testBucket2: {lastProcessedObject: "", isBucketProcessed: true}}),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
hasEnumerated: true,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
buckets: map[string]bucket{
|
||||
testBucket: {name: testBucket, startOffset: "moar.txt", shouldInclude: true},
|
||||
testBucket2: {name: testBucket2, startOffset: "", shouldInclude: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include buckets",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeBuckets([]string{bucket1, bucket2}),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include buckets and api key",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withIncludeBuckets([]string{bucket1, bucket2}), withAPIKey(ctx, testAPIKey)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with exclude buckets",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withExcludeBuckets([]string{bucket1, bucket2}), withAPIKey(ctx, testAPIKey)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
excludeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with exclude buckets and api key",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withExcludeBuckets([]string{bucket1, bucket2}), withAPIKey(ctx, testAPIKey)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
excludeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include and exclude buckets",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeBuckets([]string{bucket1, bucket2}),
|
||||
withExcludeBuckets([]string{"bucket3", "bucket4"}),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include and exclude buckets and api key",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeBuckets([]string{bucket1, bucket2}),
|
||||
withExcludeBuckets([]string{"bucket3", "bucket4"}),
|
||||
withAPIKey(ctx, testAPIKey),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include objects, no bucket",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withIncludeObjects([]string{"object1", "object2"})},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeObjects: map[string]struct{}{object1: {}, object2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include objects, include buckets",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeObjects([]string{"object1", "object2"}),
|
||||
withIncludeBuckets([]string{bucket1, bucket2}),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeObjects: map[string]struct{}{object1: {}, object2: {}},
|
||||
includeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include objects and api key, include buckets",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withIncludeObjects([]string{"object1", "object2"}),
|
||||
withIncludeBuckets([]string{bucket1, bucket2}),
|
||||
withAPIKey(ctx, testAPIKey),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeObjects: map[string]struct{}{object1: {}, object2: {}},
|
||||
includeBuckets: map[string]struct{}{bucket1: {}, bucket2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with exclude objects",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withExcludeObjects([]string{"object1", "object2"})},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
excludeObjects: map[string]struct{}{object1: {}, object2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with exclude objects and api key",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withExcludeObjects([]string{"object1", "object2"}), withAPIKey(ctx, testAPIKey)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
excludeObjects: map[string]struct{}{object1: {}, object2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with include and exclude objects",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeObjects([]string{"object1", "object2"}),
|
||||
withExcludeObjects([]string{"object3", "object4"}),
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeObjects: map[string]struct{}{object1: {}, object2: {}},
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with concurrency",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withConcurrency(10)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: 10,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, default concurrency",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with negative concurrency",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withConcurrency(-1)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with valid max object size",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withMaxObjectSize(10)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with negative max object size",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withMaxObjectSize(-1)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, max object size above limit",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withMaxObjectSize(int64(2 * common.GB))},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with max object size 0",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withMaxObjectSize(0)},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
concurrency: defaultConcurrency,
|
||||
maxObjectSize: defaultMaxObjectSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new gcs manager, with HTTP client",
|
||||
projID: testProjectID,
|
||||
opts: []gcsManagerOption{withHTTPClient(ctx, &http.Client{})},
|
||||
want: &gcsManager{projectID: testProjectID, concurrency: defaultConcurrency, maxObjectSize: defaultMaxObjectSize},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := newGCSManager(tc.projID, tc.opts...)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("newGCSManager() error = %v, wantErr %v", err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// The client should never be nil.
|
||||
if got.client == nil {
|
||||
t.Errorf("newGCSManager() client should not be nil")
|
||||
}
|
||||
|
||||
diff := cmp.Diff(got, tc.want,
|
||||
cmp.AllowUnexported(gcsManager{}, bucket{}),
|
||||
cmpopts.IgnoreFields(gcsManager{}, "client", "workerPool", "buckets"),
|
||||
)
|
||||
if diff != "" {
|
||||
t.Errorf("newGCSManager(%v, %v) got: %v, want: %v, diff: %v", tc.projID, tc.opts, got, tc.want, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGCSManagerStats(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
projectID string
|
||||
opts []gcsManagerOption
|
||||
wantStats *attributes
|
||||
}{
|
||||
{
|
||||
name: "enumerate, all buckets, with objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withExcludeBuckets([]string{perfTestBucketGlob, publicBucket})},
|
||||
wantStats: &attributes{
|
||||
numBuckets: 4,
|
||||
numObjects: 5,
|
||||
bucketObjects: map[string]uint64{testBucket: 2, testBucket2: 1, testBucket3: 1, testBucket4: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list objects, with exclude objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withExcludeObjects([]string{"aws1.txt", "moar2.txt"}),
|
||||
withExcludeBuckets([]string{perfTestBucketGlob, publicBucket}),
|
||||
},
|
||||
wantStats: &attributes{
|
||||
numBuckets: 4,
|
||||
numObjects: 3,
|
||||
bucketObjects: map[string]uint64{testBucket: 0, testBucket2: 1, testBucket3: 1, testBucket4: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list objects, with include objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeObjects([]string{"aws1.txt"}),
|
||||
withExcludeBuckets([]string{perfTestBucketGlob, publicBucket}),
|
||||
},
|
||||
wantStats: &attributes{
|
||||
numBuckets: 4,
|
||||
numObjects: 1,
|
||||
bucketObjects: map[string]uint64{testBucket: 1, testBucket2: 0, testBucket3: 0, testBucket4: 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
gm, err := newGCSManager(tc.projectID, tc.opts...)
|
||||
if err != nil {
|
||||
t.Fatalf("newGCSManager() error = %v", err)
|
||||
}
|
||||
|
||||
got, err := gm.attributes(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("attributes() error = %v", err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(got, tc.wantStats, cmp.AllowUnexported(attributes{}), cmpopts.IgnoreFields(attributes{}, "mu")); diff != "" {
|
||||
t.Errorf("attributes() got: %v, want: %v, diff: %v", got, tc.wantStats, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGCSManagerStats_Time(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
opts := []gcsManagerOption{withDefaultADC(ctx)}
|
||||
gm, err := newGCSManager(testProjectID, opts...)
|
||||
if err != nil {
|
||||
t.Fatalf("newGCSManager() error = %v", err)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
var stats *attributes
|
||||
stats, _ = gm.attributes(ctx)
|
||||
end := time.Since(start).Seconds()
|
||||
|
||||
fmt.Printf("Time taken to get %d objects: %f seconds\n", stats.numObjects, end)
|
||||
}
|
||||
|
||||
func TestGCSManagerListObjects(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
projectID string
|
||||
opts []gcsManagerOption
|
||||
want []object
|
||||
wantNumBkt uint32
|
||||
wantNumObj uint64
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list objects, all buckets, no objects",
|
||||
projectID: "other",
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx)},
|
||||
want: []object{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list objects, all buckets, with objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withExcludeBuckets([]string{perfTestBucketGlob, publicBucket})},
|
||||
want: []object{
|
||||
{
|
||||
name: "aws1.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/aws1.txt?generation=1677870994890594&alt=media",
|
||||
acl: []string{},
|
||||
md5: "7a4df45f839cd594ac236b19524cec92",
|
||||
},
|
||||
{
|
||||
name: "moar2.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 12,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/moar2.txt?generation=1677871000378542&alt=media",
|
||||
acl: []string{},
|
||||
md5: "e59ff97941044f85df5297e1c302d260",
|
||||
},
|
||||
{
|
||||
name: "aws3.txt",
|
||||
bucket: testBucket2,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th2/o/aws3.txt?generation=1677871022489611&alt=media",
|
||||
acl: []string{},
|
||||
md5: "26eb03378a0c8e99ee0cecf9155ace81",
|
||||
},
|
||||
{
|
||||
name: "moar.txt",
|
||||
bucket: testBucket3,
|
||||
contentType: "text/plain",
|
||||
size: 6,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th3/o/moar.txt?generation=1677871042896804&alt=media",
|
||||
acl: []string{},
|
||||
md5: "09f7e02f1290be211da707a266f153b3",
|
||||
},
|
||||
{
|
||||
name: "AMAZON_FASHION_5.json",
|
||||
bucket: testBucket4,
|
||||
contentType: "application/json",
|
||||
size: 1413469,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th4/o/AMAZON_FASHION_5.json?generation=1677871063457469&alt=media",
|
||||
acl: []string{},
|
||||
md5: "5a1d8f483aa34695256379050d42a2b7",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 4,
|
||||
wantNumObj: 5,
|
||||
},
|
||||
{
|
||||
name: "list objects, include buckets, with objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withIncludeBuckets([]string{testBucket})},
|
||||
want: []object{
|
||||
{
|
||||
name: "aws1.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/aws1.txt?generation=1677870994890594&alt=media",
|
||||
acl: []string{},
|
||||
md5: "7a4df45f839cd594ac236b19524cec92",
|
||||
},
|
||||
{
|
||||
name: "moar2.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 12,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/moar2.txt?generation=1677871000378542&alt=media",
|
||||
acl: []string{},
|
||||
md5: "e59ff97941044f85df5297e1c302d260",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 1,
|
||||
wantNumObj: 2,
|
||||
},
|
||||
{
|
||||
name: "list objects, exclude buckets, with objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{withDefaultADC(ctx), withExcludeBuckets([]string{testBucket, testBucket2, perfTestBucketGlob, publicBucket})},
|
||||
want: []object{
|
||||
{
|
||||
name: "moar.txt",
|
||||
bucket: testBucket3,
|
||||
contentType: "text/plain",
|
||||
size: 6,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th3/o/moar.txt?generation=1677871042896804&alt=media",
|
||||
acl: []string{},
|
||||
md5: "09f7e02f1290be211da707a266f153b3",
|
||||
},
|
||||
{
|
||||
name: "AMAZON_FASHION_5.json",
|
||||
bucket: testBucket4,
|
||||
contentType: "application/json",
|
||||
size: 1413469,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th4/o/AMAZON_FASHION_5.json?generation=1677871063457469&alt=media",
|
||||
acl: []string{},
|
||||
md5: "5a1d8f483aa34695256379050d42a2b7",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 2,
|
||||
wantNumObj: 2,
|
||||
},
|
||||
{
|
||||
name: "list objects, with exclude objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withExcludeObjects([]string{"aws1.txt", "moar2.txt"}),
|
||||
withExcludeBuckets([]string{perfTestBucketGlob, publicBucket}),
|
||||
},
|
||||
want: []object{
|
||||
{
|
||||
name: "aws3.txt",
|
||||
bucket: testBucket2,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th2/o/aws3.txt?generation=1677871022489611&alt=media",
|
||||
acl: []string{},
|
||||
md5: "26eb03378a0c8e99ee0cecf9155ace81",
|
||||
},
|
||||
{
|
||||
name: "moar.txt",
|
||||
bucket: testBucket3,
|
||||
contentType: "text/plain",
|
||||
size: 6,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th3/o/moar.txt?generation=1677871042896804&alt=media",
|
||||
acl: []string{},
|
||||
md5: "09f7e02f1290be211da707a266f153b3",
|
||||
},
|
||||
{
|
||||
name: "AMAZON_FASHION_5.json",
|
||||
bucket: testBucket4,
|
||||
contentType: "application/json",
|
||||
size: 1413469,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th4/o/AMAZON_FASHION_5.json?generation=1677871063457469&alt=media",
|
||||
acl: []string{},
|
||||
md5: "5a1d8f483aa34695256379050d42a2b7",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 4, // We still list objects in all buckets.
|
||||
wantNumObj: 3,
|
||||
},
|
||||
{
|
||||
name: "list objects, with include objects, include bucket",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeObjects([]string{"aws1.txt"}),
|
||||
withIncludeBuckets([]string{testBucket}),
|
||||
},
|
||||
want: []object{
|
||||
{
|
||||
name: "aws1.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/aws1.txt?generation=1677870994890594&alt=media",
|
||||
acl: []string{},
|
||||
md5: "7a4df45f839cd594ac236b19524cec92",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 1,
|
||||
wantNumObj: 1,
|
||||
},
|
||||
{
|
||||
name: "list objects, with include objects",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeObjects([]string{"aws1.txt"}),
|
||||
withExcludeBuckets([]string{perfTestBucketGlob, publicBucket}),
|
||||
},
|
||||
want: []object{
|
||||
{
|
||||
name: "aws1.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 150,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/aws1.txt?generation=1677870994890594&alt=media",
|
||||
acl: []string{},
|
||||
md5: "7a4df45f839cd594ac236b19524cec92",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 4,
|
||||
wantNumObj: 1,
|
||||
},
|
||||
{
|
||||
name: "list objects, with include bucket, exclude object",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeBuckets([]string{testBucket}),
|
||||
withExcludeObjects([]string{"aws1.txt"}),
|
||||
withExcludeBuckets([]string{perfTestBucketGlob, publicBucket}),
|
||||
},
|
||||
want: []object{
|
||||
{
|
||||
name: "moar2.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 12,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/moar2.txt?generation=1677871000378542&alt=media",
|
||||
acl: []string{},
|
||||
md5: "e59ff97941044f85df5297e1c302d260",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 1,
|
||||
wantNumObj: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mgr, err := newGCSManager(tc.projectID, tc.opts...)
|
||||
assert.Nil(t, err)
|
||||
|
||||
got, err := mgr.listObjects(ctx)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("GCSManager.ListObjects() error = %v, wantErr %v", err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// If we expect an error, we're done.
|
||||
// Short-circuit the rest of the test.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
go func() {
|
||||
defer func() {
|
||||
doneCh <- struct{}{}
|
||||
}()
|
||||
res := make([]object, 0, len(tc.want))
|
||||
for obj := range got {
|
||||
res = append(res, obj.(object))
|
||||
}
|
||||
|
||||
if len(res) != len(tc.want) {
|
||||
t.Errorf("gcsManager.listObjects() got: %v, want: %v", res, tc.want)
|
||||
}
|
||||
|
||||
// Test the bucket and object counts.
|
||||
assert.Equal(t, tc.wantNumBkt, mgr.numBuckets)
|
||||
assert.Equal(t, tc.wantNumObj, mgr.numObjects)
|
||||
|
||||
// Sort the objects by name to make the test deterministic.
|
||||
// This is necessary because we list bucket objects concurrently.
|
||||
sort.Slice(res, func(i, j int) bool { return res[i].name < res[j].name })
|
||||
sort.Slice(tc.want, func(i, j int) bool { return tc.want[i].name < tc.want[j].name })
|
||||
|
||||
// Test the objects are equal.
|
||||
if diff := cmp.Diff(res, tc.want, cmp.AllowUnexported(object{}), cmpopts.IgnoreFields(object{}, "Reader", "createdAt", "updatedAt")); diff != "" {
|
||||
t.Errorf("gcsManager.listObjects() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}()
|
||||
|
||||
<-doneCh
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGCSManagerListObjects_Resuming(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
projectID string
|
||||
opts []gcsManagerOption
|
||||
want []object
|
||||
wantNumBkt uint32
|
||||
wantNumObj uint64
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list objects, testBucket, resume from moar2.txt",
|
||||
projectID: testProjectID,
|
||||
opts: []gcsManagerOption{
|
||||
withDefaultADC(ctx),
|
||||
withIncludeBuckets([]string{testBucket}),
|
||||
withBucketOffsets(map[string]offsetInfo{testBucket: {lastProcessedObject: "moar2.txt"}}),
|
||||
},
|
||||
want: []object{
|
||||
{
|
||||
name: "moar2.txt",
|
||||
bucket: testBucket,
|
||||
contentType: "text/plain",
|
||||
size: 12,
|
||||
link: "https://storage.googleapis.com/download/storage/v1/b/test-bkt-th/o/moar2.txt?generation=1677871000378542&alt=media",
|
||||
acl: []string{},
|
||||
md5: "e59ff97941044f85df5297e1c302d260",
|
||||
},
|
||||
},
|
||||
wantNumBkt: 1,
|
||||
wantNumObj: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mgr, err := newGCSManager(tc.projectID, tc.opts...)
|
||||
assert.Nil(t, err)
|
||||
|
||||
got, err := mgr.listObjects(ctx)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("GCSManager.ListObjects() error = %v, wantErr %v", err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
// If we expect an error, we're done.
|
||||
// Short-circuit the rest of the test.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
go func() {
|
||||
defer func() {
|
||||
doneCh <- struct{}{}
|
||||
}()
|
||||
res := make([]object, 0, len(tc.want))
|
||||
for obj := range got {
|
||||
res = append(res, obj.(object))
|
||||
}
|
||||
|
||||
if len(res) != len(tc.want) {
|
||||
t.Errorf("gcsManager.listObjects() got: %v, want: %v", res, tc.want)
|
||||
}
|
||||
|
||||
// Test the bucket and object counts.
|
||||
assert.Equal(t, tc.wantNumBkt, mgr.numBuckets)
|
||||
assert.Equal(t, tc.wantNumObj, mgr.numObjects)
|
||||
|
||||
// Sort the objects by name to make the test deterministic.
|
||||
// This is necessary because we list bucket objects concurrently.
|
||||
sort.Slice(res, func(i, j int) bool { return res[i].name < res[j].name })
|
||||
sort.Slice(tc.want, func(i, j int) bool { return tc.want[i].name < tc.want[j].name })
|
||||
|
||||
// Test the objects are equal.
|
||||
if diff := cmp.Diff(res, tc.want, cmp.AllowUnexported(object{}), cmpopts.IgnoreFields(object{}, "Reader", "createdAt", "updatedAt")); diff != "" {
|
||||
t.Errorf("gcsManager.listObjects() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}()
|
||||
|
||||
<-doneCh
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isObjectTypeValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
objName string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "valid object type, text",
|
||||
objName: "aws1.txt",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid object type, zip",
|
||||
objName: "aws1.zip",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "invalid object type",
|
||||
objName: "video.mp4",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := isObjectTypeValid(ctx, tc.objName)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isObjectSizeValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
objSize int64
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "valid object size, 150 bytes",
|
||||
objSize: 150,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid object size, 1 MB",
|
||||
objSize: int64(1 * common.MB),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid object size, 49 MB",
|
||||
objSize: int64(49 * common.MB),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "invalid object size, 1 GB",
|
||||
objSize: int64(1 * common.GB),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid object size, 2 GB",
|
||||
objSize: int64(2 * common.GB),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid object size, empty object",
|
||||
objSize: 0,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid object size, negative object size",
|
||||
objSize: -1,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
g := &gcsManager{
|
||||
maxObjectSize: maxObjectSizeLimit,
|
||||
}
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := g.isObjectSizeValid(ctx, tc.objSize)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
package gcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/sources"
|
||||
)
|
||||
|
||||
func createTestSource(src *sourcespb.GCS) (*Source, *anypb.Any) {
|
||||
s := &Source{}
|
||||
conn, err := anypb.New(src)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return s, conn
|
||||
}
|
||||
|
||||
func TestSourceInit(t *testing.T) {
|
||||
source, conn := createTestSource(&sourcespb.GCS{
|
||||
ProjectId: testProjectID,
|
||||
IncludeBuckets: []string{
|
||||
publicBucket,
|
||||
},
|
||||
ExcludeBuckets: []string{
|
||||
perfTestBucketGlob,
|
||||
},
|
||||
ExcludeObjects: []string{
|
||||
"object1",
|
||||
},
|
||||
Credential: &sourcespb.GCS_Unauthenticated{},
|
||||
})
|
||||
|
||||
err := source.Init(context.Background(), "test", 1, 1, true, conn, 8)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, source.gcsManager)
|
||||
}
|
||||
|
||||
func TestConfigureGCSManager(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
conn *sourcespb.GCS
|
||||
want *gcsManager
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "nil conn",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid conn, bare config",
|
||||
conn: &sourcespb.GCS{
|
||||
ProjectId: testProjectID,
|
||||
Credential: &sourcespb.GCS_Adc{},
|
||||
ExcludeBuckets: []string{
|
||||
perfTestBucketGlob,
|
||||
},
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
excludeBuckets: map[string]struct{}{perfTestBucketGlob: {}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid conn, include and exclude buckets",
|
||||
conn: &sourcespb.GCS{
|
||||
ProjectId: testProjectID,
|
||||
Credential: &sourcespb.GCS_Adc{},
|
||||
IncludeBuckets: []string{
|
||||
"bucket1",
|
||||
},
|
||||
ExcludeBuckets: []string{
|
||||
perfTestBucketGlob,
|
||||
},
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeBuckets: map[string]struct{}{"bucket1": {}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid conn, include and exclude objects",
|
||||
conn: &sourcespb.GCS{
|
||||
ProjectId: testProjectID,
|
||||
Credential: &sourcespb.GCS_Adc{},
|
||||
IncludeObjects: []string{
|
||||
"object1",
|
||||
},
|
||||
ExcludeObjects: []string{
|
||||
"object2",
|
||||
},
|
||||
ExcludeBuckets: []string{
|
||||
perfTestBucketGlob,
|
||||
},
|
||||
},
|
||||
want: &gcsManager{
|
||||
projectID: testProjectID,
|
||||
includeObjects: map[string]struct{}{"object1": {}},
|
||||
excludeBuckets: map[string]struct{}{perfTestBucketGlob: {}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
got, err := configureGCSManager(ctx, tc.conn, 8)
|
||||
if err != nil && !tc.wantErr {
|
||||
t.Errorf("source.configureGCSManager() error = %v", err)
|
||||
}
|
||||
|
||||
if !tc.wantErr {
|
||||
if diff := cmp.Diff(tc.want, got,
|
||||
cmp.AllowUnexported(gcsManager{}),
|
||||
cmpopts.IgnoreFields(gcsManager{}, "client", "workerPool", "concurrency", "buckets", "maxObjectSize", "attr"),
|
||||
); diff != "" {
|
||||
t.Errorf("source.Init() diff: (-want +got)\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceOauth2Client(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
creds *credentialspb.Oauth2
|
||||
want *http.Client
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid creds",
|
||||
creds: &credentialspb.Oauth2{
|
||||
RefreshToken: "some-refresh-token",
|
||||
ClientId: "some-client-id",
|
||||
AccessToken: "some-access-token",
|
||||
},
|
||||
want: &http.Client{},
|
||||
},
|
||||
{
|
||||
name: "invalid creds, nil creds",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid creds, empty refresh token",
|
||||
creds: &credentialspb.Oauth2{
|
||||
RefreshToken: "",
|
||||
AccessToken: "some-access-token",
|
||||
ClientId: "some-client-id",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid creds, empty client id",
|
||||
creds: &credentialspb.Oauth2{
|
||||
RefreshToken: "some-refresh-token",
|
||||
AccessToken: "some-access-token",
|
||||
ClientId: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid creds, empty access token",
|
||||
creds: &credentialspb.Oauth2{
|
||||
AccessToken: "",
|
||||
RefreshToken: "some-refresh-token",
|
||||
ClientId: "some-client-id",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
got, err := oauth2Client(ctx, tc.creds)
|
||||
if (err != nil) != tc.wantErr {
|
||||
t.Errorf("source.oauth2Client() error = %v", err)
|
||||
}
|
||||
|
||||
if !tc.wantErr {
|
||||
if diff := cmp.Diff(tc.want, got,
|
||||
cmpopts.IgnoreFields(http.Client{}, "Transport"),
|
||||
); diff != "" {
|
||||
t.Errorf("source.oauth2Client() diff: (-want +got)\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockObjectManager struct {
|
||||
// numObjects is the number of objects to return in the listObjects call.
|
||||
numObjects int
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
func (m *mockObjectManager) attributes(_ context.Context) (*attributes, error) {
|
||||
if m.wantErr {
|
||||
return nil, fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
return &attributes{
|
||||
numObjects: uint64(m.numObjects),
|
||||
numBuckets: 1,
|
||||
bucketObjects: map[string]uint64{testBucket: 5},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type mockReader struct {
|
||||
offset int
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (m *mockReader) Read(p []byte) (n int, err error) {
|
||||
if m.offset >= len(m.data) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n = copy(p, m.data[m.offset:])
|
||||
m.offset += n
|
||||
return
|
||||
}
|
||||
|
||||
func (m *mockObjectManager) listObjects(context.Context) (chan io.Reader, error) {
|
||||
if m.wantErr {
|
||||
return nil, fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
ch := make(chan io.Reader)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
// Add 5 objects to the channel.
|
||||
for i := 0; i < m.numObjects; i++ {
|
||||
ch <- createTestObject(i)
|
||||
}
|
||||
}()
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func createTestObject(id int) object {
|
||||
return object{
|
||||
name: fmt.Sprintf("object%d", id),
|
||||
bucket: testBucket,
|
||||
contentType: "plain/text",
|
||||
owner: "testman@test.com",
|
||||
link: fmt.Sprintf("https://storage.googleapis.com/%s/%s", testBucket, fmt.Sprintf("object%d", id)),
|
||||
acl: []string{"authenticatedUsers"},
|
||||
size: 42,
|
||||
md5: fmt.Sprintf("md5hash%d", id),
|
||||
Reader: &mockReader{data: []byte(fmt.Sprintf("hello world %d", id))},
|
||||
createdAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
}
|
||||
}
|
||||
|
||||
func createTestSourceChunk(id int) *sources.Chunk {
|
||||
return &sources.Chunk{
|
||||
SourceName: "test",
|
||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GCS,
|
||||
SourceID: 0,
|
||||
Verify: true,
|
||||
Data: []byte(fmt.Sprintf("hello world %d", id)),
|
||||
SourceMetadata: &source_metadatapb.MetaData{
|
||||
Data: &source_metadatapb.MetaData_Gcs{
|
||||
Gcs: &source_metadatapb.GCS{
|
||||
Filename: fmt.Sprintf("object%d", id),
|
||||
Bucket: testBucket,
|
||||
ContentType: "plain/text",
|
||||
Email: "testman@test.com",
|
||||
Link: fmt.Sprintf("https://storage.googleapis.com/%s/%s", testBucket, fmt.Sprintf("object%d", id)),
|
||||
Acls: []string{"authenticatedUsers"},
|
||||
CreatedAt: "1577836800",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceChunks_ListObjects(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
|
||||
source := &Source{
|
||||
gcsManager: &mockObjectManager{numObjects: 5},
|
||||
chunksCh: chunksCh,
|
||||
Progress: sources.Progress{},
|
||||
}
|
||||
|
||||
err := source.enumerate(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
go func() {
|
||||
defer close(chunksCh)
|
||||
err := source.Chunks(ctx, chunksCh)
|
||||
assert.Nil(t, err)
|
||||
}()
|
||||
|
||||
want := make([]*sources.Chunk, 0, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
want = append(want, createTestSourceChunk(i))
|
||||
}
|
||||
|
||||
count := 0
|
||||
got := make([]*sources.Chunk, 0, 5)
|
||||
for ch := range chunksCh {
|
||||
got = append(got, ch)
|
||||
count++
|
||||
}
|
||||
|
||||
// Ensure we get 5 objects back.
|
||||
assert.Equal(t, 5, count)
|
||||
|
||||
// Sort the results to ensure deterministic ordering.
|
||||
sort.Slice(want, func(i, j int) bool {
|
||||
return want[i].SourceMetadata.GetGcs().Filename < want[j].SourceMetadata.GetGcs().Filename
|
||||
})
|
||||
sort.Slice(got, func(i, j int) bool {
|
||||
return got[i].SourceMetadata.GetGcs().Filename < got[j].SourceMetadata.GetGcs().Filename
|
||||
})
|
||||
|
||||
for i, c := range got {
|
||||
assert.Equal(t, want[i].SourceMetadata.GetGcs().Filename, c.SourceMetadata.GetGcs().Filename)
|
||||
assert.Equal(t, want[i].Data, c.Data)
|
||||
assert.Equal(t, want[i].SourceMetadata.GetGcs().CreatedAt, c.SourceMetadata.GetGcs().CreatedAt)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSourceInit_Enumerate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
source := &Source{gcsManager: &mockObjectManager{numObjects: 5}}
|
||||
|
||||
err := source.enumerate(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Ensure the attributes are set.
|
||||
assert.Equal(t, uint64(5), source.stats.numObjects)
|
||||
assert.Equal(t, uint32(1), source.stats.numBuckets)
|
||||
assert.Equal(t, uint64(5), source.stats.bucketObjects[testBucket])
|
||||
}
|
||||
|
||||
func TestSourceChunks_ListObjects_Error(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
source := &Source{gcsManager: &mockObjectManager{wantErr: true}}
|
||||
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
|
||||
defer close(chunksCh)
|
||||
err := source.Chunks(ctx, chunksCh)
|
||||
assert.True(t, err != nil)
|
||||
}
|
||||
|
||||
func TestSourceChunks_ProgressSet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
source := &Source{
|
||||
gcsManager: &mockObjectManager{numObjects: defaultCachePersistIncrement},
|
||||
chunksCh: chunksCh,
|
||||
Progress: sources.Progress{},
|
||||
}
|
||||
|
||||
err := source.enumerate(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
go func() {
|
||||
defer close(chunksCh)
|
||||
err := source.Chunks(ctx, chunksCh)
|
||||
assert.Nil(t, err)
|
||||
}()
|
||||
|
||||
want := make([]*sources.Chunk, 0, defaultCachePersistIncrement)
|
||||
for i := 0; i < defaultCachePersistIncrement; i++ {
|
||||
want = append(want, createTestSourceChunk(i))
|
||||
}
|
||||
|
||||
got := make([]*sources.Chunk, 0, defaultCachePersistIncrement)
|
||||
for ch := range chunksCh {
|
||||
got = append(got, ch)
|
||||
}
|
||||
|
||||
// Ensure we get 2500 objects back.
|
||||
assert.Equal(t, len(want), len(got))
|
||||
|
||||
// Test that the resume progress is set.
|
||||
var progress strings.Builder
|
||||
for i := range got {
|
||||
progress.WriteString(fmt.Sprintf("md5hash%d", i))
|
||||
// Add a comma if not the last element.
|
||||
if i != len(got)-1 {
|
||||
progress.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
encodeResume := strings.Split(source.Progress.EncodedResumeInfo, ",")
|
||||
sort.Slice(encodeResume, func(i, j int) bool {
|
||||
numI, _ := strconv.Atoi(strings.TrimPrefix(encodeResume[i], "md5hash"))
|
||||
numJ, _ := strconv.Atoi(strings.TrimPrefix(encodeResume[j], "md5hash"))
|
||||
return numI < numJ
|
||||
})
|
||||
|
||||
assert.Equal(t, progress.String(), strings.Join(encodeResume, ","))
|
||||
assert.Equal(t, int32(defaultCachePersistIncrement), source.Progress.SectionsCompleted)
|
||||
assert.Equal(t, int64(100), source.Progress.PercentComplete)
|
||||
assert.Equal(t, fmt.Sprintf("GCS source finished processing %d objects", defaultCachePersistIncrement), source.Progress.Message)
|
||||
}
|
||||
|
||||
func TestSource_CachePersistence(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
wantObjCnt := 4 // ensure we have less objects than the cache increment
|
||||
mockObjManager := &mockObjectManager{numObjects: wantObjCnt}
|
||||
|
||||
chunksCh := make(chan *sources.Chunk, 1)
|
||||
source := &Source{
|
||||
gcsManager: mockObjManager,
|
||||
chunksCh: chunksCh,
|
||||
Progress: sources.Progress{},
|
||||
}
|
||||
|
||||
err := source.enumerate(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
go func() {
|
||||
defer close(chunksCh)
|
||||
err := source.Chunks(ctx, chunksCh)
|
||||
assert.Nil(t, err)
|
||||
}()
|
||||
|
||||
want := make([]*sources.Chunk, 0, wantObjCnt)
|
||||
for i := 0; i < wantObjCnt; i++ {
|
||||
want = append(want, createTestSourceChunk(i))
|
||||
}
|
||||
|
||||
got := make([]*sources.Chunk, 0, wantObjCnt)
|
||||
for ch := range chunksCh {
|
||||
got = append(got, ch)
|
||||
}
|
||||
|
||||
// Ensure we get 4 objects back.
|
||||
assert.Equal(t, len(want), len(got))
|
||||
|
||||
// Test that the resume progress is empty.
|
||||
// The cache should not have been persisted.
|
||||
assert.Equal(t, "", source.Progress.EncodedResumeInfo)
|
||||
assert.Equal(t, int32(wantObjCnt), source.Progress.SectionsCompleted)
|
||||
assert.Equal(t, int64(100), source.Progress.PercentComplete)
|
||||
assert.Equal(t, fmt.Sprintf("GCS source finished processing %d objects", wantObjCnt), source.Progress.Message)
|
||||
}
|
||||
@@ -295,7 +295,7 @@ func CloneRepo(ctx context.Context, userInfo *url.Userinfo, gitUrl string, args
|
||||
return "", nil, errors.New("clone command exited with no output")
|
||||
}
|
||||
if cloneCmd.ProcessState != nil && cloneCmd.ProcessState.ExitCode() != 0 {
|
||||
logger.V(1).Info("git clone failed", "output", string(output), "error", err)
|
||||
logger.V(1).Info("git clone failed", "error", err)
|
||||
return "", nil, fmt.Errorf("could not clone repo: %s, %w", safeUrl, err)
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ func (s *Git) ScanCommits(ctx context.Context, repo *git.Repository, path string
|
||||
return err
|
||||
}
|
||||
|
||||
commitChan, err := gitparse.NewParser().RepoPath(ctx, path, scanOptions.HeadHash, scanOptions.BaseHash == "", scanOptions.ExcludeGlobs)
|
||||
commitChan, err := gitparse.NewParser().RepoPath(ctx, path, scanOptions.HeadHash, scanOptions.BaseHash == "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -378,7 +378,7 @@ func (s *Git) ScanCommits(ctx context.Context, repo *git.Repository, path string
|
||||
var email, hash, when string
|
||||
email = commit.Author
|
||||
hash = commit.Hash
|
||||
when = commit.Date.Format("2006-01-02 15:04:05 -0700")
|
||||
when = commit.Date.String()
|
||||
|
||||
// Handle binary files by reading the entire file rather than using the diff.
|
||||
if diff.IsBinary {
|
||||
@@ -520,7 +520,7 @@ func (s *Git) ScanStaged(ctx context.Context, repo *git.Repository, path string,
|
||||
var email, hash, when string
|
||||
email = commit.Author
|
||||
hash = commit.Hash
|
||||
when = commit.Date.Format("2006-01-02 15:04:05 -0700")
|
||||
when = commit.Date.String()
|
||||
|
||||
// Handle binary files by reading the entire file rather than using the diff.
|
||||
if diff.IsBinary {
|
||||
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
)
|
||||
|
||||
type ScanOptions struct {
|
||||
Filter *common.Filter
|
||||
BaseHash string // When scanning a git.Log, this is the oldest/first commit.
|
||||
HeadHash string
|
||||
MaxDepth int64
|
||||
ExcludeGlobs []string
|
||||
LogOptions *git.LogOptions
|
||||
Filter *common.Filter
|
||||
BaseHash string // When scanning a git.Log, this is the oldest/first commit.
|
||||
HeadHash string
|
||||
MaxDepth int64
|
||||
LogOptions *git.LogOptions
|
||||
}
|
||||
|
||||
type ScanOption func(*ScanOptions)
|
||||
@@ -40,12 +39,6 @@ func ScanOptionMaxDepth(maxDepth int64) ScanOption {
|
||||
}
|
||||
}
|
||||
|
||||
func ScanOptionExcludeGlobs(globs []string) ScanOption {
|
||||
return func(scanOptions *ScanOptions) {
|
||||
scanOptions.ExcludeGlobs = globs
|
||||
}
|
||||
}
|
||||
|
||||
func ScanOptionLogOptions(logOptions *git.LogOptions) ScanOption {
|
||||
return func(scanOptions *ScanOptions) {
|
||||
scanOptions.LogOptions = logOptions
|
||||
|
||||
@@ -88,8 +88,6 @@ func (s *Source) newClient(region string) (*s3.S3, error) {
|
||||
cfg.Region = aws.String(region)
|
||||
|
||||
switch cred := s.conn.GetCredential().(type) {
|
||||
case *sourcespb.S3_SessionToken:
|
||||
cfg.Credentials = credentials.NewStaticCredentials(cred.SessionToken.Key, cred.SessionToken.Secret, cred.SessionToken.SessionToken)
|
||||
case *sourcespb.S3_AccessKey:
|
||||
cfg.Credentials = credentials.NewStaticCredentials(cred.AccessKey.Key, cred.AccessKey.Secret, "")
|
||||
case *sourcespb.S3_Unauthenticated:
|
||||
@@ -123,7 +121,7 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk) err
|
||||
var bucketsToScan []string
|
||||
|
||||
switch s.conn.GetCredential().(type) {
|
||||
case *sourcespb.S3_AccessKey, *sourcespb.S3_SessionToken, *sourcespb.S3_CloudEnvironment:
|
||||
case *sourcespb.S3_AccessKey, *sourcespb.S3_CloudEnvironment:
|
||||
if len(s.conn.Buckets) == 0 {
|
||||
res, err := client.ListBuckets(&s3.ListBucketsInput{})
|
||||
if err != nil {
|
||||
|
||||
+1
-33
@@ -44,33 +44,6 @@ type Source interface {
|
||||
GetProgress() *Progress
|
||||
}
|
||||
|
||||
// GCSConfig defines the optional configuration for a GCS source.
|
||||
type GCSConfig struct {
|
||||
// CloudCred determines whether to use cloud credentials.
|
||||
// This can NOT be used with a secret.
|
||||
CloudCred,
|
||||
// WithoutAuth is a flag to indicate whether to use authentication.
|
||||
WithoutAuth bool
|
||||
// ApiKey is the API key to use to authenticate with the source.
|
||||
ApiKey,
|
||||
// ProjectID is the project ID to use to authenticate with the source.
|
||||
ProjectID,
|
||||
// ServiceAccount is the service account to use to authenticate with the source.
|
||||
ServiceAccount string
|
||||
// MaxObjectSize is the maximum object size to scan.
|
||||
MaxObjectSize int64
|
||||
// Concurrency is the number of concurrent workers to use to scan the source.
|
||||
Concurrency int
|
||||
// IncludeBuckets is a list of buckets to include in the scan.
|
||||
IncludeBuckets,
|
||||
// ExcludeBuckets is a list of buckets to exclude from the scan.
|
||||
ExcludeBuckets,
|
||||
// IncludeObjects is a list of objects to include in the scan.
|
||||
IncludeObjects,
|
||||
// ExcludeObjects is a list of objects to exclude from the scan.
|
||||
ExcludeObjects []string
|
||||
}
|
||||
|
||||
// GitConfig defines the optional configuration for a git source.
|
||||
type GitConfig struct {
|
||||
// RepoPath is the path to the repository to scan.
|
||||
@@ -83,9 +56,6 @@ type GitConfig struct {
|
||||
MaxDepth int
|
||||
// Filter is the filter to use to scan the source.
|
||||
Filter *common.Filter
|
||||
// ExcludeGlobs is a list of globs to exclude from the scan.
|
||||
// This differs from the Filter exclusions as ExcludeGlobs is applied at the `git log -p` level
|
||||
ExcludeGlobs []string
|
||||
}
|
||||
|
||||
// GithubConfig defines the optional configuration for a github source.
|
||||
@@ -140,9 +110,7 @@ type S3Config struct {
|
||||
// Key is any key to use to authenticate with the source.
|
||||
Key,
|
||||
// Secret is any secret to use to authenticate with the source.
|
||||
Secret,
|
||||
// Temporary session token associated with a temporary access key id and secret key.
|
||||
SessionToken string
|
||||
Secret string
|
||||
// Buckets is the list of buckets to scan.
|
||||
Buckets []string
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ message Oauth2 {
|
||||
string refresh_token = 1;
|
||||
string client_id = 2;
|
||||
string client_secret = 3;
|
||||
string access_token = 4;
|
||||
}
|
||||
|
||||
message KeySecret {
|
||||
@@ -45,12 +44,6 @@ message KeySecret {
|
||||
string secret = 2;
|
||||
}
|
||||
|
||||
message AWSSessionTokenSecret {
|
||||
string key = 1;
|
||||
string secret = 2;
|
||||
string sessionToken = 3;
|
||||
}
|
||||
|
||||
message AWS {
|
||||
string key = 1;
|
||||
string secret = 2;
|
||||
|
||||
@@ -8,7 +8,6 @@ enum DecoderType {
|
||||
UNKNOWN = 0;
|
||||
PLAIN = 1;
|
||||
BASE64 = 2;
|
||||
UTF16 = 3;
|
||||
}
|
||||
|
||||
enum DetectorType {
|
||||
@@ -913,13 +912,6 @@ enum DetectorType {
|
||||
Shopify = 902;
|
||||
RabbitMQ = 903;
|
||||
CustomRegex = 904;
|
||||
Etherscan = 905;
|
||||
Infura = 906;
|
||||
Alchemy = 907;
|
||||
BlockNative = 908;
|
||||
Moralis = 909;
|
||||
BscScan = 910;
|
||||
CoinMarketCap = 911;
|
||||
}
|
||||
|
||||
message Result {
|
||||
|
||||
+10
-28
@@ -111,15 +111,20 @@ message Gitlab {
|
||||
int64 line = 7;
|
||||
}
|
||||
|
||||
message GoogleDrive {
|
||||
string file = 1;
|
||||
string link = 2;
|
||||
string email = 3;
|
||||
string timestamp = 4;
|
||||
bool shared = 5;
|
||||
string last_modified_by = 6;
|
||||
}
|
||||
|
||||
message GCS {
|
||||
string bucket = 1;
|
||||
string filename = 2;
|
||||
string file = 2;
|
||||
string link = 3;
|
||||
string email = 4;
|
||||
string created_at = 5;
|
||||
string updated_at = 6;
|
||||
repeated string acls = 7;
|
||||
string content_type = 8;
|
||||
}
|
||||
|
||||
message Jira {
|
||||
@@ -203,8 +208,6 @@ message Teams {
|
||||
string file = 6;
|
||||
string email = 7;
|
||||
string location = 8;
|
||||
string team_name = 9;
|
||||
string team_id = 10;
|
||||
}
|
||||
|
||||
// https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-FileInfo
|
||||
@@ -232,25 +235,6 @@ message PublicEventMonitoring {
|
||||
}
|
||||
}
|
||||
|
||||
message SharePoint {
|
||||
string link = 1;
|
||||
string timestamp = 2;
|
||||
string author = 3;
|
||||
string title = 4;
|
||||
int64 views = 5;
|
||||
string docid = 6;
|
||||
string email = 7;
|
||||
}
|
||||
|
||||
message GoogleDrive {
|
||||
string file = 1;
|
||||
string link = 2;
|
||||
string email = 3;
|
||||
string timestamp = 4;
|
||||
bool shared = 5;
|
||||
string last_modified_by = 6;
|
||||
}
|
||||
|
||||
message MetaData {
|
||||
oneof data {
|
||||
Azure azure = 1;
|
||||
@@ -277,7 +261,5 @@ message MetaData {
|
||||
Artifactory artifactory = 22;
|
||||
Syslog syslog = 23;
|
||||
PublicEventMonitoring publicEventMonitoring = 24;
|
||||
SharePoint sharepoint = 25;
|
||||
GoogleDrive googleDrive = 26;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-24
@@ -41,8 +41,6 @@ enum SourceType {
|
||||
SOURCE_TYPE_PUBLIC_EVENT_MONITORING = 26;
|
||||
SOURCE_TYPE_SLACK_REALTIME = 27;
|
||||
SOURCE_TYPE_GOOGLE_DRIVE = 28;
|
||||
SOURCE_TYPE_SHAREPOINT = 29;
|
||||
SOURCE_TYPE_GCS_UNAUTHED = 30;
|
||||
}
|
||||
|
||||
message LocalSource {
|
||||
@@ -127,19 +125,9 @@ message Filesystem {
|
||||
|
||||
message GCS {
|
||||
oneof credential {
|
||||
string json_service_account = 1;
|
||||
string api_key = 2;
|
||||
credentials.Unauthenticated unauthenticated = 3;
|
||||
credentials.CloudEnvironment adc = 4;
|
||||
string service_account_file = 11;
|
||||
credentials.Oauth2 oauth = 12;
|
||||
string json_sa = 1;
|
||||
}
|
||||
string project_id = 5;
|
||||
repeated string include_buckets = 6;
|
||||
repeated string exclude_buckets = 7;
|
||||
repeated string include_objects = 8;
|
||||
repeated string exclude_objects = 9;
|
||||
int64 max_object_size = 10;
|
||||
repeated string buckets = 2;
|
||||
}
|
||||
|
||||
message Git {
|
||||
@@ -214,7 +202,6 @@ message S3 {
|
||||
credentials.KeySecret access_key = 1;
|
||||
credentials.Unauthenticated unauthenticated = 2;
|
||||
credentials.CloudEnvironment cloud_environment = 4;
|
||||
credentials.AWSSessionTokenSecret session_token = 5;
|
||||
}
|
||||
repeated string buckets = 3;
|
||||
}
|
||||
@@ -259,11 +246,10 @@ message Teams {
|
||||
oneof credential {
|
||||
string token = 2;
|
||||
credentials.ClientCredentials authenticated = 3;
|
||||
credentials.Oauth2 oauth = 7;
|
||||
}
|
||||
repeated string channels = 4;
|
||||
repeated string ignoreList = 5;
|
||||
repeated string team_ids = 6;
|
||||
string team_id = 6;
|
||||
}
|
||||
|
||||
// https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-RetrieveFolderorRepositoryArchive
|
||||
@@ -298,10 +284,3 @@ message SlackRealtime {
|
||||
credentials.SlackTokens tokens = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message Sharepoint {
|
||||
oneof credential {
|
||||
credentials.Oauth2 oauth = 1;
|
||||
}
|
||||
string site_url = 2;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user