diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14b6da4..c35a060 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,6 +95,8 @@ jobs: security: name: Security Scanning runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 @@ -103,15 +105,14 @@ jobs: go-version-file: 'go.mod' - name: Run govulncheck - continue-on-error: true run: | - go install golang.org/x/vuln/cmd/govulncheck@latest + go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 govulncheck ./... - name: Run gosec (SAST) - uses: securego/gosec@master - with: - args: '-no-fail -fmt json -out gosec-results.json ./...' + run: | + go install github.com/securego/gosec/v2/cmd/gosec@v2.22.4 + gosec -exclude-generated -exclude=G115,G304,G107 -fmt json -out gosec-results.json ./... - name: Upload gosec results uses: actions/upload-artifact@v4 diff --git a/cmd/capiscio/badge.go b/cmd/capiscio/badge.go index de35a3a..b41b69c 100644 --- a/cmd/capiscio/badge.go +++ b/cmd/capiscio/badge.go @@ -909,7 +909,7 @@ Example: // Write to file if requested if requestOutFile != "" { - if err := os.WriteFile(requestOutFile, []byte(result.Token), 0644); err != nil { + if err := os.WriteFile(requestOutFile, []byte(result.Token), 0600); err != nil { return fmt.Errorf("failed to write badge to file: %w", err) } fmt.Printf("\nšŸ“ Badge saved to: %s\n", requestOutFile) diff --git a/cmd/capiscio/gateway.go b/cmd/capiscio/gateway.go index 2b6dbe2..2f49899 100644 --- a/cmd/capiscio/gateway.go +++ b/cmd/capiscio/gateway.go @@ -6,6 +6,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "time" "github.com/capiscio/capiscio-core/v2/pkg/badge" "github.com/capiscio/capiscio-core/v2/pkg/gateway" @@ -56,8 +57,14 @@ var gatewayStartCmd = &cobra.Command{ // 5. Start Server addr := fmt.Sprintf(":%d", gatewayPort) + srv := &http.Server{ + Addr: addr, + Handler: handler, + ReadHeaderTimeout: 10 * time.Second, + IdleTimeout: 120 * time.Second, + } log.Printf("Gateway listening on %s -> %s", addr, gatewayTarget) - return http.ListenAndServe(addr, handler) + return srv.ListenAndServe() }, } diff --git a/cmd/capiscio/init.go b/cmd/capiscio/init.go index 7adb108..411d750 100644 --- a/cmd/capiscio/init.go +++ b/cmd/capiscio/init.go @@ -245,6 +245,7 @@ func generateAndSaveKeys(outputDir string) (ed25519.PublicKey, ed25519.PrivateKe return nil, nil, "", jose.JSONWebKey{}, fmt.Errorf("failed to marshal public key: %w", err) } publicKeyPath := filepath.Join(outputDir, "public.jwk") + // #nosec G306 -- public key material if err := os.WriteFile(publicKeyPath, pubBytes, 0644); err != nil { return nil, nil, "", jose.JSONWebKey{}, fmt.Errorf("failed to write public key: %w", err) } @@ -252,6 +253,7 @@ func generateAndSaveKeys(outputDir string) (ed25519.PublicKey, ed25519.PrivateKe // Save DID didPath := filepath.Join(outputDir, "did.txt") + // #nosec G306 -- DID is public identifier if err := os.WriteFile(didPath, []byte(didKey+"\n"), 0644); err != nil { return nil, nil, "", jose.JSONWebKey{}, fmt.Errorf("failed to write DID: %w", err) } @@ -279,6 +281,7 @@ func saveAgentCard(outputDir, agentID, agentName, didKey, serverURL string, pubJ if err != nil { return fmt.Errorf("failed to marshal agent card: %w", err) } + // #nosec G306 -- agent cards are public-facing metadata if err := os.WriteFile(agentCardPath, cardBytes, 0644); err != nil { return fmt.Errorf("failed to write agent card: %w", err) } diff --git a/cmd/capiscio/key.go b/cmd/capiscio/key.go index 7236d3c..dfbe37b 100644 --- a/cmd/capiscio/key.go +++ b/cmd/capiscio/key.go @@ -88,6 +88,7 @@ self-signed Trust Badges.`, if err != nil { return err } + // #nosec G306 -- public key material if err := os.WriteFile(keyOutPublic, pubBytes, 0644); err != nil { return fmt.Errorf("failed to write public key: %w", err) } @@ -95,6 +96,7 @@ self-signed Trust Badges.`, // 6. Output did:key identifier if keyOutDID != "" { + // #nosec G306 -- DID is public identifier if err := os.WriteFile(keyOutDID, []byte(didKey+"\n"), 0644); err != nil { return fmt.Errorf("failed to write did:key: %w", err) } diff --git a/cmd/capiscio/policy.go b/cmd/capiscio/policy.go index ac49542..8fa7aa0 100644 --- a/cmd/capiscio/policy.go +++ b/cmd/capiscio/policy.go @@ -176,7 +176,7 @@ func runPolicyContext(_ *cobra.Command, _ []string) error { } if policyOutput != "" { - if err := os.WriteFile(policyOutput, append(output, '\n'), 0644); err != nil { + if err := os.WriteFile(policyOutput, append(output, '\n'), 0600); err != nil { return fmt.Errorf("write output file: %w", err) } fmt.Printf("āœ… Policy context written to %s\n", policyOutput) diff --git a/examples/secure_ping_pong/server/main.go b/examples/secure_ping_pong/server/main.go index 449b40c..a757a2c 100644 --- a/examples/secure_ping_pong/server/main.go +++ b/examples/secure_ping_pong/server/main.go @@ -7,6 +7,7 @@ import ( "io" "log" "net/http" + "time" "github.com/capiscio/capiscio-core/v2/pkg/simpleguard" ) @@ -73,7 +74,12 @@ func main() { log.Println("šŸ›”ļø Secure Ping Pong Server running on :8080") log.Println(" Waiting for signed requests...") - if err := http.ListenAndServe(":8080", mux); err != nil { + srv := &http.Server{ + Addr: ":8080", + Handler: mux, + ReadHeaderTimeout: 10 * time.Second, + } + if err := srv.ListenAndServe(); err != nil { log.Fatal(err) } } diff --git a/go.mod b/go.mod index 45b1db3..1eacc54 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/capiscio/capiscio-core/v2 -go 1.25.0 +go 1.25.8 require ( github.com/go-jose/go-jose/v4 v4.1.3 @@ -8,7 +8,7 @@ require ( github.com/open-policy-agent/opa v1.14.1 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 - google.golang.org/grpc v1.79.1 + google.golang.org/grpc v1.79.3 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 684f4fd..a500015 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= -google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/rpc/simpleguard_service.go b/internal/rpc/simpleguard_service.go index f936986..99c6405 100644 --- a/internal/rpc/simpleguard_service.go +++ b/internal/rpc/simpleguard_service.go @@ -549,6 +549,7 @@ func initGenerateAndSaveKeys(outputDir string) (*initKeyResult, error) { } pubKeyPath := filepath.Join(outputDir, "public.jwk") + // #nosec G306 -- public key material is intended to be readable if err := os.WriteFile(pubKeyPath, pubJWKBytes, 0644); err != nil { return nil, fmt.Errorf("failed to write public key: %v", err) } @@ -616,6 +617,7 @@ func (s *SimpleGuardService) Init(_ context.Context, req *pb.InitRequest) (*pb.I } agentCardPath := filepath.Join(outputDir, "agent-card.json") + // #nosec G306 -- agent cards are public-facing metadata if err := os.WriteFile(agentCardPath, agentCardBytes, 0644); err != nil { return &pb.InitResponse{ErrorMessage: fmt.Sprintf("failed to write agent card: %v", err)}, nil } diff --git a/pkg/badge/keeper.go b/pkg/badge/keeper.go index 57ac521..a4817fe 100644 --- a/pkg/badge/keeper.go +++ b/pkg/badge/keeper.go @@ -291,7 +291,7 @@ func (k *Keeper) renewSelfSign() (*RenewalResult, error) { // Write to file if k.config.OutputFile != "" { - if err := os.WriteFile(k.config.OutputFile, []byte(token), 0644); err != nil { + if err := os.WriteFile(k.config.OutputFile, []byte(token), 0600); err != nil { return nil, fmt.Errorf("failed to write badge file: %w", err) } } diff --git a/pkg/pdp/bundle_manager.go b/pkg/pdp/bundle_manager.go index 268ecab..ddf9ea1 100644 --- a/pkg/pdp/bundle_manager.go +++ b/pkg/pdp/bundle_manager.go @@ -289,7 +289,8 @@ func (m *BundleManager) nextDelay() time.Duration { } // Add jitter: ±25% to prevent thundering herd - jitter := delay * 0.25 * (rand.Float64()*2 - 1) //nolint:gosec // jitter doesn't need crypto rand + // #nosec G404 -- jitter doesn't need crypto rand + jitter := delay * 0.25 * (rand.Float64()*2 - 1) //nolint:gosec delay += jitter return time.Duration(delay)