Skip to content

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
79 changes: 77 additions & 2 deletions .github/workflows/code-scanning.yml
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 }}
Expand All @@ -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 ./...'

Comment on lines +63 to +66
Copy link

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.

Suggested change
- name: Run gosec
uses: securego/gosec@master
with:
args: '-no-fail -fmt sarif -out gosec-results.sarif ./...'
- name: Run gosec
uses: securego/gosec@v2.19.0
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
Copy link

Choose a reason for hiding this comment

The 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: Run gitleaks
uses: gitleaks/gitleaks-action@v2
with:
config-path: .gitleaks.toml
format: sarif
report-path: gitleaks-report.sarif
- name: Run gitleaks
uses: gitleaks/gitleaks-action@v2.19.0
with:
config-path: .gitleaks.toml
format: sarif
report-path: gitleaks-report.sarif

- 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) }}
Expand Down
59 changes: 59 additions & 0 deletions .gitleaks.toml
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
]
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ RUN --mount=type=cache,target=/go/pkg/mod \
FROM gcr.io/distroless/base-debian12
# Set the working directory
WORKDIR /server
# Set default toolset permissions
ENV GITHUB_TOOLSET_PERMISSIONS='{"issues":{"level":"read","scopes":["repo"]},"pullrequests":{"level":"write","scopes":["repo"]},"discussions":{"level":"read","scopes":["repo"]},"actions":{"level":"admin","scopes":["repo","workflow"]},"dependabot":{"level":"write","scopes":["repo","security_events"]},"code_scanning":{"level":"read","scopes":["repo","security_events"]},"secret_scanning":{"level":"admin","scopes":["repo","security_events"]}}'
# Copy the binary from the build stage
COPY --from=build /bin/github-mcp-server .
# Set the entrypoint to the server binary
Expand Down
171 changes: 171 additions & 0 deletions pkg/security/security_test.go
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

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig

Check failure on line 16 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: DefaultConfig

Check failure on line 16 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 16 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 16 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig
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
Copy link

Choose a reason for hiding this comment

The 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 TestSecurityConfig(t *testing.T) {
t.Run("DefaultConfig", func(t *testing.T) {
cfg := DefaultConfig()
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")
})
}
func TestSecurityConfig(t *testing.T) {
t.Run("DefaultConfig", func(t *testing.T) {
cfg := DefaultConfig()
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")
})
t.Run("NonDefaultConfig", func(t *testing.T) {
cfg := DefaultConfig()
cfg.ReadOnly = false
cfg.DynamicToolsets = false
cfg.RateLimit.Enabled = false
cfg.RateLimit.RequestsPerSecond = 42
assert.False(t, cfg.ReadOnly, "ReadOnly should be false when set")
assert.False(t, cfg.DynamicToolsets, "DynamicToolsets should be false when set")
assert.False(t, cfg.RateLimit.Enabled, "Rate limiting should be disabled when set")
assert.Equal(t, float64(42), cfg.RateLimit.RequestsPerSecond, "RPS should reflect non-default value")
})
}


func TestSecurityHeaders(t *testing.T) {
logger := logrus.New()
cfg := DefaultConfig()

Check failure on line 26 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig

Check failure on line 26 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: DefaultConfig

Check failure on line 26 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 26 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 26 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig
middleware := NewSecurityMiddleware(cfg, logger)

Check failure on line 27 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

Check failure on line 27 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: NewSecurityMiddleware

Check failure on line 27 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 27 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 27 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

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) {
Copy link

Choose a reason for hiding this comment

The 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

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig

Check failure on line 52 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: DefaultConfig

Check failure on line 52 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 52 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 52 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig
cfg.RateLimit.RequestsPerSecond = 2
cfg.RateLimit.Burst = 1
middleware := NewSecurityMiddleware(cfg, logger)

Check failure on line 55 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

Check failure on line 55 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: NewSecurityMiddleware

Check failure on line 55 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 55 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 55 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

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

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig

Check failure on line 83 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: DefaultConfig

Check failure on line 83 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 83 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 83 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig
middleware := NewSecurityMiddleware(cfg, logger)

Check failure on line 84 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

Check failure on line 84 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: NewSecurityMiddleware

Check failure on line 84 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 84 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 84 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

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

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig

Check failure on line 105 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: DefaultConfig

Check failure on line 105 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 105 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 105 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig
middleware := NewSecurityMiddleware(cfg, logger)

Check failure on line 106 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

Check failure on line 106 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: NewSecurityMiddleware

Check failure on line 106 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 106 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: NewSecurityMiddleware

Check failure on line 106 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: NewSecurityMiddleware

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

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig

Check failure on line 141 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: DefaultConfig

Check failure on line 141 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 141 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: DefaultConfig

Check failure on line 141 in pkg/security/security_test.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: DefaultConfig
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])
}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy