Skip to content

Commit d633598

Browse files
author
Евгений Балякин
committed
fix in style
1 parent 2d2b382 commit d633598

6 files changed

Lines changed: 115 additions & 4 deletions

File tree

cmd/baseline.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ func newBaselineCommand(stdout io.Writer, stderr io.Writer, exitCode *int) *cobr
4545
}
4646

4747
func runBaselineInitConfig(config baselineInitConfig, stdout io.Writer, stderr io.Writer) int {
48+
if scanIsRunningAsRoot() {
49+
return stopRootScan(stderr)
50+
}
51+
4852
database, err := matcher.LoadDefaultDatabase()
4953
if err != nil {
5054
fmt.Fprintln(stderr, err)

cmd/scan.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func addScanFlags(command *cobra.Command, config *scanConfig) {
7474
}
7575

7676
func runScanConfig(config scanConfig, stdout io.Writer, stderr io.Writer, version string) int {
77+
if scanIsRunningAsRoot() {
78+
return stopRootScan(stderr)
79+
}
80+
7781
database, err := matcher.LoadDefaultDatabase()
7882
if err != nil {
7983
fmt.Fprintln(stderr, err)

cmd/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cmd
22

33
import (
4+
"fmt"
5+
"io"
46
"os"
57
"path/filepath"
68
"strings"
@@ -18,8 +20,14 @@ const (
1820
exitHigh = 2
1921
exitRuntime = 3
2022
exitUsage = 4
23+
24+
rootUserID = 0
25+
rootScanWarning = "sudocheck should not be run as root."
26+
rootScanGuidance = "Please run sudocheck as the unprivileged user you want to audit; no checks were performed."
2127
)
2228

29+
var getEffectiveUserID = os.Geteuid
30+
2331
type stringList []string
2432

2533
func (values *stringList) String() string {
@@ -49,6 +57,16 @@ func exitForFindings(findings []model.Finding, failOn model.Severity) int {
4957
return exitHigh
5058
}
5159

60+
func scanIsRunningAsRoot() bool {
61+
return getEffectiveUserID() == rootUserID
62+
}
63+
64+
func stopRootScan(stderr io.Writer) int {
65+
fmt.Fprintln(stderr, rootScanWarning)
66+
fmt.Fprintln(stderr, rootScanGuidance)
67+
return exitUsage
68+
}
69+
5270
func inferFormat(format string, jsonOutput bool, sarifOutput bool, reportPath string) string {
5371
if jsonOutput {
5472
return formatJSON

cmd/types_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"bytes"
5+
"strings"
56
"testing"
67

78
"github.com/balyakin/sudocheck/internal/model"
@@ -35,3 +36,54 @@ func TestExitForFindingsFailsOnMedium(t *testing.T) {
3536
t.Fatal("expected non-zero exit code")
3637
}
3738
}
39+
40+
func TestRunScanConfigRefusesRootUser(t *testing.T) {
41+
runAsRootForTest(t)
42+
43+
stdout := bytes.Buffer{}
44+
stderr := bytes.Buffer{}
45+
46+
exitCode := runScanConfig(scanConfig{}, &stdout, &stderr, "dev")
47+
48+
if exitCode != exitUsage {
49+
t.Fatalf("expected usage exit code, got %d", exitCode)
50+
}
51+
if stdout.String() != "" {
52+
t.Fatalf("expected empty stdout, got %q", stdout.String())
53+
}
54+
if !strings.Contains(stderr.String(), rootScanGuidance) {
55+
t.Fatalf("expected root guidance in stderr, got %q", stderr.String())
56+
}
57+
}
58+
59+
func TestRunBaselineInitConfigRefusesRootUser(t *testing.T) {
60+
runAsRootForTest(t)
61+
62+
stdout := bytes.Buffer{}
63+
stderr := bytes.Buffer{}
64+
config := baselineInitConfig{output: "sudocheck.baseline.json"}
65+
66+
exitCode := runBaselineInitConfig(config, &stdout, &stderr)
67+
68+
if exitCode != exitUsage {
69+
t.Fatalf("expected usage exit code, got %d", exitCode)
70+
}
71+
if stdout.String() != "" {
72+
t.Fatalf("expected empty stdout, got %q", stdout.String())
73+
}
74+
if !strings.Contains(stderr.String(), rootScanGuidance) {
75+
t.Fatalf("expected root guidance in stderr, got %q", stderr.String())
76+
}
77+
}
78+
79+
func runAsRootForTest(t *testing.T) {
80+
t.Helper()
81+
82+
originalGetEffectiveUserID := getEffectiveUserID
83+
getEffectiveUserID = func() int {
84+
return rootUserID
85+
}
86+
t.Cleanup(func() {
87+
getEffectiveUserID = originalGetEffectiveUserID
88+
})
89+
}

internal/reporter/terminal.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ var (
1414
bannerStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("14"))
1515
criticalStyle = lipgloss.NewStyle().
1616
Bold(true).
17-
Foreground(lipgloss.Color("15")).
18-
Background(lipgloss.Color("9"))
17+
Foreground(lipgloss.Color("9")).
18+
Inline(true)
1919
highStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9"))
2020
mediumStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("11"))
2121
lowStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("14"))
@@ -70,7 +70,9 @@ func renderFinding(finding model.Finding, options Options) string {
7070
builder := strings.Builder{}
7171
header := fmt.Sprintf("%s %s — %s %s (%s)", severityIcon(finding.Severity),
7272
strings.ToUpper(string(finding.Severity)), finding.Source, finding.BinaryName, finding.Detail)
73-
builder.WriteString(renderStyled(header+"\n", severityStyle(finding.Severity), options))
73+
styledHeader := renderStyled(header, severityStyle(finding.Severity), options)
74+
builder.WriteString(strings.TrimLeft(styledHeader, " \t"))
75+
builder.WriteString("\n")
7476
builder.WriteString("┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄\n")
7577
if len(finding.Exploits) > 0 && !options.HideExploits {
7678
builder.WriteString(fmt.Sprintf("Exploit: %s\n", finding.Exploits[0].Command))
@@ -140,5 +142,5 @@ func renderStyled(value string, style lipgloss.Style, options Options) string {
140142
if options.NoColor {
141143
return value
142144
}
143-
return style.Render(value)
145+
return strings.TrimRight(style.Render(value), " \t")
144146
}

internal/reporter/terminal_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,34 @@ func TestRenderTerminalMatchesSpecFindingShape(t *testing.T) {
4848
}
4949
}
5050
}
51+
52+
func TestRenderTerminalCriticalFindingHasNoLeadingPadding(t *testing.T) {
53+
report := model.Report{
54+
Version: "v0.1.0",
55+
Summary: model.Summary{
56+
Critical: 1,
57+
Total: 1,
58+
},
59+
Findings: []model.Finding{
60+
{
61+
Severity: model.SeverityCritical,
62+
Source: "sudo",
63+
BinaryName: "all",
64+
Detail: "PASSWD, run as ALL : ALL",
65+
},
66+
},
67+
}
68+
69+
output := RenderTerminal(report, Options{})
70+
71+
for _, line := range strings.Split(output, "\n") {
72+
if strings.Contains(line, "CRITICAL —") {
73+
if strings.HasPrefix(line, " ") {
74+
t.Fatalf("expected critical finding line without leading padding, got %q", line)
75+
}
76+
return
77+
}
78+
}
79+
80+
t.Fatalf("expected critical finding line, got:\n%s", output)
81+
}

0 commit comments

Comments
 (0)