Skip to content

Commit ffdc620

Browse files
authored
Add unit tests for CLI orchestration, TLS config generation, and SSA cache behavior (#1536)
Signed-off-by: Cosmin Cojocar <cosmin@cojocar.ch>
1 parent c13a486 commit ffdc620

File tree

3 files changed

+420
-0
lines changed

3 files changed

+420
-0
lines changed

cmd/gosec/run_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"flag"
6+
"os"
7+
"os/exec"
8+
"testing"
9+
10+
"github.com/securego/gosec/v2/cmd/vflag"
11+
)
12+
13+
func TestRun_NoInputReturnsFailure(t *testing.T) {
14+
t.Parallel()
15+
16+
code := runInSubprocess(t, "no-input")
17+
if code != exitFailure {
18+
t.Fatalf("unexpected exit code: got %d want %d", code, exitFailure)
19+
}
20+
}
21+
22+
func TestRun_VersionReturnsSuccess(t *testing.T) {
23+
t.Parallel()
24+
25+
code := runInSubprocess(t, "version")
26+
if code != exitSuccess {
27+
t.Fatalf("unexpected exit code: got %d want %d", code, exitSuccess)
28+
}
29+
}
30+
31+
func runInSubprocess(t *testing.T, scenario string) int {
32+
t.Helper()
33+
34+
executable, err := os.Executable()
35+
if err != nil {
36+
t.Fatalf("failed to resolve test executable: %v", err)
37+
}
38+
39+
cmd := exec.Command(executable, "-test.run=^TestRunHelperProcess$")
40+
cmd.Env = append(os.Environ(), "GOSEC_RUN_HELPER=1", "GOSEC_RUN_SCENARIO="+scenario)
41+
42+
err = cmd.Run()
43+
if err == nil {
44+
return 0
45+
}
46+
47+
var exitErr *exec.ExitError
48+
if !errors.As(err, &exitErr) {
49+
t.Fatalf("failed to run helper process: %v", err)
50+
}
51+
52+
return exitErr.ExitCode()
53+
}
54+
55+
func TestRunHelperProcess(t *testing.T) {
56+
_ = t
57+
58+
if os.Getenv("GOSEC_RUN_HELPER") != "1" {
59+
return
60+
}
61+
62+
scenario := os.Getenv("GOSEC_RUN_SCENARIO")
63+
64+
flag.CommandLine = flag.NewFlagSet("gosec-helper", flag.ContinueOnError)
65+
os.Args = []string{"gosec"}
66+
67+
*flagIgnoreNoSec = false
68+
*flagShowIgnored = false
69+
*flagAlternativeNoSec = ""
70+
*flagEnableAudit = false
71+
*flagOutput = ""
72+
*flagConfig = ""
73+
*flagQuiet = true
74+
*flagRulesInclude = ""
75+
flagRulesExclude = vflag.ValidatedFlag{}
76+
*flagExcludeGenerated = false
77+
*flagLogfile = ""
78+
*flagSortIssues = true
79+
*flagBuildTags = ""
80+
*flagSeverity = "low"
81+
*flagConfidence = "low"
82+
*flagNoFail = false
83+
*flagScanTests = false
84+
*flagVersion = false
85+
*flagStdOut = false
86+
*flagColor = false
87+
*flagRecursive = false
88+
*flagVerbose = ""
89+
*flagTrackSuppressions = false
90+
*flagTerse = false
91+
*flagAiAPIProvider = ""
92+
*flagAiAPIKey = ""
93+
*flagAiBaseURL = ""
94+
*flagAiSkipSSL = false
95+
flagDirsExclude = nil
96+
97+
if scenario == "version" {
98+
*flagVersion = true
99+
}
100+
101+
os.Exit(run())
102+
}

cmd/tlsconfig/tlsconfig_test.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"net/http"
6+
"net/http/httptest"
7+
"os"
8+
"path/filepath"
9+
"reflect"
10+
"testing"
11+
)
12+
13+
func TestMapTLSVersions(t *testing.T) {
14+
t.Parallel()
15+
16+
versions := mapTLSVersions([]string{"TLSv1.3", "TLSv1", "unknown", "TLSv1.2"})
17+
expected := []int{0x0301, 0x0303, 0x0304}
18+
if !reflect.DeepEqual(versions, expected) {
19+
t.Fatalf("unexpected mapped versions: got %v want %v", versions, expected)
20+
}
21+
}
22+
23+
func TestGetTLSConfFromURL(t *testing.T) {
24+
t.Parallel()
25+
26+
t.Run("decodes valid JSON", func(t *testing.T) {
27+
t.Parallel()
28+
29+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
30+
_, _ = w.Write([]byte(`{"version": 1.0, "configurations": {"modern": {"tls_versions": ["TLSv1.3"]}}}`))
31+
}))
32+
defer srv.Close()
33+
34+
conf, err := getTLSConfFromURL(srv.URL)
35+
if err != nil {
36+
t.Fatalf("expected no error, got %v", err)
37+
}
38+
if conf == nil {
39+
t.Fatalf("expected configuration, got nil")
40+
}
41+
if conf.Version != 1.0 {
42+
t.Fatalf("unexpected version: got %v", conf.Version)
43+
}
44+
})
45+
46+
t.Run("returns error on invalid JSON", func(t *testing.T) {
47+
t.Parallel()
48+
49+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
50+
_, _ = w.Write([]byte(`{invalid`))
51+
}))
52+
defer srv.Close()
53+
54+
conf, err := getTLSConfFromURL(srv.URL)
55+
if err == nil {
56+
t.Fatalf("expected error, got nil")
57+
}
58+
if conf != nil {
59+
t.Fatalf("expected nil configuration on decode error")
60+
}
61+
})
62+
}
63+
64+
func TestGetGoCipherConfig(t *testing.T) {
65+
t.Parallel()
66+
67+
t.Run("returns error for missing named configuration", func(t *testing.T) {
68+
t.Parallel()
69+
70+
_, err := getGoCipherConfig("modern", ServerSideTLSJson{Configurations: map[string]Configuration{}})
71+
if err == nil {
72+
t.Fatalf("expected error for missing configuration")
73+
}
74+
})
75+
76+
t.Run("returns error when no TLS versions map", func(t *testing.T) {
77+
t.Parallel()
78+
79+
input := ServerSideTLSJson{
80+
Configurations: map[string]Configuration{
81+
"modern": {
82+
OpenSSLCiphersuites: []string{"TLS_AES_128_GCM_SHA256"},
83+
TLSVersions: []string{"SSLv3"},
84+
},
85+
},
86+
}
87+
88+
_, err := getGoCipherConfig("modern", input)
89+
if err == nil {
90+
t.Fatalf("expected error when TLS versions are unmapped")
91+
}
92+
})
93+
94+
t.Run("maps TLS versions and preserves IANA cipher names", func(t *testing.T) {
95+
t.Parallel()
96+
97+
input := ServerSideTLSJson{
98+
Configurations: map[string]Configuration{
99+
"modern": {
100+
OpenSSLCiphersuites: []string{"TLS_AES_128_GCM_SHA256"},
101+
TLSVersions: []string{"TLSv1.3", "TLSv1.2"},
102+
},
103+
},
104+
}
105+
106+
conf, err := getGoCipherConfig("modern", input)
107+
if err != nil {
108+
t.Fatalf("expected no error, got %v", err)
109+
}
110+
111+
if conf.Name != "Modern" {
112+
t.Fatalf("unexpected normalized name: got %q", conf.Name)
113+
}
114+
if conf.MinVersion != "0x0303" || conf.MaxVersion != "0x0304" {
115+
t.Fatalf("unexpected TLS bounds: min=%s max=%s", conf.MinVersion, conf.MaxVersion)
116+
}
117+
if len(conf.Ciphers) != 1 || conf.Ciphers[0] != "TLS_AES_128_GCM_SHA256" {
118+
t.Fatalf("unexpected ciphers: %v", conf.Ciphers)
119+
}
120+
})
121+
}
122+
123+
func TestGetCurrentDir(t *testing.T) {
124+
t.Parallel()
125+
126+
originalCommandLine := flag.CommandLine
127+
defer func() {
128+
flag.CommandLine = originalCommandLine
129+
}()
130+
131+
newFlagSet := func(args ...string) {
132+
flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError)
133+
_ = flag.CommandLine.Parse(args)
134+
}
135+
136+
t.Run("returns cwd when no args provided", func(t *testing.T) {
137+
newFlagSet()
138+
dir, err := getCurrentDir()
139+
if err != nil {
140+
t.Fatalf("expected no error, got %v", err)
141+
}
142+
143+
expected, err := filepath.Abs(".")
144+
if err != nil {
145+
t.Fatalf("failed to resolve expected abs path: %v", err)
146+
}
147+
if dir != expected {
148+
t.Fatalf("unexpected dir: got %q want %q", dir, expected)
149+
}
150+
})
151+
152+
t.Run("returns provided absolute path for single arg", func(t *testing.T) {
153+
tempDir := t.TempDir()
154+
newFlagSet(tempDir)
155+
156+
dir, err := getCurrentDir()
157+
if err != nil {
158+
t.Fatalf("expected no error, got %v", err)
159+
}
160+
161+
expected, err := filepath.Abs(tempDir)
162+
if err != nil {
163+
t.Fatalf("failed to resolve expected abs path: %v", err)
164+
}
165+
if dir != expected {
166+
t.Fatalf("unexpected dir: got %q want %q", dir, expected)
167+
}
168+
})
169+
170+
t.Run("returns error when more than one arg is provided", func(t *testing.T) {
171+
tempA := t.TempDir()
172+
tempB := filepath.Join(os.TempDir(), "another-dir")
173+
newFlagSet(tempA, tempB)
174+
175+
dir, err := getCurrentDir()
176+
if err == nil {
177+
t.Fatalf("expected error, got dir=%q", dir)
178+
}
179+
})
180+
}

0 commit comments

Comments
 (0)