[engine] Fix gosec
The gosec check was previously failing silently (producing a retcode of
zero) because it was run in a sandbox without the necessary environment
variables set (specifically `GOPACKAGESDRIVER=off` needed to be set for
it to work inside the sandbox).
After fixing the check there were a bunch of fixes required, mostly
related to error propagation.
Change-Id: Ib6ff1ae370d3e07fb9c63cb2bfddf907a526955f
Reviewed-on: https://fuchsia-review.googlesource.com/c/shac-project/shac/+/922774
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Fuchsia-Auto-Submit: Oliver Newman <olivernewman@google.com>
Reviewed-by: Anthony Fandrianto <atyfto@google.com>
diff --git a/checks/go.star b/checks/go.star
index 517bf01..5cf216b 100644
--- a/checks/go.star
+++ b/checks/go.star
@@ -41,7 +41,14 @@
gofmt = shac.check(_gofmt, formatter = True)
-def _gosec(ctx, version = "v2.15.0", level = "error"):
+def _gosec(ctx, version = "v2.15.0", level = "error", exclude = [
+ # shac checks are allowed to run arbitrary subprocesses, so it's common for
+ # shac's source code to run non-constant subcommands.
+ "G204",
+ # shac checks are allowed to read arbitrary files, so it's common for shac's
+ # source code to read non-constant files.
+ "G304",
+]):
"""Runs gosec on a Go code base.
See https://github.com/securego/gosec for more details.
@@ -52,29 +59,43 @@
be rolled from time to time.
level: level at which issues should be emitted.
"""
+ affected_files = set(ctx.scm.affected_files())
exe = go_install(ctx, "github.com/securego/gosec/v2/cmd/gosec", version)
- res = ctx.os.exec([exe, "-fmt=json", "-quiet", "-exclude=G304", "-exclude-dir=.tools", "./..."], raise_on_failure = False).wait()
- if res.retcode:
- # Schema is https://pkg.go.dev/github.com/securego/gosec/v2#ReportInfo
- d = json.decode(res.stdout)
- o = len(ctx.scm.root) + 1
- for file, data in d["Golang errors"]:
+ res = ctx.os.exec(
+ [exe, "-fmt=json", "-quiet", "-exclude=%s" % ",".join(exclude), "-exclude-dir=.tools", "./..."],
+ ok_retcodes = (0, 1),
+ env = go_env(ctx, "gosec"),
+ ).wait()
+ if not res.retcode:
+ return
+
+ # Schema is https://pkg.go.dev/github.com/securego/gosec/v2#ReportInfo
+ d = json.decode(res.stdout)
+ o = len(ctx.scm.root) + 1
+ for file, errs in d["Golang errors"].items():
+ filepath = file[o:]
+ if filepath not in affected_files:
+ continue
+ for e in errs:
ctx.emit.finding(
level = "error",
- message = i["error"],
- filepath = file[o:],
- line = int(i["line"]),
- col = int(i["column"]),
+ message = e["error"],
+ filepath = filepath,
+ line = int(e["line"]),
+ col = int(e["column"]),
)
- for i in d["Issues"]:
- line = i["line"].split("-")[0]
- ctx.emit.finding(
- level = level,
- message = i["rule_id"] + ": " + i["details"],
- filepath = i["file"][o:],
- line = int(line),
- col = int(i["column"]),
- )
+ for i in d["Issues"]:
+ line = i["line"].split("-")[0]
+ filepath = i["file"][o:]
+ if filepath not in affected_files:
+ continue
+ ctx.emit.finding(
+ level = level,
+ message = i["rule_id"] + ": " + i["details"],
+ filepath = filepath,
+ line = int(line),
+ col = int(i["column"]),
+ )
gosec = shac.check(_gosec)
diff --git a/internal/cli/check.go b/internal/cli/check.go
index b88e5c7..34d81f6 100644
--- a/internal/cli/check.go
+++ b/internal/cli/check.go
@@ -63,7 +63,7 @@
}
if c.jsonOutput != "" {
- if err2 := os.WriteFile(c.jsonOutput, buf.Bytes(), 0o666); err == nil {
+ if err2 := os.WriteFile(c.jsonOutput, buf.Bytes(), 0o600); err == nil {
err = err2
}
}
diff --git a/internal/engine/pkg.go b/internal/engine/pkg.go
index 82ddaa0..e770b05 100644
--- a/internal/engine/pkg.go
+++ b/internal/engine/pkg.go
@@ -193,7 +193,7 @@
}
parentdir := filepath.Dir(depdir)
- if err = os.MkdirAll(parentdir, 0o777); err != nil {
+ if err = os.MkdirAll(parentdir, 0o700); err != nil {
return nil, err
}
if err = p.gitCommand(ctx, parentdir, "clone", fullURL, filepath.Base(depdir)); err != nil {
diff --git a/internal/engine/run.go b/internal/engine/run.go
index 705c07c..fa73f5b 100644
--- a/internal/engine/run.go
+++ b/internal/engine/run.go
@@ -576,8 +576,7 @@
return errors.New("did you forget to call shac.register_check?")
}
// Last phase where checks are called.
- s.bufferAllChecks(ctx, ch)
- return nil
+ return s.bufferAllChecks(ctx, ch)
}
// parse parses a single shac.star file.
@@ -604,8 +603,12 @@
}
// bufferAllChecks adds all the checks to the channel for execution.
-func (s *shacState) bufferAllChecks(ctx context.Context, ch chan<- func() error) {
- args := starlark.Tuple{getCtx(path.Join(s.root, s.subdir), s.vars)}
+func (s *shacState) bufferAllChecks(ctx context.Context, ch chan<- func() error) error {
+ shacCtx, err := getCtx(path.Join(s.root, s.subdir), s.vars)
+ if err != nil {
+ return err
+ }
+ args := starlark.Tuple{shacCtx}
args.Freeze()
for i := range s.checks {
if s.filter != nil && !s.filter(s.checks[i]) {
@@ -631,6 +634,7 @@
return err
}
}
+ return nil
}
func (s *shacState) newTempDir() (string, error) {
@@ -699,10 +703,10 @@
var err error
for _, proc := range c.subprocesses {
if !proc.waitCalled {
- proc.cleanup()
if err == nil {
err = fmt.Errorf("wait() was not called on %s", proc.String())
}
+ _ = proc.cleanup()
}
}
return err
diff --git a/internal/engine/runtime_ctx.go b/internal/engine/runtime_ctx.go
index 0cbf6ca..d9f48b6 100644
--- a/internal/engine/runtime_ctx.go
+++ b/internal/engine/runtime_ctx.go
@@ -23,10 +23,12 @@
// getCtx returns the ctx object to pass to a registered check callback.
//
// Make sure to update //doc/stdlib.star whenever this function is modified.
-func getCtx(root string, vars map[string]string) starlark.Value {
+func getCtx(root string, vars map[string]string) (starlark.Value, error) {
starlarkVars := starlark.NewDict(len(vars))
for k, v := range vars {
- starlarkVars.SetKey(starlark.String(k), starlark.String(v))
+ if err := starlarkVars.SetKey(starlark.String(k), starlark.String(v)); err != nil {
+ return nil, err
+ }
}
return toValue("ctx", starlark.StringDict{
// Implemented in runtime_ctx_emit.go
@@ -61,5 +63,5 @@
"vars": toValue("ctx.vars", starlark.StringDict{
"get": newBuiltin("ctx.vars.get", ctxVarsGet),
}),
- })
+ }), nil
}
diff --git a/internal/engine/runtime_ctx_os.go b/internal/engine/runtime_ctx_os.go
index 8189bb6..115335e 100644
--- a/internal/engine/runtime_ctx_os.go
+++ b/internal/engine/runtime_ctx_os.go
@@ -199,7 +199,7 @@
// Ignore errors during cleanup because cleanupFuncs will only be
// populated if another error occurred prior to starting the
// subprocess.
- f()
+ _ = f()
}
}()
diff --git a/internal/reporting/multi.go b/internal/reporting/multi.go
index d8fd3af..c7ef18b 100644
--- a/internal/reporting/multi.go
+++ b/internal/reporting/multi.go
@@ -43,14 +43,14 @@
}
func (t *MultiReport) CheckCompleted(ctx context.Context, check string, start time.Time, d time.Duration, level engine.Level, err error) {
- t.do(func(r Report) error {
+ _ = t.do(func(r Report) error {
r.CheckCompleted(ctx, check, start, d, level, err)
return nil
})
}
func (t *MultiReport) Print(ctx context.Context, check, file string, line int, message string) {
- t.do(func(r Report) error {
+ _ = t.do(func(r Report) error {
r.Print(ctx, check, file, line, message)
return nil
})
diff --git a/internal/sandbox/sandbox.go b/internal/sandbox/sandbox.go
index 2db044c..6041e65 100644
--- a/internal/sandbox/sandbox.go
+++ b/internal/sandbox/sandbox.go
@@ -138,7 +138,6 @@
}
args = append(args, "--")
args = append(args, config.Cmd...)
- //#nosec G204
return exec.CommandContext(ctx, s.nsjailPath, args...)
}