216 lines
5.3 KiB
Go
216 lines
5.3 KiB
Go
// ©AngelaMos | 2026
|
|
// reporter_test.go
|
|
|
|
package reporter
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/CarterPerez-dev/portia/pkg/types"
|
|
)
|
|
|
|
func testResult() *types.ScanResult {
|
|
return &types.ScanResult{
|
|
Findings: []types.Finding{
|
|
{ //nolint:gosec
|
|
RuleID: "aws-access-key-id",
|
|
Description: "AWS Access Key ID",
|
|
Severity: types.SeverityCritical,
|
|
Match: `AKIAIOSFODNN7EXAMPLE`,
|
|
Secret: "AKIAIOSFODNN7EXAMPLE",
|
|
FilePath: "src/config.py",
|
|
LineNumber: 42,
|
|
LineContent: `aws_key = "AKIAIOSFODNN7EXAMPLE"`,
|
|
CommitSHA: "abc123def456",
|
|
Author: "dev@example.com",
|
|
HIBPStatus: types.HIBPBreached,
|
|
BreachCount: 1500,
|
|
},
|
|
{ //nolint:gosec
|
|
RuleID: "generic-password",
|
|
Description: "Password in Assignment",
|
|
Severity: types.SeverityHigh,
|
|
Match: `password = "xK9mP2vL5nQ8"`,
|
|
Secret: "xK9mP2vL5nQ8",
|
|
Entropy: 4.12,
|
|
FilePath: "config.yaml",
|
|
LineNumber: 10,
|
|
LineContent: `password = "xK9mP2vL5nQ8"`,
|
|
HIBPStatus: types.HIBPClean,
|
|
},
|
|
},
|
|
TotalFiles: 100,
|
|
TotalRules: 50,
|
|
TotalFindings: 2,
|
|
HIBPChecked: 2,
|
|
HIBPBreached: 1,
|
|
Duration: 1500 * time.Millisecond,
|
|
}
|
|
}
|
|
|
|
func TestNewReporter(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.IsType(t, &Terminal{}, New("terminal"))
|
|
assert.IsType(t, &Terminal{}, New(""))
|
|
assert.IsType(t, &JSON{}, New("json"))
|
|
assert.IsType(t, &SARIF{}, New("sarif"))
|
|
}
|
|
|
|
func TestTerminalReportWithFindings(t *testing.T) {
|
|
t.Parallel()
|
|
var buf bytes.Buffer
|
|
r := &Terminal{}
|
|
err := r.Report(&buf, testResult())
|
|
require.NoError(t, err)
|
|
|
|
output := buf.String()
|
|
assert.Contains(t, output, "2 secret(s) detected")
|
|
assert.Contains(t, output, "aws-access-key-id")
|
|
assert.Contains(t, output, "src/config.py")
|
|
assert.Contains(t, output, "FOUND IN BREACHES")
|
|
assert.Contains(t, output, "not found in breaches")
|
|
assert.Contains(t, output, "4.12")
|
|
assert.Contains(t, output, "abc123de")
|
|
}
|
|
|
|
func TestTerminalReportNoFindings(t *testing.T) {
|
|
t.Parallel()
|
|
var buf bytes.Buffer
|
|
r := &Terminal{}
|
|
result := &types.ScanResult{
|
|
TotalRules: 50,
|
|
Duration: 500 * time.Millisecond,
|
|
}
|
|
err := r.Report(&buf, result)
|
|
require.NoError(t, err)
|
|
|
|
output := buf.String()
|
|
assert.Contains(t, output, "No secrets detected")
|
|
}
|
|
|
|
func TestJSONReport(t *testing.T) {
|
|
t.Parallel()
|
|
var buf bytes.Buffer
|
|
r := &JSON{}
|
|
err := r.Report(&buf, testResult())
|
|
require.NoError(t, err)
|
|
|
|
var output jsonOutput
|
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &output))
|
|
|
|
assert.Len(t, output.Findings, 2)
|
|
assert.Equal(t, "aws-access-key-id", output.Findings[0].RuleID)
|
|
assert.Equal(t, "CRITICAL", output.Findings[0].Severity)
|
|
assert.NotContains(t, output.Findings[0].Secret, "AKIAIOSFODNN7EXAMPLE")
|
|
assert.Equal(t, 100, output.Summary.TotalFiles)
|
|
assert.Equal(t, 2, output.Summary.TotalFindings)
|
|
assert.Equal(t, 50, output.Summary.TotalRules)
|
|
}
|
|
|
|
func TestJSONReportNoFindings(t *testing.T) {
|
|
t.Parallel()
|
|
var buf bytes.Buffer
|
|
r := &JSON{}
|
|
result := &types.ScanResult{TotalRules: 50}
|
|
err := r.Report(&buf, result)
|
|
require.NoError(t, err)
|
|
|
|
var output jsonOutput
|
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &output))
|
|
assert.Empty(t, output.Findings)
|
|
}
|
|
|
|
func TestSARIFReport(t *testing.T) {
|
|
t.Parallel()
|
|
var buf bytes.Buffer
|
|
r := &SARIF{}
|
|
err := r.Report(&buf, testResult())
|
|
require.NoError(t, err)
|
|
|
|
var log sarifLog
|
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &log))
|
|
|
|
assert.Equal(t, "2.1.0", log.Version)
|
|
require.Len(t, log.Runs, 1)
|
|
|
|
run := log.Runs[0]
|
|
assert.Equal(t, "portia", run.Tool.Driver.Name)
|
|
assert.Len(t, run.Results, 2)
|
|
|
|
assert.Equal(t, "aws-access-key-id", run.Results[0].RuleID)
|
|
assert.Equal(t, "error", run.Results[0].Level)
|
|
assert.Equal(t,
|
|
"src/config.py",
|
|
run.Results[0].Locations[0].PhysicalLocation.
|
|
ArtifactLocation.URI,
|
|
)
|
|
assert.Equal(t,
|
|
42,
|
|
run.Results[0].Locations[0].PhysicalLocation.Region.StartLine,
|
|
)
|
|
}
|
|
|
|
func TestSARIFReportNoFindings(t *testing.T) {
|
|
t.Parallel()
|
|
var buf bytes.Buffer
|
|
r := &SARIF{}
|
|
result := &types.ScanResult{TotalRules: 50}
|
|
err := r.Report(&buf, result)
|
|
require.NoError(t, err)
|
|
|
|
var log sarifLog
|
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &log))
|
|
assert.Empty(t, log.Runs[0].Results)
|
|
}
|
|
|
|
func TestMaskSecret(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := map[string]struct {
|
|
input string
|
|
want string
|
|
}{
|
|
"short secret": {
|
|
input: "abc",
|
|
want: "***",
|
|
},
|
|
"medium secret": {
|
|
input: "AKIAIOSFODNN7EXA",
|
|
want: "AKIA********7EXA",
|
|
},
|
|
"long secret": { //nolint:gosec
|
|
input: "sk_live_4eC39HqLyjWDarjtT1zdp7dc",
|
|
want: "sk_liv********************zdp7dc",
|
|
},
|
|
}
|
|
|
|
for name, tc := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := maskSecret(tc.input)
|
|
assert.Equal(t, tc.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTruncateSHA(t *testing.T) {
|
|
t.Parallel()
|
|
assert.Equal(t, "abc123de", truncateSHA("abc123def456"))
|
|
assert.Equal(t, "short", truncateSHA("short"))
|
|
}
|
|
|
|
func TestSeverityToSARIF(t *testing.T) {
|
|
t.Parallel()
|
|
assert.Equal(t, "error", severityToSARIF(types.SeverityCritical))
|
|
assert.Equal(t, "error", severityToSARIF(types.SeverityHigh))
|
|
assert.Equal(t, "warning", severityToSARIF(types.SeverityMedium))
|
|
assert.Equal(t, "note", severityToSARIF(types.SeverityLow))
|
|
}
|