Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/AkihiroSuda/gomodjail
go 1.24.0

require (
github.com/AkihiroSuda/gomoddirectivecomments v0.1.0
github.com/AkihiroSuda/gosocialcheck v0.0.4
github.com/elastic/go-seccomp-bpf v1.6.0
github.com/spf13/cobra v1.10.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/AkihiroSuda/gomoddirectivecomments v0.1.0 h1:5sKxYIkq9GGs0DTnuPNVm2Z/LmhKdTN+8QblThzTKqg=
github.com/AkihiroSuda/gomoddirectivecomments v0.1.0/go.mod h1:flXOhVLWfsi4FuFhFoc9F3m7wAH2RT4aM3fVyQROKt4=
github.com/AkihiroSuda/gosocialcheck v0.0.4 h1:DSeLoaG7jgjbscgFfyRzdLQprpTnomVxJF7pRAdjRnw=
github.com/AkihiroSuda/gosocialcheck v0.0.4/go.mod h1:DNDBTlEkGah2wiGC50XNFPETBNiN1o2c15w9ziECOtM=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
Expand Down
122 changes: 11 additions & 111 deletions pkg/profile/fromgomod/fromgomod.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,128 +4,28 @@ import (
"fmt"
"log/slog"
"slices"
"strings"

"github.com/AkihiroSuda/gomoddirectivecomments"
"golang.org/x/mod/modfile"

"github.com/AkihiroSuda/gomodjail/pkg/profile"
)

func FromGoMod(mod *modfile.File, prof *profile.Profile) error {
prof.Module = mod.Module.Mod.Path
currentDefaultPolicy := profile.PolicyUnconfined

for _, c := range append(mod.Module.Syntax.Before, mod.Module.Syntax.Suffix...) {
if tok := c.Token; tok != "" {
pol, err := policyFromComment(tok)
if err != nil {
err = fmt.Errorf("failed to parse comment %+v: %w", c, err)
return err
}
currentDefaultPolicy = pol
}
var err error
parsedPolicies, err := gomoddirectivecomments.Parse(mod, "gomodjail", profile.PolicyUnconfined)
if err != nil {
return fmt.Errorf("failed to parse Go module directive comments: %w", err)
}

for _, c := range append(mod.Go.Syntax.Before, mod.Go.Syntax.Suffix...) {
if tok := c.Token; tok != "" {
pol, err := policyFromComment(tok)
if err != nil {
err = fmt.Errorf("failed to parse comment %+v: %w", c, err)
return err
}
return fmt.Errorf("policy %q is specified in an invalid position", pol)
for modPath, pol := range parsedPolicies {
if !slices.Contains(profile.KnownPolicies, pol) {
return fmt.Errorf("module %q: unknown policy %q", modPath, pol)
}
}

for _, f := range mod.Require {
if syn := f.Syntax; syn != nil {
pol := currentDefaultPolicy
if syn.InBlock {
// TODO: cache line blocks
if lineBlock := findLineBlock(mod.Syntax.Stmt, syn); lineBlock != nil {
lineBlockPol, err := policyFromLineBlock(lineBlock)
if err != nil {
err = fmt.Errorf("failed to parse line block %+v: %w", lineBlock, err)
return err
}
if lineBlockPol != "" {
pol = lineBlockPol
}
}
}
for _, c := range append(syn.Before, syn.Suffix...) {
if tok := c.Token; tok != "" {
polFromComment, err := policyFromComment(tok)
if err != nil {
err = fmt.Errorf("failed to parse comment %+v: %w", c, err)
return err
}
if polFromComment != "" {
pol = polFromComment
}
}
}
if pol == "" {
pol = currentDefaultPolicy
}
if pol == profile.PolicyUnconfined {
pol = "" // reduce map size
}
if existPol, ok := prof.Modules[f.Mod.Path]; ok && existPol != pol {
slog.Warn("Overwriting an existing policy", "module", f.Mod.Path, "old", existPol, "new", pol)
}
if pol == "" {
delete(prof.Modules, f.Mod.Path)
} else {
prof.Modules[f.Mod.Path] = pol
}
if existPol, ok := prof.Modules[modPath]; ok && existPol != pol {
slog.Warn("Overwriting an existing policy", "module", modPath, "old", existPol, "new", pol)
}
}
prof.Modules = parsedPolicies
return nil
}

func policyFromComment(token string) (string, error) {
token = strings.TrimPrefix(token, "//")
// TODO: support /* ... */
for _, f := range strings.Fields(token) {
f = strings.TrimPrefix(f, "//")
if strings.HasPrefix(f, "gomodjail:") {
pol := profile.Policy(strings.TrimPrefix(f, "gomodjail:"))
if !slices.Contains(profile.KnownPolicies, pol) {
return pol, fmt.Errorf("unknown policy %q", pol)
}
return pol, nil
}
}
return "", nil
}

func findLineBlock(exprs []modfile.Expr, line modfile.Expr) *modfile.LineBlock {
start, end := line.Span()
for _, expr := range exprs {
lb, ok := expr.(*modfile.LineBlock)
if !ok {
continue
}
lbStart, lbEnd := lb.Span()
if start.Line >= lbStart.Line && end.Line <= lbEnd.Line {
return lb
}
}
return nil
}

func policyFromLineBlock(lb *modfile.LineBlock) (string, error) {
for _, c := range append(lb.Before, lb.Suffix...) {
if tok := c.Token; tok != "" {
pol, err := policyFromComment(tok)
if err != nil {
return "", err
}
if pol != "" {
return pol, nil
}
}
}
return "", nil
}