Skip to content

Commit 8359a7a

Browse files
Update for policy checks
1 parent bec13b2 commit 8359a7a

10 files changed

Lines changed: 1688 additions & 3 deletions

File tree

.goenv-policy.yaml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# yaml-language-server: $schema=./schemas/policy-schema.json
2+
# goenv SBOM Policy Configuration
3+
# Version: 1.0
4+
# Description: Example policy for validating Go project SBOMs
5+
6+
version: "1"
7+
8+
# Policy options
9+
options:
10+
fail_on_error: true
11+
fail_on_warning: false
12+
verbose: false
13+
14+
# Validation rules
15+
rules:
16+
# Supply Chain Security Rules
17+
- name: no-local-replaces
18+
type: supply-chain
19+
severity: error
20+
description: Prevent local path replace directives that bypass checksum verification
21+
check: replace-directives
22+
blocked:
23+
- local-path
24+
25+
- name: no-vendoring
26+
type: supply-chain
27+
severity: warning
28+
description: Discourage vendored dependencies (optional - adjust per org policy)
29+
check: vendoring-status
30+
blocked:
31+
- vendored
32+
33+
# Security Rules
34+
- name: block-retracted-versions
35+
type: security
36+
severity: error
37+
description: Prevent use of retracted module versions
38+
check: retracted-versions
39+
40+
- name: require-cgo-disabled
41+
type: security
42+
severity: warning
43+
description: Recommend disabling CGO for reduced attack surface
44+
check: cgo-disabled
45+
required:
46+
- "false"
47+
48+
# Completeness Rules
49+
- name: require-stdlib-component
50+
type: completeness
51+
severity: warning
52+
description: Ensure standard library component is included in SBOM
53+
check: required-components
54+
required:
55+
- golang-stdlib
56+
57+
- name: require-goenv-metadata
58+
type: completeness
59+
severity: info
60+
description: Ensure Go-aware metadata is present
61+
check: required-metadata
62+
required:
63+
- goenv:go_version
64+
- goenv:platform
65+
- goenv:build_context.goos
66+
- goenv:build_context.goarch
67+
68+
# License Compliance Rules
69+
- name: block-gpl-licenses
70+
type: license
71+
severity: error
72+
description: Block copyleft licenses (adjust per org policy)
73+
check: license-compliance
74+
blocked:
75+
- GPL-2.0
76+
- GPL-3.0
77+
- AGPL-3.0
78+
79+
- name: warn-lgpl-licenses
80+
type: license
81+
severity: warning
82+
description: Warn on LGPL licenses requiring disclosure
83+
check: license-compliance
84+
blocked:
85+
- LGPL-2.1
86+
- LGPL-3.0

.vscode/settings.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,18 @@
33
"makefile.configureOnOpen": false,
44
"go.toolsGopath": "${env:HOME}/go/tools",
55
"go.goroot": "${env:HOME}/.goenv/versions/1.23.2",
6-
"go.gopath": "${env:HOME}/go/1.23.2"
6+
"go.gopath": "${env:HOME}/go/1.23.2",
7+
"yaml.schemas": {
8+
"https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml",
9+
"https://json.schemastore.org/github-action.json": ".github/actions/*/action.yml"
10+
},
11+
"yaml.customTags": [
12+
"!Policy scalar"
13+
],
14+
"yaml.validate": true,
15+
"files.associations": {
16+
"*-policy.yaml": "yaml",
17+
".goenv-policy.yaml": "yaml",
18+
"examples/policies/*.yaml": "yaml"
19+
}
720
}

cmd/compliance/sbom.go

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func init() {
104104
sbomCmd.AddCommand(sbomProjectCmd)
105105
sbomCmd.AddCommand(sbomHashCmd)
106106
sbomCmd.AddCommand(sbomVerifyCmd)
107+
sbomCmd.AddCommand(sbomValidateCmd)
107108
cmdpkg.RootCmd.AddCommand(sbomCmd)
108109
}
109110

@@ -152,14 +153,44 @@ Examples:
152153
RunE: runSBOMVerify,
153154
}
154155

156+
var sbomValidateCmd = &cobra.Command{
157+
Use: "validate <sbom-file>",
158+
Short: "Validate SBOM against policy rules",
159+
Long: `Validate an SBOM file against defined policy rules.
160+
161+
Policy files are YAML documents that define validation rules for:
162+
- Supply chain security (replace directives, vendoring)
163+
- Security requirements (CGO status, retracted versions)
164+
- Completeness checks (required components, metadata)
165+
- License compliance (allowed/blocked licenses)
166+
167+
Examples:
168+
# Validate with default policy
169+
goenv sbom validate sbom.json --policy=.goenv-policy.yaml
170+
171+
# Validate and fail on warnings
172+
goenv sbom validate sbom.json --policy=policy.yaml --fail-on-warning
173+
174+
# Validate with verbose output
175+
goenv sbom validate sbom.json --policy=policy.yaml --verbose`,
176+
Args: cobra.ExactArgs(1),
177+
RunE: runSBOMValidate,
178+
}
179+
155180
var (
156-
hashAlgorithm string
157-
verifyDiff bool
181+
hashAlgorithm string
182+
verifyDiff bool
183+
policyFile string
184+
failOnWarning bool
185+
verboseValidate bool
158186
)
159187

160188
func init() {
161189
sbomHashCmd.Flags().StringVar(&hashAlgorithm, "algorithm", "sha256", "Hash algorithm (sha256, sha512)")
162190
sbomVerifyCmd.Flags().BoolVar(&verifyDiff, "diff", false, "Show detailed differences if SBOMs don't match")
191+
sbomValidateCmd.Flags().StringVarP(&policyFile, "policy", "p", ".goenv-policy.yaml", "Path to policy configuration file")
192+
sbomValidateCmd.Flags().BoolVar(&failOnWarning, "fail-on-warning", false, "Treat warnings as failures")
193+
sbomValidateCmd.Flags().BoolVar(&verboseValidate, "verbose", false, "Show detailed validation output")
163194
}
164195

165196
func runSBOMHash(cmd *cobra.Command, args []string) error {
@@ -223,6 +254,91 @@ func runSBOMVerify(cmd *cobra.Command, args []string) error {
223254
return fmt.Errorf("SBOMs are not reproducibly identical")
224255
}
225256

257+
func runSBOMValidate(cmd *cobra.Command, args []string) error {
258+
sbomPath := args[0]
259+
260+
// Verify SBOM file exists
261+
if !utils.FileExists(sbomPath) {
262+
return fmt.Errorf("SBOM file not found: %s", sbomPath)
263+
}
264+
265+
// Verify policy file exists
266+
if !utils.FileExists(policyFile) {
267+
return fmt.Errorf("policy file not found: %s (use --policy to specify)", policyFile)
268+
}
269+
270+
cfg, _ := cmdutil.SetupContext()
271+
272+
// Load policy engine
273+
engine, err := sbom.NewPolicyEngine(policyFile)
274+
if err != nil {
275+
return errors.FailedTo("load policy", err)
276+
}
277+
278+
if cfg.Debug || verboseValidate {
279+
fmt.Fprintf(cmd.ErrOrStderr(), "goenv: Validating %s against policy %s\n", sbomPath, policyFile)
280+
}
281+
282+
// Run validation
283+
result, err := engine.Validate(sbomPath)
284+
if err != nil {
285+
return errors.FailedTo("validate SBOM", err)
286+
}
287+
288+
// Output results
289+
if verboseValidate {
290+
fmt.Fprint(cmd.OutOrStdout(), result.Summary)
291+
292+
// Show detailed violations
293+
if len(result.Violations) > 0 {
294+
fmt.Fprintf(cmd.OutOrStdout(), "\nViolations:\n")
295+
for i, v := range result.Violations {
296+
fmt.Fprintf(cmd.OutOrStdout(), "\n%d. %s\n", i+1, v.Rule)
297+
fmt.Fprintf(cmd.OutOrStdout(), " Severity: %s\n", v.Severity)
298+
fmt.Fprintf(cmd.OutOrStdout(), " Component: %s\n", v.Component)
299+
fmt.Fprintf(cmd.OutOrStdout(), " Message: %s\n", v.Message)
300+
if v.Remediation != "" {
301+
fmt.Fprintf(cmd.OutOrStdout(), " Remediation: %s\n", v.Remediation)
302+
}
303+
}
304+
}
305+
306+
if len(result.Warnings) > 0 {
307+
fmt.Fprintf(cmd.OutOrStdout(), "\nWarnings:\n")
308+
for i, w := range result.Warnings {
309+
fmt.Fprintf(cmd.OutOrStdout(), "\n%d. %s\n", i+1, w.Rule)
310+
fmt.Fprintf(cmd.OutOrStdout(), " Severity: %s\n", w.Severity)
311+
fmt.Fprintf(cmd.OutOrStdout(), " Component: %s\n", w.Component)
312+
fmt.Fprintf(cmd.OutOrStdout(), " Message: %s\n", w.Message)
313+
if w.Remediation != "" {
314+
fmt.Fprintf(cmd.OutOrStdout(), " Remediation: %s\n", w.Remediation)
315+
}
316+
}
317+
}
318+
} else {
319+
// Concise output
320+
if result.Passed {
321+
fmt.Fprintf(cmd.OutOrStdout(), "✓ SBOM validation passed\n")
322+
} else {
323+
fmt.Fprintf(cmd.ErrOrStderr(), "✗ SBOM validation failed\n")
324+
fmt.Fprintf(cmd.ErrOrStderr(), " %d violations, %d warnings\n",
325+
len(result.Violations), len(result.Warnings))
326+
fmt.Fprintf(cmd.ErrOrStderr(), " Run with --verbose for details\n")
327+
}
328+
}
329+
330+
// Return error if validation failed
331+
if !result.Passed {
332+
if failOnWarning && len(result.Warnings) > 0 {
333+
return fmt.Errorf("validation failed with %d violations and %d warnings",
334+
len(result.Violations), len(result.Warnings))
335+
}
336+
return fmt.Errorf("validation failed with %d violations", len(result.Violations))
337+
}
338+
339+
return nil
340+
}
341+
226342
func runSBOMProject(cmd *cobra.Command, args []string) error {
227343
cfg, mgr := cmdutil.SetupContext()
228344

0 commit comments

Comments
 (0)