Skip to content

Commit de70ceb

Browse files
Merge branch 'main' into lambdatest
2 parents 2fd6f81 + 8b6aac3 commit de70ceb

File tree

10 files changed

+431
-107
lines changed

10 files changed

+431
-107
lines changed

action.yml

Lines changed: 73 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ inputs:
1010
base:
1111
description: Start scanning from here (usually main branch).
1212
required: false
13-
default: ''
13+
default: ""
1414
head:
1515
description: Scan commits until here (usually dev branch).
1616
required: false
1717
extra_args:
18-
default: ''
18+
default: ""
1919
description: Extra args to be passed to the trufflehog cli.
2020
required: false
2121
version:
22-
default: 'latest'
22+
default: "latest"
2323
description: Scan with this trufflehog cli version.
2424
required: false
2525
branding:
@@ -29,71 +29,78 @@ branding:
2929
runs:
3030
using: "composite"
3131
steps:
32-
- shell: bash
33-
working-directory: ${{ inputs.path }}
34-
env:
35-
BASE: ${{ inputs.base }}
36-
HEAD: ${{ inputs.head }}
37-
ARGS: ${{ inputs.extra_args }}
38-
COMMIT_IDS: ${{ toJson(github.event.commits.*.id) }}
39-
VERSION: ${{ inputs.version }}
40-
run: |
41-
##########################################
42-
## ADVANCED USAGE ##
43-
## Scan by BASE & HEAD user inputs ##
44-
## If BASE == HEAD, exit with error ##
45-
##########################################
46-
git status >/dev/null # make sure we are in a git repository
47-
if [ -n "$BASE" ] || [ -n "$HEAD" ]; then
48-
if [ -n "$BASE" ]; then
49-
base_commit=$(git rev-parse "$BASE" 2>/dev/null) || true
50-
else
51-
base_commit=""
52-
fi
53-
if [ -n "$HEAD" ]; then
54-
head_commit=$(git rev-parse "$HEAD" 2>/dev/null) || true
55-
else
56-
head_commit=""
32+
- shell: bash
33+
working-directory: ${{ inputs.path }}
34+
env:
35+
BASE: ${{ inputs.base }}
36+
HEAD: ${{ inputs.head }}
37+
ARGS: ${{ inputs.extra_args }}
38+
COMMIT_IDS: ${{ toJson(github.event.commits.*.id) }}
39+
VERSION: ${{ inputs.version }}
40+
run: |
41+
##########################################
42+
## ADVANCED USAGE ##
43+
## Scan by BASE & HEAD user inputs ##
44+
## If BASE == HEAD, exit with error ##
45+
##########################################
46+
# Check if jq is installed, if not, install it
47+
if ! command -v jq &> /dev/null
48+
then
49+
echo "jq could not be found, installing..."
50+
apt-get -y update && apt-get install -y jq
5751
fi
58-
if [ "$base_commit" == "$head_commit" ] ; then
59-
echo "::error::BASE and HEAD commits are the same. TruffleHog won't scan anything. Please see documentation (https://github.com/trufflesecurity/trufflehog#octocat-trufflehog-github-action)."
60-
exit 1
61-
fi
62-
##########################################
63-
## Scan commits based on event type ##
64-
##########################################
65-
else
66-
if [ "${{ github.event_name }}" == "push" ]; then
67-
COMMIT_LENGTH=$(printenv COMMIT_IDS | jq length)
68-
if [ $COMMIT_LENGTH == "0" ]; then
69-
echo "No commits to scan"
70-
exit 0
52+
53+
git status >/dev/null # make sure we are in a git repository
54+
if [ -n "$BASE" ] || [ -n "$HEAD" ]; then
55+
if [ -n "$BASE" ]; then
56+
base_commit=$(git rev-parse "$BASE" 2>/dev/null) || true
57+
else
58+
base_commit=""
7159
fi
72-
HEAD=${{ github.event.after }}
73-
if [ ${{ github.event.before }} == "0000000000000000000000000000000000000000" ]; then
74-
BASE=""
60+
if [ -n "$HEAD" ]; then
61+
head_commit=$(git rev-parse "$HEAD" 2>/dev/null) || true
7562
else
76-
BASE=${{ github.event.before }}
63+
head_commit=""
64+
fi
65+
if [ "$base_commit" == "$head_commit" ] ; then
66+
echo "::error::BASE and HEAD commits are the same. TruffleHog won't scan anything. Please see documentation (https://github.com/trufflesecurity/trufflehog#octocat-trufflehog-github-action)."
67+
exit 1
68+
fi
69+
##########################################
70+
## Scan commits based on event type ##
71+
##########################################
72+
else
73+
if [ "${{ github.event_name }}" == "push" ]; then
74+
COMMIT_LENGTH=$(printenv COMMIT_IDS | jq length)
75+
if [ $COMMIT_LENGTH == "0" ]; then
76+
echo "No commits to scan"
77+
exit 0
78+
fi
79+
HEAD=${{ github.event.after }}
80+
if [ ${{ github.event.before }} == "0000000000000000000000000000000000000000" ]; then
81+
BASE=""
82+
else
83+
BASE=${{ github.event.before }}
84+
fi
85+
elif [ "${{ github.event_name }}" == "workflow_dispatch" ] || [ "${{ github.event_name }}" == "schedule" ]; then
86+
BASE=""
87+
HEAD=""
88+
elif [ "${{ github.event_name }}" == "pull_request" ]; then
89+
BASE=${{github.event.pull_request.base.sha}}
90+
HEAD=${{github.event.pull_request.head.sha}}
7791
fi
78-
elif [ "${{ github.event_name }}" == "workflow_dispatch" ] || [ "${{ github.event_name }}" == "schedule" ]; then
79-
BASE=""
80-
HEAD=""
81-
elif [ "${{ github.event_name }}" == "pull_request" ]; then
82-
BASE=${{github.event.pull_request.base.sha}}
83-
HEAD=${{github.event.pull_request.head.sha}}
8492
fi
85-
fi
86-
##########################################
87-
## Run TruffleHog ##
88-
##########################################
89-
docker run --rm -v .:/tmp -w /tmp \
90-
ghcr.io/trufflesecurity/trufflehog:${VERSION} \
91-
git file:///tmp/ \
92-
--since-commit \
93-
${BASE:-''} \
94-
--branch \
95-
${HEAD:-''} \
96-
--fail \
97-
--no-update \
98-
--github-actions \
99-
${ARGS:-''}
93+
##########################################
94+
## Run TruffleHog ##
95+
##########################################
96+
docker run --rm -v .:/tmp -w /tmp \
97+
ghcr.io/trufflesecurity/trufflehog:${VERSION} \
98+
git file:///tmp/ \
99+
--since-commit \
100+
${BASE:-''} \
101+
--branch \
102+
${HEAD:-''} \
103+
--fail \
104+
--no-update \
105+
--github-actions \
106+
${ARGS:-''}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ require (
7373
github.com/marusama/semaphore/v2 v2.5.0
7474
github.com/mattn/go-isatty v0.0.20
7575
github.com/mholt/archives v0.0.0-20241216060121-23e0af8fe73d
76-
github.com/microsoft/go-mssqldb v1.8.0
76+
github.com/microsoft/go-mssqldb v1.8.2
7777
github.com/mitchellh/go-ps v1.0.0
7878
github.com/muesli/reflow v0.3.0
7979
github.com/patrickmn/go-cache v2.1.0+incompatible

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
543543
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
544544
github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
545545
github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
546+
github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I=
547+
github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo=
546548
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
547549
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
548550
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=

pkg/detectors/heroku/v1/heroku.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package heroku
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"strings"
9+
10+
regexp "github.com/wasilibs/go-re2"
11+
12+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
13+
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
14+
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
15+
)
16+
17+
type Scanner struct{}
18+
19+
// Ensure the Scanner satisfies the interface at compile time.
20+
var _ detectors.Detector = (*Scanner)(nil)
21+
var _ detectors.Versioner = (*Scanner)(nil)
22+
23+
var (
24+
client = common.SaneHttpClient()
25+
26+
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
27+
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"heroku"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`)
28+
)
29+
30+
func (s Scanner) Version() int {
31+
return 1
32+
}
33+
34+
// Keywords are used for efficiently pre-filtering chunks.
35+
// Use identifiers in the secret preferably, or the provider name.
36+
func (s Scanner) Keywords() []string {
37+
return []string{"heroku"}
38+
}
39+
40+
// FromData will find and optionally verify Heroku secrets in a given set of bytes.
41+
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
42+
dataStr := string(data)
43+
44+
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
45+
46+
for _, match := range matches {
47+
resMatch := strings.TrimSpace(match[1])
48+
49+
s1 := detectors.Result{
50+
DetectorType: detectorspb.DetectorType_Heroku,
51+
Raw: []byte(resMatch),
52+
}
53+
54+
if verify {
55+
isVerified, verificationErr := VerifyHerokuAPIKey(ctx, client, resMatch)
56+
s1.Verified = isVerified
57+
s1.SetVerificationError(verificationErr)
58+
}
59+
60+
results = append(results, s1)
61+
}
62+
63+
return results, nil
64+
}
65+
66+
func (s Scanner) Type() detectorspb.DetectorType {
67+
return detectorspb.DetectorType_Heroku
68+
}
69+
70+
func (s Scanner) Description() string {
71+
return "Heroku is a cloud platform that lets companies build, deliver, monitor and scale apps. Heroku API keys can be used to access and manage Heroku applications and services."
72+
}
73+
74+
func VerifyHerokuAPIKey(ctx context.Context, client *http.Client, key string) (bool, error) {
75+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.heroku.com/account", http.NoBody)
76+
if err != nil {
77+
return false, err
78+
}
79+
80+
req.Header.Add("Accept", "application/vnd.heroku+json; version=3")
81+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
82+
83+
resp, err := client.Do(req)
84+
if err != nil {
85+
return false, err
86+
}
87+
88+
defer func() {
89+
_, _ = io.Copy(io.Discard, resp.Body)
90+
_ = resp.Body.Close()
91+
}()
92+
93+
switch resp.StatusCode {
94+
case http.StatusOK:
95+
return true, nil
96+
case http.StatusUnauthorized:
97+
return false, nil
98+
default:
99+
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
100+
}
101+
}

pkg/detectors/heroku/heroku_test.go renamed to pkg/detectors/heroku/v1/heroku_test.go

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,6 @@ import (
1010
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
1111
)
1212

13-
var (
14-
validPattern = `[{
15-
"_id": "1a8d0cca-e1a9-4318-bc2f-f5658ab2dcb5",
16-
"name": "Heroku",
17-
"type": "Detector",
18-
"api": true,
19-
"authentication_type": "",
20-
"verification_url": "https://api.example.com/example",
21-
"test_secrets": {
22-
"heroku_secret": "bAf8bA7d-7088-07ce-3f87-7ec21653297d"
23-
},
24-
"expected_response": "200",
25-
"method": "GET",
26-
"deprecated": false
27-
}]`
28-
secret = "bAf8bA7d-7088-07ce-3f87-7ec21653297d"
29-
)
30-
3113
func TestHeroku_Pattern(t *testing.T) {
3214
d := Scanner{}
3315
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
@@ -38,9 +20,22 @@ func TestHeroku_Pattern(t *testing.T) {
3820
want []string
3921
}{
4022
{
41-
name: "valid pattern",
42-
input: validPattern,
43-
want: []string{secret},
23+
name: "valid pattern",
24+
input: `[{
25+
"_id": "1a8d0cca-e1a9-4318-bc2f-f5658ab2dcb5",
26+
"name": "Heroku",
27+
"type": "Detector",
28+
"api": true,
29+
"authentication_type": "",
30+
"verification_url": "https://api.example.com/example",
31+
"test_secrets": {
32+
"heroku_secret": "bAf8bA7d-7088-07ce-3f87-7ec21653297d"
33+
},
34+
"expected_response": "200",
35+
"method": "GET",
36+
"deprecated": false
37+
}]`,
38+
want: []string{"bAf8bA7d-7088-07ce-3f87-7ec21653297d"},
4439
},
4540
}
4641

pkg/detectors/heroku/heroku.go renamed to pkg/detectors/heroku/v2/heroku.go

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,37 @@ package heroku
22

33
import (
44
"context"
5-
"fmt"
6-
"net/http"
75
"strings"
86

97
regexp "github.com/wasilibs/go-re2"
108

119
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1210
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
11+
v1 "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/heroku/v1"
1312
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
1413
)
1514

1615
type Scanner struct{}
1716

1817
// Ensure the Scanner satisfies the interface at compile time.
1918
var _ detectors.Detector = (*Scanner)(nil)
19+
var _ detectors.Versioner = (*Scanner)(nil)
2020

2121
var (
2222
client = common.SaneHttpClient()
2323

2424
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
25-
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"heroku"}) + `\b([0-9Aa-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`)
25+
keyPat = regexp.MustCompile(`\b(HRKU-AA[0-9a-zA-Z_-]{58})\b`)
2626
)
2727

28+
func (s Scanner) Version() int {
29+
return 2
30+
}
31+
2832
// Keywords are used for efficiently pre-filtering chunks.
2933
// Use identifiers in the secret preferably, or the provider name.
3034
func (s Scanner) Keywords() []string {
31-
return []string{"heroku"}
35+
return []string{"HRKU-AA"}
3236
}
3337

3438
// FromData will find and optionally verify Heroku secrets in a given set of bytes.
@@ -46,19 +50,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
4650
}
4751

4852
if verify {
49-
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.heroku.com/apps", nil)
50-
if err != nil {
51-
continue
52-
}
53-
req.Header.Add("Accept", "application/vnd.heroku+json; version=3")
54-
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
55-
res, err := client.Do(req)
56-
if err == nil {
57-
defer res.Body.Close()
58-
if res.StatusCode >= 200 && res.StatusCode < 300 {
59-
s1.Verified = true
60-
}
61-
}
53+
isVerified, verificationErr := v1.VerifyHerokuAPIKey(ctx, client, resMatch)
54+
s1.Verified = isVerified
55+
s1.SetVerificationError(verificationErr)
6256
}
6357

6458
results = append(results, s1)

0 commit comments

Comments
 (0)