* added octopus deploy detector * chore: feature flag gating, regen protos * chore: add SecretParts, fix integration test * chore: refactors tests, updated http client and fix redacted token length for more visibility
155 lines
3.4 KiB
Go
155 lines
3.4 KiB
Go
package octopusdeploy
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"net/url"
|
||
|
||
regexp "github.com/wasilibs/go-re2"
|
||
|
||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detector_typepb"
|
||
)
|
||
|
||
type Scanner struct {
|
||
client *http.Client
|
||
detectors.DefaultMultiPartCredentialProvider
|
||
}
|
||
|
||
// Compile-time interface check
|
||
var _ detectors.Detector = (*Scanner)(nil)
|
||
|
||
var (
|
||
defaultClient = detectors.NewClientWithDedup(detectors.DetectorHttpClientWithNoLocalAddresses)
|
||
|
||
// Octopus Deploy API keys:
|
||
// Format: API- followed by 29–34 uppercase letters or digits
|
||
octopusTokenPat = regexp.MustCompile(
|
||
`\b(API-[A-Z0-9]{29,34})\b`,
|
||
)
|
||
|
||
urlPat = regexp.MustCompile(`\b([a-z0-9-]+\.octopus\.app)\b`)
|
||
)
|
||
|
||
// Keywords used for fast pre-filtering
|
||
func (s Scanner) Keywords() []string {
|
||
return []string{"octopus"}
|
||
}
|
||
|
||
func (s Scanner) getClient() *http.Client {
|
||
if s.client != nil {
|
||
return s.client
|
||
}
|
||
return defaultClient
|
||
}
|
||
|
||
// FromData scans for Octopus API tokens and optionally verifies them
|
||
func (s Scanner) FromData(
|
||
ctx context.Context,
|
||
verify bool,
|
||
data []byte,
|
||
) (results []detectors.Result, err error) {
|
||
|
||
dataStr := string(data)
|
||
|
||
uniqueTokens := make(map[string]struct{})
|
||
uniqueUrls := make(map[string]struct{})
|
||
|
||
for _, urlMatch := range urlPat.FindAllStringSubmatch(dataStr, -1) {
|
||
uniqueUrls[urlMatch[1]] = struct{}{}
|
||
}
|
||
for _, match := range octopusTokenPat.FindAllStringSubmatch(dataStr, -1) {
|
||
uniqueTokens[match[1]] = struct{}{}
|
||
}
|
||
|
||
for url := range uniqueUrls {
|
||
for token := range uniqueTokens {
|
||
result := detectors.Result{
|
||
DetectorType: detector_typepb.DetectorType_OctopusDeploy,
|
||
Raw: []byte(token),
|
||
RawV2: []byte(fmt.Sprintf("%s:%s", url, token)),
|
||
Redacted: token[:8] + "...",
|
||
SecretParts: map[string]string{
|
||
"key": token,
|
||
"url": url,
|
||
},
|
||
}
|
||
|
||
if verify {
|
||
verified, verificationErr := verifyOctopusToken(
|
||
ctx,
|
||
s.getClient(),
|
||
url,
|
||
token,
|
||
)
|
||
result.SetVerificationError(verificationErr, token)
|
||
result.Verified = verified
|
||
}
|
||
|
||
results = append(results, result)
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
func verifyOctopusToken(
|
||
ctx context.Context,
|
||
client *http.Client,
|
||
baseUrl string,
|
||
token string,
|
||
) (bool, error) {
|
||
// API REFERENCE: https://trufflesec.octopus.app/api
|
||
// DOCS: https://octopus.com/docs/octopus-rest-api
|
||
url := &url.URL{
|
||
Scheme: "https",
|
||
Host: baseUrl,
|
||
Path: "/api/users/me",
|
||
}
|
||
req, err := http.NewRequestWithContext(
|
||
ctx,
|
||
http.MethodGet,
|
||
url.String(),
|
||
http.NoBody,
|
||
)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
req.Header.Set("X-Octopus-ApiKey", token)
|
||
|
||
res, err := detectors.DoWithDedup(client, detector_typepb.DetectorType_OctopusDeploy, fmt.Sprintf("%s:%s", baseUrl, token), req)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
defer func() {
|
||
_, _ = io.Copy(io.Discard, res.Body)
|
||
_ = res.Body.Close()
|
||
}()
|
||
|
||
switch res.StatusCode {
|
||
case http.StatusOK, http.StatusForbidden:
|
||
return true, nil
|
||
|
||
case http.StatusUnauthorized:
|
||
// Invalid or revoked key
|
||
return false, nil
|
||
|
||
default:
|
||
return false, fmt.Errorf(
|
||
"unexpected HTTP response status %d",
|
||
res.StatusCode,
|
||
)
|
||
}
|
||
}
|
||
|
||
func (s Scanner) Type() detector_typepb.DetectorType {
|
||
return detector_typepb.DetectorType_OctopusDeploy
|
||
}
|
||
|
||
func (s Scanner) Description() string {
|
||
return "Octopus Deploy is a DevOps deployment automation platform. Octopus Deploy API keys can be used to automate deployments, manage projects, environments, and infrastructure resources."
|
||
}
|