Files
2026-04-05 23:53:14 +03:00

7.9 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
07-import-cicd 01 execute 1
pkg/importer/importer.go
pkg/importer/trufflehog.go
pkg/importer/trufflehog_test.go
pkg/importer/testdata/trufflehog-sample.json
true
IMP-01
truths artifacts key_links
TruffleHog v3 JSON output can be parsed into []engine.Finding
Detector names from TruffleHog are normalized to lowercase KeyHunter provider names
Verified flag in TruffleHog JSON maps to Finding.Verified + VerifyStatus
path provides contains
pkg/importer/importer.go Importer interface type Importer interface
path provides contains
pkg/importer/trufflehog.go TruffleHog v3 JSON parser func (TruffleHogImporter) Import
path provides
pkg/importer/testdata/trufflehog-sample.json Test fixture matching TruffleHog v3 JSON schema
from to via pattern
pkg/importer/trufflehog.go pkg/engine/finding.go constructs engine.Finding from TruffleHog records engine.Finding{
Create the pkg/importer package with the Importer interface and the TruffleHog v3 JSON adapter.

Purpose: External tool import requires a uniform contract so the CLI command (Plan 07-04) can dispatch by format flag. TruffleHog is the most widely used scanner; its v3 JSON output is the canonical import target (IMP-01). Output: Importer interface, TruffleHogImporter implementation, unit test, JSON fixture.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/07-import-cicd/07-CONTEXT.md @pkg/engine/finding.go From pkg/engine/finding.go: ```go type Finding struct { ProviderName string KeyValue string KeyMasked string Confidence string Source string SourceType string LineNumber int Offset int64 DetectedAt time.Time Verified bool VerifyStatus string VerifyHTTPCode int VerifyMetadata map[string]string VerifyError string } func MaskKey(key string) string ``` Task 1: Importer interface + TruffleHog parser + fixtures pkg/importer/importer.go, pkg/importer/trufflehog.go, pkg/importer/testdata/trufflehog-sample.json - Importer interface declares: Import(r io.Reader) ([]engine.Finding, error) and Name() string - TruffleHogImporter parses a JSON array of objects with fields {SourceID, SourceName, SourceMetadata (object), DetectorName, DetectorType, Verified (bool), Raw (string), Redacted (string), ExtraData (object)} - Each record maps to engine.Finding: ProviderName = normalizeTruffleHogName(DetectorName) // e.g. "OpenAI" -> "openai", "GitHubV2" -> "github", "AWS" -> "aws" KeyValue = Raw KeyMasked = engine.MaskKey(Raw) Confidence = "high" if Verified else "medium" SourceType = "import:trufflehog" Source = extractSourcePath(SourceMetadata) — traverses SourceMetadata.Data.{Git,Filesystem,Github}.file/link/repository, falls back to SourceName LineNumber = extracted from SourceMetadata.Data.Git.line if present, else 0 Verified = Verified VerifyStatus = "live" if Verified else "unverified" DetectedAt = time.Now() - normalizeTruffleHogName(): lowercase, strip trailing version digits ("GithubV2" -> "github"), map known aliases (AWS -> aws, GCP -> gcp). Unknown names: lowercased as-is. - Invalid JSON returns wrapped error - Empty array returns empty slice, nil error - Name() returns "trufflehog" Create pkg/importer/importer.go: ```go package importer
import (
    "io"

    "github.com/salvacybersec/keyhunter/pkg/engine"
)

// Importer parses output from an external secret scanner and returns
// normalized engine.Finding records. Implementations must be stateless
// and safe for reuse across calls.
type Importer interface {
    Name() string
    Import(r io.Reader) ([]engine.Finding, error)
}
```

Create pkg/importer/trufflehog.go:
- Define `type TruffleHogImporter struct{}`
- Define `type trufflehogRecord struct` with JSON tags matching TruffleHog v3: SourceID, SourceName, SourceMetadata (json.RawMessage), DetectorName, DetectorType int, Verified bool, Raw, Redacted string, ExtraData json.RawMessage.
- Implement `Name() string` returning "trufflehog".
- Implement `Import(r io.Reader) ([]engine.Finding, error)`:
  1. json.NewDecoder(r).Decode(&records) — if error, wrap: fmt.Errorf("decoding trufflehog json: %w", err)
  2. For each record: build engine.Finding per behavior spec. Skip records with empty Raw (log-skip count via return? no — just skip silently).
  3. Return slice + nil.
- Implement `normalizeTruffleHogName(detector string) string`:
  - lowercased := strings.ToLower(detector)
  - trim trailing "v\d+" via regexp (package-level var `var tfhVersionSuffix = regexp.MustCompile(`v\d+$`)`)
  - apply alias map: {"gcp": "gcp", "aws": "aws", "openai": "openai", "anthropic": "anthropic", "huggingface": "huggingface"}
  - return trimmed
- Implement `extractSourcePath(meta json.RawMessage) (path string, line int)`:
  - Unmarshal into `struct{ Data struct{ Git *struct{ File, Repository, Commit string; Line int } ; Filesystem *struct{ File string }; Github *struct{ File, Link, Repository string } } }`
  - Return first non-empty in priority: Git.File, Filesystem.File, Github.File, Github.Link, Git.Repository, Github.Repository. Line from Git.Line.
  - On unmarshal error: return "", 0 (not fatal).

Create pkg/importer/testdata/trufflehog-sample.json with a realistic fixture containing 3 records:
- record 1: DetectorName "OpenAI", Verified true, Raw "sk-proj-abcdef1234567890abcdef", SourceMetadata.Data.Git.File "src/config.py", Line 42
- record 2: DetectorName "AnthropicV2", Verified false, Raw "sk-ant-api03-xxxxxxxxxxxxxxxx", SourceMetadata.Data.Filesystem.File "/tmp/leaked.env"
- record 3: DetectorName "AWS", Verified true, Raw "AKIAIOSFODNN7EXAMPLE", SourceMetadata.Data.Github.Link "https://github.com/foo/bar/blob/main/a.yml"

Create pkg/importer/trufflehog_test.go:
- TestTruffleHogImporter_Import: open testdata, call Import, assert len==3, assert findings[0].ProviderName=="openai", Confidence=="high", Verified==true, Source=="src/config.py", LineNumber==42.
- TestTruffleHogImporter_NormalizeName: table test — {"OpenAI","openai"}, {"GithubV2","github"}, {"AnthropicV2","anthropic"}, {"AWS","aws"}, {"UnknownDetector","unknowndetector"}.
- TestTruffleHogImporter_EmptyArray: Import(strings.NewReader("[]")) returns empty slice, nil error.
- TestTruffleHogImporter_InvalidJSON: Import(strings.NewReader("not json")) returns error.
- TestTruffleHogImporter_Name: asserts "trufflehog".

All TruffleHog v3 field decisions per 07-CONTEXT.md decisions block.
cd /home/salva/Documents/apikey && go test ./pkg/importer/... -run TruffleHog -v - pkg/importer/importer.go declares Importer interface - pkg/importer/trufflehog.go implements Importer for TruffleHog v3 JSON - All 5 tests pass - go build ./pkg/importer/... succeeds go test ./pkg/importer/... -v passes. go vet ./pkg/importer/... clean.

<success_criteria> TruffleHog v3 JSON can be loaded from disk and converted to []engine.Finding with correct provider name normalization and verify status mapping. </success_criteria>

After completion, create `.planning/phases/07-import-cicd/07-01-SUMMARY.md`.