-
Notifications
You must be signed in to change notification settings - Fork 0
feat(security): enhance security features #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,6 +1,13 @@ | ||||||||||||||||||||||||||
name: "CodeQL" | ||||||||||||||||||||||||||
name: "Security Scanning" | ||||||||||||||||||||||||||
run-name: ${{ github.event.inputs.code_scanning_run_name }} | ||||||||||||||||||||||||||
on: [push, pull_request, workflow_dispatch] | ||||||||||||||||||||||||||
on: | ||||||||||||||||||||||||||
push: | ||||||||||||||||||||||||||
branches: [ "main" ] | ||||||||||||||||||||||||||
pull_request: | ||||||||||||||||||||||||||
branches: [ "main" ] | ||||||||||||||||||||||||||
schedule: | ||||||||||||||||||||||||||
- cron: '0 0 * * *' # Run daily at midnight UTC | ||||||||||||||||||||||||||
workflow_dispatch: | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
concurrency: | ||||||||||||||||||||||||||
group: ${{ github.workflow }}-${{ github.ref }} | ||||||||||||||||||||||||||
|
@@ -12,6 +19,74 @@ env: | |||||||||||||||||||||||||
CODE_SCANNING_IS_ANALYZING_DEFAULT_BRANCH: ${{ github.event.inputs.code_scanning_is_analyzing_default_branch }} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
jobs: | ||||||||||||||||||||||||||
security-scan: | ||||||||||||||||||||||||||
name: Security Scan | ||||||||||||||||||||||||||
runs-on: ubuntu-latest | ||||||||||||||||||||||||||
permissions: | ||||||||||||||||||||||||||
contents: read | ||||||||||||||||||||||||||
security-events: write | ||||||||||||||||||||||||||
actions: read | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
steps: | ||||||||||||||||||||||||||
- name: Checkout repository | ||||||||||||||||||||||||||
uses: actions/checkout@v4 | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Container Scanning | ||||||||||||||||||||||||||
- name: Run Trivy vulnerability scanner | ||||||||||||||||||||||||||
uses: aquasecurity/trivy-action@master | ||||||||||||||||||||||||||
with: | ||||||||||||||||||||||||||
scan-type: 'fs,config' | ||||||||||||||||||||||||||
scan-ref: '.' | ||||||||||||||||||||||||||
format: 'sarif' | ||||||||||||||||||||||||||
output: 'trivy-results.sarif' | ||||||||||||||||||||||||||
severity: 'CRITICAL,HIGH' | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
- name: Upload Trivy scan results | ||||||||||||||||||||||||||
uses: github/codeql-action/upload-sarif@v3 | ||||||||||||||||||||||||||
if: always() | ||||||||||||||||||||||||||
with: | ||||||||||||||||||||||||||
sarif_file: 'trivy-results.sarif' | ||||||||||||||||||||||||||
category: 'trivy' | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Dependency Scanning | ||||||||||||||||||||||||||
- name: Verify dependencies | ||||||||||||||||||||||||||
run: | | ||||||||||||||||||||||||||
go mod verify | ||||||||||||||||||||||||||
go mod download | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
- name: Run govulncheck | ||||||||||||||||||||||||||
run: | | ||||||||||||||||||||||||||
go install golang.org/x/vuln/cmd/govulncheck@latest | ||||||||||||||||||||||||||
govulncheck ./... | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# SAST Scanning | ||||||||||||||||||||||||||
- name: Run gosec | ||||||||||||||||||||||||||
uses: securego/gosec@master | ||||||||||||||||||||||||||
with: | ||||||||||||||||||||||||||
args: '-no-fail -fmt sarif -out gosec-results.sarif ./...' | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
- name: Upload gosec scan results | ||||||||||||||||||||||||||
uses: github/codeql-action/upload-sarif@v3 | ||||||||||||||||||||||||||
if: always() | ||||||||||||||||||||||||||
with: | ||||||||||||||||||||||||||
sarif_file: gosec-results.sarif | ||||||||||||||||||||||||||
category: gosec | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
# Secrets Scanning | ||||||||||||||||||||||||||
- name: Run gitleaks | ||||||||||||||||||||||||||
uses: gitleaks/gitleaks-action@v2 | ||||||||||||||||||||||||||
with: | ||||||||||||||||||||||||||
config-path: .gitleaks.toml | ||||||||||||||||||||||||||
format: sarif | ||||||||||||||||||||||||||
report-path: gitleaks-report.sarif | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Comment on lines
+76
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 suggestion (security): Consider pinning the gitleaks action to a specific commit or release. Pinning to a specific release or commit hash helps prevent unexpected changes in the action from impacting your workflow.
Suggested change
|
||||||||||||||||||||||||||
- name: Upload gitleaks scan results | ||||||||||||||||||||||||||
uses: github/codeql-action/upload-sarif@v3 | ||||||||||||||||||||||||||
if: always() | ||||||||||||||||||||||||||
with: | ||||||||||||||||||||||||||
sarif_file: gitleaks-report.sarif | ||||||||||||||||||||||||||
category: gitleaks | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
analyze: | ||||||||||||||||||||||||||
name: Analyze (${{ matrix.language }}) | ||||||||||||||||||||||||||
runs-on: ${{ fromJSON(matrix.runner) }} | ||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Gitleaks configuration file | ||
|
||
title = "Gitleaks Rules" | ||
|
||
# Add custom rules for secret detection | ||
[[rules]] | ||
id = "github-token" | ||
description = "GitHub Token" | ||
regex = '''(?i)(?:github(?:_| |\.|-)token|gh(?:_| |\.|-)token|gh(?:_| |\.|-)pat|github(?:_| |\.|-)pat)(?:[ |=|:])[ |"|']*([a-zA-Z0-9]{35,41})''' | ||
secret-group = 1 | ||
entropy = 4.5 | ||
keywords = [ | ||
"github[_|-]token", | ||
"gh[_|-]token", | ||
"gh[_|-]pat", | ||
"github[_|-]pat" | ||
] | ||
|
||
[[rules]] | ||
id = "github-oauth" | ||
description = "GitHub OAuth Access Token" | ||
regex = '''(?i)(?:github(?:_| |\.|-)oauth|gh(?:_| |\.|-)oauth)(?:[ |=|:])[ |"|']*([a-zA-Z0-9]{35,41})''' | ||
secret-group = 1 | ||
entropy = 4.5 | ||
keywords = [ | ||
"github[_|-]oauth", | ||
"gh[_|-]oauth" | ||
] | ||
|
||
[[rules]] | ||
id = "aws-access-token" | ||
description = "AWS Access Token" | ||
regex = '''(?i)(?:aws_access_key_id|aws_secret_access_key)(?:[ |=|:])[ |"|']*([a-zA-Z0-9/+]{40})''' | ||
secret-group = 1 | ||
keywords = [ | ||
"aws_access_key_id", | ||
"aws_secret_access_key" | ||
] | ||
|
||
[[rules]] | ||
id = "private-key" | ||
description = "Private Key" | ||
regex = '''(?i)-----BEGIN[ A-Z0-9_-]*PRIVATE KEY-----''' | ||
keywords = [ | ||
"BEGIN PRIVATE KEY", | ||
"BEGIN RSA PRIVATE KEY", | ||
"BEGIN DSA PRIVATE KEY", | ||
"BEGIN EC PRIVATE KEY", | ||
"BEGIN PGP PRIVATE KEY BLOCK" | ||
] | ||
|
||
# Allow list specific files/paths | ||
[allowlist] | ||
paths = [ | ||
'''(.*?)(test)''', # Test files | ||
'''(.*?)(mock)''', # Mock files | ||
'''(.*?)(\\.example)''', # Example files | ||
'''(.*?)(\\.sample)''' # Sample files | ||
] |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,171 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package security | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"context" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"net/http/httptest" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"testing" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/sirupsen/logrus" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/stretchr/testify/assert" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestSecurityConfig(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("DefaultConfig", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg := DefaultConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 16 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.True(t, cfg.ReadOnly, "Default config should be read-only") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.True(t, cfg.DynamicToolsets, "Dynamic toolsets should be enabled by default") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.True(t, cfg.RateLimit.Enabled, "Rate limiting should be enabled by default") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, float64(10), cfg.RateLimit.RequestsPerSecond, "Default RPS should be 10") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+14
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Test for SecurityConfig covers only default values. Please add tests for non-default configurations, including toggling security features, to verify correct behavior in all supported modes.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestSecurityHeaders(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger := logrus.New() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg := DefaultConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 26 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
middleware := NewSecurityMiddleware(cfg, logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 27 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler := middleware.SecurityHeaders(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
w.WriteHeader(http.StatusOK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := httptest.NewRequest("GET", "/", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rec := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler.ServeHTTP(rec, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expectedHeaders := map[string]string{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"Content-Security-Policy": "default-src 'self'", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"X-Content-Type-Options": "nosniff", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"X-Frame-Options": "DENY", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"X-XSS-Protection": "1; mode=block", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"Strict-Transport-Security": "max-age=31536000; includeSubDomains", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for header, expected := range expectedHeaders { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, expected, rec.Header().Get(header), "Security header %s not set correctly", header) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestRateLimiting(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Rate limiting tests do not cover reset or recovery after limit is exceeded. Please add a test to verify the rate limiter allows requests again after the reset period, confirming correct recovery behavior. Suggested implementation: func TestRateLimiting(t *testing.T) {
logger := logrus.New()
cfg := DefaultConfig() }
func TestRateLimiter_ResetsAfterPeriod(t *testing.T) {
logger := logrus.New()
cfg := DefaultConfig()
// Set a short window for testing
cfg.RateLimitWindow = 100 * time.Millisecond
cfg.RateLimitRequests = 2
middleware := NewSecurityMiddleware(cfg, logger)
handler := middleware.RateLimit(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
req := httptest.NewRequest("GET", "/", nil)
rec := httptest.NewRecorder()
// First request should pass
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
// Second request should pass
rec = httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
// Third request should be rate limited
rec = httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusTooManyRequests, rec.Code)
// Wait for the window to reset
time.Sleep(cfg.RateLimitWindow + 10*time.Millisecond)
// After reset, request should pass again
rec = httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
} |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger := logrus.New() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg := DefaultConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 52 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg.RateLimit.RequestsPerSecond = 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg.RateLimit.Burst = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
middleware := NewSecurityMiddleware(cfg, logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 55 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler := middleware.RateLimiting(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
w.WriteHeader(http.StatusOK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Within Limits", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := httptest.NewRequest("GET", "/", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rec := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler.ServeHTTP(rec, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, http.StatusOK, rec.Code) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Exceeds Limits", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Make multiple requests quickly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for i := 0; i < 5; i++ { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := httptest.NewRequest("GET", "/", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rec := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler.ServeHTTP(rec, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if i >= 3 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, http.StatusTooManyRequests, rec.Code) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestToolsetIsolation(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger := logrus.New() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg := DefaultConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 83 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
middleware := NewSecurityMiddleware(cfg, logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 84 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Context Creation", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ctx1 := middleware.CreateToolsetContext("toolset1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ctx2 := middleware.CreateToolsetContext("toolset2") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.NotEqual(t, ctx1.ID, ctx2.ID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.NotNil(t, ctx1.Logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.NotNil(t, ctx2.Logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Resource Limits", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ctx := middleware.CreateToolsetContext("test-toolset") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, int64(512*1024*1024), ctx.ResourceLimits.MaxMemory) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, float64(1.0), ctx.ResourceLimits.MaxCPU) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, 1000, ctx.ResourceLimits.MaxRequests) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestRequestValidation(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger := logrus.New() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg := DefaultConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 105 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
middleware := NewSecurityMiddleware(cfg, logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 106 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler := middleware.RequestValidation(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
w.WriteHeader(http.StatusOK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
})) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Valid Request", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := httptest.NewRequest("POST", "/", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req.Header.Set("Content-Type", "application/json") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req.Header.Set("Authorization", "Bearer token") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rec := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler.ServeHTTP(rec, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, http.StatusOK, rec.Code) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Invalid Content Type", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := httptest.NewRequest("POST", "/", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req.Header.Set("Content-Type", "text/plain") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req.Header.Set("Authorization", "Bearer token") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rec := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler.ServeHTTP(rec, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, http.StatusUnsupportedMediaType, rec.Code) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("Missing Authorization", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := httptest.NewRequest("POST", "/", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req.Header.Set("Content-Type", "application/json") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rec := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handler.ServeHTTP(rec, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Equal(t, http.StatusUnauthorized, rec.Code) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestAuditLogging(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger := logrus.New() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cfg := DefaultConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failure on line 141 in pkg/security/security_test.go
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
middleware := NewSecurityMiddleware(cfg, logger) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Create a test buffer for logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
buf := &logBuffer{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger.SetOutput(buf) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ctx := context.Background() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
toolsetID := "test-toolset" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
operation := "test-operation" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
middleware.AuditLog(ctx, toolsetID, operation) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logs := buf.String() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Contains(t, logs, toolsetID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert.Contains(t, logs, operation) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Helper type for capturing logs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type logBuffer struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logs []string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func (b *logBuffer) Write(p []byte) (n int, err error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
b.logs = append(b.logs, string(p)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return len(p), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func (b *logBuffer) String() string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return string(b.logs[len(b.logs)-1]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 suggestion (security): Pin the gosec action to a stable release instead of 'master'.
Using 'master' may introduce breaking changes or vulnerabilities. Please use a specific version tag for stability and security.