chore: implement instruction compliance remediation
- Replace Go interface{} with any (Go 1.18+ standard)
- Add database indexes to frequently queried model fields
- Add JSDoc documentation to frontend API client methods
- Remove deprecated docker-compose version keys
- Add concurrency groups to all 25 GitHub Actions workflows
- Add YAML front matter and fix H1→H2 headings in docs
Coverage: Backend 85.5%, Frontend 87.73%
Security: No vulnerabilities detected
Refs: docs/plans/instruction_compliance_spec.md
This commit is contained in:
4
.github/workflows/auto-add-to-project.yml
vendored
4
.github/workflows/auto-add-to-project.yml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.issue.number || github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/auto-changelog.yml
vendored
4
.github/workflows/auto-changelog.yml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-draft:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/auto-label-issues.yml
vendored
4
.github/workflows/auto-label-issues.yml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
auto-label:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/auto-versioning.yml
vendored
4
.github/workflows/auto-versioning.yml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
12
.github/workflows/benchmark.yml
vendored
12
.github/workflows/benchmark.yml
vendored
@@ -15,6 +15,13 @@ on:
|
||||
- 'backend/**'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25.5'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
deployments: write
|
||||
@@ -29,7 +36,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache-dependency-path: backend/go.sum
|
||||
|
||||
- name: Run Benchmark
|
||||
@@ -40,7 +47,8 @@ jobs:
|
||||
# Only store results on pushes to main - PRs just run benchmarks without storage
|
||||
# This avoids gh-pages branch errors and permission issues on fork PRs
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
uses: benchmark-action/github-action-benchmark@v1
|
||||
# Security: Pinned to full SHA for supply chain security
|
||||
uses: benchmark-action/github-action-benchmark@4e0b38bc48375986542b13c0d8976b7b80c60c00 # v1
|
||||
with:
|
||||
name: Go Benchmark
|
||||
tool: 'go'
|
||||
|
||||
4
.github/workflows/caddy-major-monitor.yml
vendored
4
.github/workflows/caddy-major-monitor.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
- cron: '17 7 * * 1' # Mondays at 07:17 UTC
|
||||
workflow_dispatch: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
12
.github/workflows/codecov-upload.yml
vendored
12
.github/workflows/codecov-upload.yml
vendored
@@ -7,6 +7,14 @@ on:
|
||||
- development
|
||||
- 'feature/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25.5'
|
||||
NODE_VERSION: '24.12.0'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -23,7 +31,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache-dependency-path: backend/go.sum
|
||||
|
||||
- name: Run Go tests with coverage
|
||||
@@ -54,7 +62,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
with:
|
||||
node-version: '24.12.0'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
|
||||
9
.github/workflows/codeql.yml
vendored
9
.github/workflows/codeql.yml
vendored
@@ -8,6 +8,13 @@ on:
|
||||
schedule:
|
||||
- cron: '0 3 * * 1'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25.5'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
@@ -42,7 +49,7 @@ jobs:
|
||||
if: matrix.language == 'go'
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
|
||||
|
||||
4
.github/workflows/create-labels.yml
vendored
4
.github/workflows/create-labels.yml
vendored
@@ -4,6 +4,10 @@ name: Create Project Labels
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
create-labels:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/docker-build.yml
vendored
4
.github/workflows/docker-build.yml
vendored
@@ -15,6 +15,10 @@ on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/charon
|
||||
|
||||
4
.github/workflows/docker-lint.yml
vendored
4
.github/workflows/docker-lint.yml
vendored
@@ -10,6 +10,10 @@ on:
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
hadolint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/docker-publish.yml
vendored
4
.github/workflows/docker-publish.yml
vendored
@@ -15,6 +15,10 @@ on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/charon
|
||||
|
||||
9
.github/workflows/docs-to-issues.yml
vendored
9
.github/workflows/docs-to-issues.yml
vendored
@@ -24,6 +24,13 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.12.0'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
@@ -44,7 +51,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
with:
|
||||
node-version: '24.12.0'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install gray-matter
|
||||
|
||||
5
.github/workflows/docs.yml
vendored
5
.github/workflows/docs.yml
vendored
@@ -21,6 +21,9 @@ concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.12.0'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Documentation
|
||||
@@ -35,7 +38,7 @@ jobs:
|
||||
- name: 🔧 Set up Node.js
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
with:
|
||||
node-version: '24.12.0'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
# Step 3: Create a beautiful docs site structure
|
||||
- name: 📝 Build documentation site
|
||||
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
- cron: '0 2 * * *' # daily at 02:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
||||
4
.github/workflows/history-rewrite-tests.yml
vendored
4
.github/workflows/history-rewrite-tests.yml
vendored
@@ -9,6 +9,10 @@ on:
|
||||
paths:
|
||||
- 'scripts/history-rewrite/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/pr-checklist.yml
vendored
4
.github/workflows/pr-checklist.yml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate history-rewrite checklist (conditional)
|
||||
|
||||
9
.github/workflows/propagate-changes.yml
vendored
9
.github/workflows/propagate-changes.yml
vendored
@@ -6,6 +6,13 @@ on:
|
||||
- main
|
||||
- development
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
NODE_VERSION: '24.12.0'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
@@ -20,7 +27,7 @@ jobs:
|
||||
- name: Set up Node (for github-script)
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
with:
|
||||
node-version: '24.12.0'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Propagate Changes
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
|
||||
12
.github/workflows/quality-checks.yml
vendored
12
.github/workflows/quality-checks.yml
vendored
@@ -6,6 +6,14 @@ on:
|
||||
pull_request:
|
||||
branches: [ main, development ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25.5'
|
||||
NODE_VERSION: '24.12.0'
|
||||
|
||||
jobs:
|
||||
backend-quality:
|
||||
name: Backend (Go)
|
||||
@@ -16,7 +24,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache-dependency-path: backend/go.sum
|
||||
|
||||
- name: Repo health check
|
||||
@@ -89,7 +97,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version: '24.12.0'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
|
||||
15
.github/workflows/release-goreleaser.yml
vendored
15
.github/workflows/release-goreleaser.yml
vendored
@@ -5,6 +5,14 @@ on:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25.5'
|
||||
NODE_VERSION: '24.12.0'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
@@ -26,12 +34,12 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
with:
|
||||
node-version: '24.12.0'
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Build Frontend
|
||||
working-directory: frontend
|
||||
@@ -43,7 +51,8 @@ jobs:
|
||||
npm run build
|
||||
|
||||
- name: Install Cross-Compilation Tools (Zig)
|
||||
uses: goto-bus-stop/setup-zig@v2
|
||||
# Security: Pinned to full SHA for supply chain security
|
||||
uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2
|
||||
with:
|
||||
version: 0.13.0
|
||||
|
||||
|
||||
4
.github/workflows/renovate.yml
vendored
4
.github/workflows/renovate.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
- cron: '0 5 * * *' # daily 05:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
4
.github/workflows/repo-health.yml
vendored
4
.github/workflows/repo-health.yml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
repo_health:
|
||||
name: Repo health
|
||||
|
||||
@@ -11,6 +11,10 @@ on:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository_owner }}/charon
|
||||
|
||||
4
.github/workflows/waf-integration.yml
vendored
4
.github/workflows/waf-integration.yml
vendored
@@ -20,6 +20,10 @@ on:
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
waf-integration:
|
||||
name: Coraza WAF Integration
|
||||
|
||||
26
Dockerfile
26
Dockerfile
@@ -253,6 +253,11 @@ RUN apk --no-cache add bash ca-certificates sqlite-libs sqlite tzdata curl gette
|
||||
&& apk --no-cache upgrade \
|
||||
&& apk --no-cache upgrade c-ares
|
||||
|
||||
# Security: Create non-root user and group for running the application
|
||||
# This follows the principle of least privilege (CIS Docker Benchmark 4.1)
|
||||
RUN addgroup -g 1000 charon && \
|
||||
adduser -D -u 1000 -G charon -h /app -s /sbin/nologin charon
|
||||
|
||||
# Download MaxMind GeoLite2 Country database
|
||||
# Note: In production, users should provide their own MaxMind license key
|
||||
# This uses the publicly available GeoLite2 database
|
||||
@@ -279,9 +284,11 @@ RUN chmod +x /usr/local/bin/crowdsec /usr/local/bin/cscli 2>/dev/null || true; \
|
||||
fi
|
||||
|
||||
# Create required CrowdSec directories in runtime image
|
||||
# Also prepare persistent config directory structure for volume mounts
|
||||
RUN mkdir -p /etc/crowdsec /etc/crowdsec/acquis.d /etc/crowdsec/bouncers \
|
||||
/etc/crowdsec/hub /etc/crowdsec/notifications \
|
||||
/var/lib/crowdsec/data /var/log/crowdsec /var/log/caddy
|
||||
/var/lib/crowdsec/data /var/log/crowdsec /var/log/caddy \
|
||||
/app/data/crowdsec/config /app/data/crowdsec/data
|
||||
|
||||
# Copy CrowdSec configuration templates from source
|
||||
COPY configs/crowdsec/acquis.yaml /etc/crowdsec.dist/acquis.yaml
|
||||
@@ -320,6 +327,14 @@ ENV CHARON_ENV=production \
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /app/data /app/data/caddy /config /app/data/crowdsec
|
||||
|
||||
# Security: Set ownership of all application directories to non-root charon user
|
||||
# Note: /app/data and /config are typically mounted as volumes; permissions
|
||||
# will be handled at runtime in docker-entrypoint.sh if needed
|
||||
RUN chown -R charon:charon /app /config /var/log/crowdsec /var/log/caddy && \
|
||||
chown -R charon:charon /etc/crowdsec 2>/dev/null || true && \
|
||||
chown -R charon:charon /etc/crowdsec.dist 2>/dev/null || true && \
|
||||
chown -R charon:charon /var/lib/crowdsec 2>/dev/null || true
|
||||
|
||||
# Re-declare build args for LABEL usage
|
||||
ARG VERSION=dev
|
||||
ARG BUILD_DATE
|
||||
@@ -339,5 +354,14 @@ LABEL org.opencontainers.image.title="Charon (CPMP legacy)" \
|
||||
# Expose ports
|
||||
EXPOSE 80 443 443/udp 2019 8080
|
||||
|
||||
# Security: Add healthcheck to monitor container health
|
||||
# Verifies the Charon API is responding correctly
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/api/v1/health || exit 1
|
||||
|
||||
# Security: Run as non-root user (CIS Docker Benchmark 4.1)
|
||||
# The entrypoint script handles any required permission fixes for volumes
|
||||
USER charon
|
||||
|
||||
# Use custom entrypoint to start both Caddy and Charon
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
@@ -134,7 +134,7 @@ func main() {
|
||||
|
||||
// Verify critical security tables exist before starting server
|
||||
// This prevents silent failures in CrowdSec reconciliation
|
||||
securityModels := []interface{}{
|
||||
securityModels := []any{
|
||||
&models.SecurityConfig{},
|
||||
&models.SecurityDecision{},
|
||||
&models.SecurityAudit{},
|
||||
|
||||
@@ -107,7 +107,7 @@ func TestMigrateCommand_Succeeds(t *testing.T) {
|
||||
t.Fatalf("reconnect db: %v", err)
|
||||
}
|
||||
|
||||
securityModels := []interface{}{
|
||||
securityModels := []any{
|
||||
&models.SecurityConfig{},
|
||||
&models.SecurityDecision{},
|
||||
&models.SecurityAudit{},
|
||||
@@ -155,7 +155,7 @@ func TestStartupVerification_MissingTables(t *testing.T) {
|
||||
}
|
||||
|
||||
// Simulate startup verification logic from main.go
|
||||
securityModels := []interface{}{
|
||||
securityModels := []any{
|
||||
&models.SecurityConfig{},
|
||||
&models.SecurityDecision{},
|
||||
&models.SecurityAudit{},
|
||||
|
||||
@@ -41,12 +41,12 @@ func TestAccessListHandler_Create(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
payload map[string]interface{}
|
||||
payload map[string]any
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
name: "create whitelist successfully",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"name": "Office Whitelist",
|
||||
"description": "Allow office IPs only",
|
||||
"type": "whitelist",
|
||||
@@ -57,7 +57,7 @@ func TestAccessListHandler_Create(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "create geo whitelist successfully",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"name": "US Only",
|
||||
"type": "geo_whitelist",
|
||||
"country_codes": "US,CA",
|
||||
@@ -67,7 +67,7 @@ func TestAccessListHandler_Create(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "create local network only",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"name": "Local Network",
|
||||
"type": "whitelist",
|
||||
"local_network_only": true,
|
||||
@@ -77,7 +77,7 @@ func TestAccessListHandler_Create(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "fail with invalid type",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"name": "Invalid",
|
||||
"type": "invalid_type",
|
||||
"enabled": true,
|
||||
@@ -86,7 +86,7 @@ func TestAccessListHandler_Create(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "fail with missing name",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"type": "whitelist",
|
||||
"enabled": true,
|
||||
},
|
||||
@@ -205,13 +205,13 @@ func TestAccessListHandler_Update(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
payload map[string]interface{}
|
||||
payload map[string]any
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
name: "update successfully",
|
||||
id: "1",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"name": "Updated Name",
|
||||
"description": "New description",
|
||||
"enabled": false,
|
||||
@@ -223,7 +223,7 @@ func TestAccessListHandler_Update(t *testing.T) {
|
||||
{
|
||||
name: "update non-existent ACL",
|
||||
id: "9999",
|
||||
payload: map[string]interface{}{
|
||||
payload: map[string]any{
|
||||
"name": "Test",
|
||||
"type": "whitelist",
|
||||
"ip_rules": `[]`,
|
||||
@@ -380,7 +380,7 @@ func TestAccessListHandler_TestIP(t *testing.T) {
|
||||
assert.Equal(t, tt.wantStatus, w.Code)
|
||||
|
||||
if w.Code == http.StatusOK {
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, response, "allowed")
|
||||
@@ -400,7 +400,7 @@ func TestAccessListHandler_GetTemplates(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response []map[string]interface{}
|
||||
var response []map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, response)
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestImportHandler_Commit_InvalidSessionUUID(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"session_uuid": "../../../etc/passwd",
|
||||
})
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestImportHandler_Commit_SessionNotFound(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"session_uuid": "nonexistent-session",
|
||||
})
|
||||
|
||||
@@ -160,7 +160,7 @@ func TestSecurityHandler_UpdateConfig_ApplyCaddyError(t *testing.T) {
|
||||
// Create handler with nil caddy manager (ApplyConfig will be called but is nil)
|
||||
h := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"name": "test",
|
||||
"waf_mode": "block",
|
||||
})
|
||||
@@ -242,7 +242,7 @@ func TestSecurityHandler_UpsertRuleSet_Error(t *testing.T) {
|
||||
// Drop table to cause upsert to fail
|
||||
db.Migrator().DropTable(&models.SecurityRuleSet{})
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"name": "test-ruleset",
|
||||
"enabled": true,
|
||||
})
|
||||
@@ -267,7 +267,7 @@ func TestSecurityHandler_CreateDecision_LogError(t *testing.T) {
|
||||
// Drop decisions table to cause log to fail
|
||||
db.Migrator().DropTable(&models.SecurityDecision{})
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"ip": "192.168.1.1",
|
||||
"action": "ban",
|
||||
})
|
||||
@@ -381,7 +381,7 @@ func TestImportHandler_UploadMulti_MissingCaddyfile(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "sites/example.com", "content": "example.com {}"},
|
||||
},
|
||||
@@ -404,7 +404,7 @@ func TestImportHandler_UploadMulti_EmptyContent(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": ""},
|
||||
},
|
||||
@@ -427,7 +427,7 @@ func TestImportHandler_UploadMulti_PathTraversal(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "example.com {}"},
|
||||
{"filename": "../../../etc/passwd", "content": "bad content"},
|
||||
@@ -676,7 +676,7 @@ func TestRemoteServerHandler_TestConnectionCustom_Unreachable2(t *testing.T) {
|
||||
svc := services.NewRemoteServerService(db)
|
||||
h := NewRemoteServerHandler(svc, nil)
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"host": "192.0.2.1", // TEST-NET - not routable
|
||||
"port": 65535,
|
||||
})
|
||||
@@ -870,7 +870,7 @@ func TestImportHandler_UploadMulti_ValidCaddyfile(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "example.com { reverse_proxy localhost:8080 }"},
|
||||
},
|
||||
@@ -894,7 +894,7 @@ func TestImportHandler_UploadMulti_SubdirFile(t *testing.T) {
|
||||
|
||||
h := NewImportHandler(db, "", t.TempDir(), "")
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "import sites/*"},
|
||||
{"filename": "sites/example.com", "content": "example.com {}"},
|
||||
|
||||
@@ -210,7 +210,7 @@ func TestAuthHandler_Me(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, float64(user.ID), resp["user_id"])
|
||||
assert.Equal(t, "admin", resp["role"])
|
||||
@@ -513,7 +513,7 @@ func TestAuthHandler_VerifyStatus_NotAuthenticated(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["authenticated"])
|
||||
}
|
||||
@@ -530,7 +530,7 @@ func TestAuthHandler_VerifyStatus_InvalidToken(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["authenticated"])
|
||||
}
|
||||
@@ -560,10 +560,10 @@ func TestAuthHandler_VerifyStatus_Authenticated(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, true, resp["authenticated"])
|
||||
userObj := resp["user"].(map[string]interface{})
|
||||
userObj := resp["user"].(map[string]any)
|
||||
assert.Equal(t, "status@example.com", userObj["email"])
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ func TestAuthHandler_VerifyStatus_DisabledUser(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["authenticated"])
|
||||
}
|
||||
@@ -643,9 +643,9 @@ func TestAuthHandler_GetAccessibleHosts_AllowAll(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
hosts := resp["hosts"].([]interface{})
|
||||
hosts := resp["hosts"].([]any)
|
||||
assert.Len(t, hosts, 2)
|
||||
}
|
||||
|
||||
@@ -679,9 +679,9 @@ func TestAuthHandler_GetAccessibleHosts_DenyAll(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
hosts := resp["hosts"].([]interface{})
|
||||
hosts := resp["hosts"].([]any)
|
||||
assert.Len(t, hosts, 0)
|
||||
}
|
||||
|
||||
@@ -718,9 +718,9 @@ func TestAuthHandler_GetAccessibleHosts_PermittedHosts(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
hosts := resp["hosts"].([]interface{})
|
||||
hosts := resp["hosts"].([]any)
|
||||
assert.Len(t, hosts, 1)
|
||||
}
|
||||
|
||||
@@ -803,7 +803,7 @@ func TestAuthHandler_CheckHostAccess_Allowed(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, true, resp["can_access"])
|
||||
}
|
||||
@@ -835,7 +835,7 @@ func TestAuthHandler_CheckHostAccess_Denied(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["can_access"])
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestBackupHandlerSanitizesFilename(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
// Ensure request-scoped logger is present and writes to our buffer
|
||||
c.Set("logger", logger.WithFields(map[string]interface{}{"test": "1"}))
|
||||
c.Set("logger", logger.WithFields(map[string]any{"test": "1"}))
|
||||
|
||||
// initialize logger to buffer
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
@@ -123,7 +123,7 @@ func TestBackupLifecycle(t *testing.T) {
|
||||
resp = httptest.NewRecorder()
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
var list []interface{}
|
||||
var list []any
|
||||
json.Unmarshal(resp.Body.Bytes(), &list)
|
||||
require.Empty(t, list)
|
||||
|
||||
@@ -158,7 +158,7 @@ func TestBackupHandler_Errors(t *testing.T) {
|
||||
resp := httptest.NewRecorder()
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
var list []interface{}
|
||||
var list []any
|
||||
json.Unmarshal(resp.Body.Bytes(), &list)
|
||||
require.Empty(t, list)
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ func BenchmarkSecurityHandler_UpsertRuleSet(b *testing.B) {
|
||||
router := gin.New()
|
||||
router.POST("/api/v1/security/rulesets", h.UpsertRuleSet)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "bench-ruleset",
|
||||
"content": "SecRule REQUEST_URI \"@contains /admin\" \"id:1000,phase:1,deny\"",
|
||||
"mode": "blocking",
|
||||
@@ -209,7 +209,7 @@ func BenchmarkSecurityHandler_CreateDecision(b *testing.B) {
|
||||
router := gin.New()
|
||||
router.POST("/api/v1/security/decisions", h.CreateDecision)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"ip": "192.168.1.100",
|
||||
"action": "block",
|
||||
"details": "benchmark test",
|
||||
@@ -273,7 +273,7 @@ func BenchmarkSecurityHandler_UpdateConfig(b *testing.B) {
|
||||
router := gin.New()
|
||||
router.PUT("/api/v1/security/config", h.UpdateConfig)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "default",
|
||||
"enabled": true,
|
||||
"rate_limit_enable": true,
|
||||
@@ -396,7 +396,7 @@ func BenchmarkSecurityHandler_LargeRuleSetContent(b *testing.B) {
|
||||
largeContent += "SecRule REQUEST_URI \"@contains /path" + string(rune(i)) + "\" \"id:" + string(rune(1000+i)) + ",phase:1,deny\"\n"
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "large-ruleset",
|
||||
"content": largeContent,
|
||||
"mode": "blocking",
|
||||
|
||||
@@ -126,7 +126,7 @@ func (h *CertificateHandler) Upload(c *gin.Context) {
|
||||
"cert",
|
||||
"Certificate Uploaded",
|
||||
fmt.Sprintf("Certificate %s uploaded", util.SanitizeForLog(cert.Name)),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": util.SanitizeForLog(cert.Name),
|
||||
"Domains": util.SanitizeForLog(cert.Domains),
|
||||
"Action": "uploaded",
|
||||
@@ -203,7 +203,7 @@ func (h *CertificateHandler) Delete(c *gin.Context) {
|
||||
"cert",
|
||||
"Certificate Deleted",
|
||||
fmt.Sprintf("Certificate ID %d deleted", id),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"ID": id,
|
||||
"Action": "deleted",
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
// mockAuthMiddleware adds a mock user to the context for testing
|
||||
func mockAuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set("user", map[string]interface{}{"id": 1, "username": "testuser"})
|
||||
c.Set("user", map[string]any{"id": 1, "username": "testuser"})
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,17 +47,17 @@ func TestListPresetsShowsCachedStatus(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
err = json.Unmarshal(resp.Body.Bytes(), &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
presets := result["presets"].([]interface{})
|
||||
presets := result["presets"].([]any)
|
||||
require.NotEmpty(t, presets, "Should have at least one preset")
|
||||
|
||||
// Find our cached preset
|
||||
found := false
|
||||
for _, p := range presets {
|
||||
preset := p.(map[string]interface{})
|
||||
preset := p.(map[string]any)
|
||||
if preset["slug"] == "test/cached" {
|
||||
found = true
|
||||
require.True(t, preset["cached"].(bool), "Preset should be marked as cached")
|
||||
|
||||
@@ -49,14 +49,14 @@ func TestListDecisions_Success(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
decisions := resp["decisions"].([]interface{})
|
||||
decisions := resp["decisions"].([]any)
|
||||
assert.Len(t, decisions, 1)
|
||||
|
||||
decision := decisions[0].(map[string]interface{})
|
||||
decision := decisions[0].(map[string]any)
|
||||
assert.Equal(t, "192.168.1.100", decision["value"])
|
||||
assert.Equal(t, "ban", decision["type"])
|
||||
assert.Equal(t, "ip", decision["scope"])
|
||||
@@ -88,11 +88,11 @@ func TestListDecisions_EmptyList(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
decisions := resp["decisions"].([]interface{})
|
||||
decisions := resp["decisions"].([]any)
|
||||
assert.Len(t, decisions, 0)
|
||||
assert.Equal(t, float64(0), resp["total"])
|
||||
}
|
||||
@@ -120,11 +120,11 @@ func TestListDecisions_CscliError(t *testing.T) {
|
||||
// Should return 200 with empty list and error message
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
decisions := resp["decisions"].([]interface{})
|
||||
decisions := resp["decisions"].([]any)
|
||||
assert.Len(t, decisions, 0)
|
||||
assert.Contains(t, resp["error"], "cscli not available")
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func TestBanIP_Success(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -232,7 +232,7 @@ func TestBanIP_DefaultDuration(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -344,7 +344,7 @@ func TestUnbanIP_Success(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -406,25 +406,25 @@ func TestListDecisions_MultipleDecisions(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
decisions := resp["decisions"].([]interface{})
|
||||
decisions := resp["decisions"].([]any)
|
||||
assert.Len(t, decisions, 3)
|
||||
assert.Equal(t, float64(3), resp["total"])
|
||||
|
||||
// Verify each decision
|
||||
d1 := decisions[0].(map[string]interface{})
|
||||
d1 := decisions[0].(map[string]any)
|
||||
assert.Equal(t, "192.168.1.100", d1["value"])
|
||||
assert.Equal(t, "cscli", d1["origin"])
|
||||
|
||||
d2 := decisions[1].(map[string]interface{})
|
||||
d2 := decisions[1].(map[string]any)
|
||||
assert.Equal(t, "10.0.0.50", d2["value"])
|
||||
assert.Equal(t, "crowdsec", d2["origin"])
|
||||
assert.Equal(t, "ssh-bf", d2["scenario"])
|
||||
|
||||
d3 := decisions[2].(map[string]interface{})
|
||||
d3 := decisions[2].(map[string]any)
|
||||
assert.Equal(t, "172.16.0.0/24", d3["value"])
|
||||
assert.Equal(t, "range", d3["scope"])
|
||||
}
|
||||
|
||||
@@ -253,12 +253,12 @@ func TestCrowdsec_ListFiles_EmptyDir(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
// Files may be nil or empty array when dir is empty
|
||||
files := resp["files"]
|
||||
if files != nil {
|
||||
assert.Len(t, files.([]interface{}), 0)
|
||||
assert.Len(t, files.([]any), 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ func TestCrowdsec_ListFiles_NonExistent(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
// Should return empty array (nil) for non-existent dir
|
||||
// The files key should exist
|
||||
@@ -329,7 +329,7 @@ func TestCrowdsec_ReadFile_NestedPath(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, "nested content", resp["content"])
|
||||
}
|
||||
@@ -398,9 +398,9 @@ func TestCrowdsec_ListPresets_Success(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
presets, ok := resp["presets"].([]interface{})
|
||||
presets, ok := resp["presets"].([]any)
|
||||
assert.True(t, ok)
|
||||
assert.Greater(t, len(presets), 0)
|
||||
}
|
||||
|
||||
@@ -648,7 +648,7 @@ func TestConsoleEnrollSuccess(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
// Enrollment request sent, but user must accept on crowdsec.net
|
||||
require.Equal(t, "pending_acceptance", resp["status"])
|
||||
@@ -725,7 +725,7 @@ func TestConsoleStatusSuccess(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
require.Equal(t, "not_enrolled", resp["status"])
|
||||
}
|
||||
@@ -754,7 +754,7 @@ func TestConsoleStatusAfterEnroll(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, w2.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w2.Body.Bytes(), &resp))
|
||||
// Enrollment request sent, but user must accept on crowdsec.net
|
||||
require.Equal(t, "pending_acceptance", resp["status"])
|
||||
@@ -969,7 +969,7 @@ func TestGetAcquisitionConfigNotFound(t *testing.T) {
|
||||
if w.Code == http.StatusNotFound {
|
||||
require.Contains(t, w.Body.String(), "not found")
|
||||
} else {
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
require.Contains(t, resp, "content")
|
||||
require.Equal(t, "/etc/crowdsec/acquis.yaml", resp["path"])
|
||||
@@ -1134,7 +1134,7 @@ func TestDeleteConsoleEnrollmentThenReenroll(t *testing.T) {
|
||||
req2 := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/console/status", http.NoBody)
|
||||
r.ServeHTTP(w2, req2)
|
||||
require.Equal(t, http.StatusOK, w2.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w2.Body.Bytes(), &resp))
|
||||
require.Equal(t, "pending_acceptance", resp["status"])
|
||||
require.Equal(t, "test-agent-1", resp["agent_name"])
|
||||
@@ -1150,7 +1150,7 @@ func TestDeleteConsoleEnrollmentThenReenroll(t *testing.T) {
|
||||
req4 := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/console/status", http.NoBody)
|
||||
r.ServeHTTP(w4, req4)
|
||||
require.Equal(t, http.StatusOK, w4.Code)
|
||||
var resp2 map[string]interface{}
|
||||
var resp2 map[string]any
|
||||
require.NoError(t, json.Unmarshal(w4.Body.Bytes(), &resp2))
|
||||
require.Equal(t, "not_enrolled", resp2["status"])
|
||||
|
||||
@@ -1167,7 +1167,7 @@ func TestDeleteConsoleEnrollmentThenReenroll(t *testing.T) {
|
||||
req6 := httptest.NewRequest(http.MethodGet, "/api/v1/admin/crowdsec/console/status", http.NoBody)
|
||||
r.ServeHTTP(w6, req6)
|
||||
require.Equal(t, http.StatusOK, w6.Code)
|
||||
var resp3 map[string]interface{}
|
||||
var resp3 map[string]any
|
||||
require.NoError(t, json.Unmarshal(w6.Body.Bytes(), &resp3))
|
||||
require.Equal(t, "pending_acceptance", resp3["status"])
|
||||
require.Equal(t, "test-agent-2", resp3["agent_name"])
|
||||
@@ -1200,7 +1200,7 @@ func TestCrowdsecStart_LAPINotReadyTimeout(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
require.Equal(t, "started", resp["status"])
|
||||
require.False(t, resp["lapi_ready"].(bool))
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestGetLAPIDecisions_FallbackToCscli(t *testing.T) {
|
||||
// Should return success (from cscli fallback)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
// Should have decisions array (empty from mock)
|
||||
@@ -58,7 +58,7 @@ func TestGetLAPIDecisions_EmptyResponse(t *testing.T) {
|
||||
// Will fallback to cscli which returns empty
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
// Should have decisions array (may be empty)
|
||||
@@ -83,7 +83,7 @@ func TestCheckLAPIHealth_Handler(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ func TestApplyPresetHandlerBackupFailure(t *testing.T) {
|
||||
require.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
|
||||
// Verify response includes backup path for traceability
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response))
|
||||
_, hasBackup := response["backup"]
|
||||
require.True(t, hasBackup, "Response should include 'backup' field for diagnostics")
|
||||
@@ -479,7 +479,7 @@ r.ServeHTTP(w, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
|
||||
require.Equal(t, "pulled", resp["status"])
|
||||
@@ -520,7 +520,7 @@ r.ServeHTTP(w, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
|
||||
require.Equal(t, "applied", resp["status"])
|
||||
|
||||
@@ -73,7 +73,7 @@ func TestPullThenApplyIntegration(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, pullResp.Code, "Pull should succeed")
|
||||
|
||||
var pullResult map[string]interface{}
|
||||
var pullResult map[string]any
|
||||
err = json.Unmarshal(pullResp.Body.Bytes(), &pullResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "pulled", pullResult["status"])
|
||||
@@ -100,7 +100,7 @@ func TestPullThenApplyIntegration(t *testing.T) {
|
||||
// This should NOT return "preset not cached" error
|
||||
require.Equal(t, http.StatusOK, applyResp.Code, "Apply should succeed after pull. Response: %s", applyResp.Body.String())
|
||||
|
||||
var applyResult map[string]interface{}
|
||||
var applyResult map[string]any
|
||||
err = json.Unmarshal(applyResp.Body.Bytes(), &applyResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "applied", applyResult["status"], "Apply status should be 'applied'")
|
||||
@@ -144,7 +144,7 @@ func TestApplyWithoutPullReturnsProperError(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusInternalServerError, applyResp.Code, "Apply should fail without cache")
|
||||
|
||||
var errorResult map[string]interface{}
|
||||
var errorResult map[string]any
|
||||
err = json.Unmarshal(applyResp.Body.Bytes(), &errorResult)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ func TestStatusResponseFormat(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ func (h *DomainHandler) Create(c *gin.Context) {
|
||||
"domain",
|
||||
"Domain Added",
|
||||
fmt.Sprintf("Domain %s added", util.SanitizeForLog(domain.Name)),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": util.SanitizeForLog(domain.Name),
|
||||
"Action": "created",
|
||||
},
|
||||
@@ -77,7 +77,7 @@ func (h *DomainHandler) Delete(c *gin.Context) {
|
||||
"domain",
|
||||
"Domain Deleted",
|
||||
fmt.Sprintf("Domain %s deleted", util.SanitizeForLog(domain.Name)),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": util.SanitizeForLog(domain.Name),
|
||||
"Action": "deleted",
|
||||
},
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestRemoteServerHandler_Create(t *testing.T) {
|
||||
handler.RegisterRoutes(router.Group("/api/v1"))
|
||||
|
||||
// Test Create
|
||||
serverData := map[string]interface{}{
|
||||
serverData := map[string]any{
|
||||
"name": "New Server",
|
||||
"provider": "generic",
|
||||
"host": "192.168.1.100",
|
||||
@@ -128,7 +128,7 @@ func TestRemoteServerHandler_TestConnection(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &result)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, result["reachable"].(bool))
|
||||
@@ -189,7 +189,7 @@ func TestRemoteServerHandler_Update(t *testing.T) {
|
||||
handler.RegisterRoutes(router.Group("/api/v1"))
|
||||
|
||||
// Test Update
|
||||
updateData := map[string]interface{}{
|
||||
updateData := map[string]any{
|
||||
"name": "Updated Server",
|
||||
"provider": "generic",
|
||||
"host": "10.0.0.1",
|
||||
@@ -293,7 +293,7 @@ func TestProxyHostHandler_Create(t *testing.T) {
|
||||
handler.RegisterRoutes(router.Group("/api/v1"))
|
||||
|
||||
// Test Create
|
||||
hostData := map[string]interface{}{
|
||||
hostData := map[string]any{
|
||||
"name": "New Host",
|
||||
"domain_names": "new.local",
|
||||
"forward_scheme": "http",
|
||||
|
||||
@@ -773,7 +773,7 @@ func CheckMountedImport(db *gorm.DB, mountPath, caddyBinary, importDir string) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustMarshal(v interface{}) []byte {
|
||||
func mustMarshal(v any) []byte {
|
||||
b, _ := json.Marshal(v)
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestImportUploadSanitizesFilename(t *testing.T) {
|
||||
logger.Init(true, buf)
|
||||
|
||||
maliciousFilename := "../evil\nfile.caddy"
|
||||
payload := map[string]interface{}{"filename": maliciousFilename, "content": "site { respond \"ok\" }"}
|
||||
payload := map[string]any{"filename": maliciousFilename, "content": "site { respond \"ok\" }"}
|
||||
bodyBytes, _ := json.Marshal(payload)
|
||||
req := httptest.NewRequest(http.MethodPost, "/import/upload", bytes.NewReader(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestImportHandler_GetStatus(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, resp["has_pending"])
|
||||
@@ -65,7 +65,7 @@ func TestImportHandler_GetStatus(t *testing.T) {
|
||||
err = json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, resp["has_pending"])
|
||||
session := resp["session"].(map[string]interface{})
|
||||
session := resp["session"].(map[string]any)
|
||||
assert.Equal(t, "transient", session["state"])
|
||||
assert.Equal(t, mountPath, session["source_file"])
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestImportHandler_GetStatus(t *testing.T) {
|
||||
err = json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, resp["has_pending"])
|
||||
session = resp["session"].(map[string]interface{})
|
||||
session = resp["session"].(map[string]any)
|
||||
assert.Equal(t, "pending", session["state"]) // DB session, not transient
|
||||
}
|
||||
|
||||
@@ -114,11 +114,11 @@ func TestImportHandler_GetPreview(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &result)
|
||||
|
||||
preview := result["preview"].(map[string]interface{})
|
||||
hosts := preview["hosts"].([]interface{})
|
||||
preview := result["preview"].(map[string]any)
|
||||
hosts := preview["hosts"].([]any)
|
||||
assert.Len(t, hosts, 1)
|
||||
|
||||
// Verify status changed to reviewing
|
||||
@@ -165,7 +165,7 @@ func TestImportHandler_Commit(t *testing.T) {
|
||||
}
|
||||
db.Create(&session)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"session_uuid": "test-uuid",
|
||||
"resolutions": map[string]string{
|
||||
"example.com": "import",
|
||||
@@ -248,7 +248,7 @@ func TestImportHandler_GetPreview_WithContent(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
err = json.Unmarshal(w.Body.Bytes(), &result)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -269,7 +269,7 @@ func TestImportHandler_Commit_Errors(t *testing.T) {
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
// Case 2: Session not found
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"session_uuid": "non-existent",
|
||||
"resolutions": map[string]string{},
|
||||
}
|
||||
@@ -287,7 +287,7 @@ func TestImportHandler_Commit_Errors(t *testing.T) {
|
||||
}
|
||||
db.Create(&session)
|
||||
|
||||
payload = map[string]interface{}{
|
||||
payload = map[string]any{
|
||||
"session_uuid": "invalid-data-uuid",
|
||||
"resolutions": map[string]string{},
|
||||
}
|
||||
@@ -367,7 +367,7 @@ func TestImportHandler_Upload_Failure(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
// The error message comes from Upload -> ImportFile -> "import failed: ..."
|
||||
assert.Contains(t, resp["error"], "import failed")
|
||||
@@ -406,11 +406,11 @@ func TestImportHandler_Upload_Conflict(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// Verify response contains conflict in preview (upload is transient)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
preview := resp["preview"].(map[string]interface{})
|
||||
conflicts := preview["conflicts"].([]interface{})
|
||||
preview := resp["preview"].(map[string]any)
|
||||
conflicts := preview["conflicts"].([]any)
|
||||
found := false
|
||||
for _, c := range conflicts {
|
||||
if c.(string) == "example.com" || strings.Contains(c.(string), "example.com") {
|
||||
@@ -450,7 +450,7 @@ func TestImportHandler_GetPreview_BackupContent(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &result)
|
||||
|
||||
assert.Equal(t, content, result["caddyfile_content"])
|
||||
@@ -495,18 +495,18 @@ func TestImportHandler_GetPreview_TransientMount(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Response body: %s", w.Body.String())
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
err = json.Unmarshal(w.Body.Bytes(), &result)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify transient session
|
||||
session, ok := result["session"].(map[string]interface{})
|
||||
session, ok := result["session"].(map[string]any)
|
||||
assert.True(t, ok, "session should be present in response")
|
||||
assert.Equal(t, "transient", session["state"])
|
||||
assert.Equal(t, mountPath, session["source_file"])
|
||||
|
||||
// Verify preview contains hosts
|
||||
preview, ok := result["preview"].(map[string]interface{})
|
||||
preview, ok := result["preview"].(map[string]any)
|
||||
assert.True(t, ok, "preview should be present in response")
|
||||
assert.NotNil(t, preview["hosts"])
|
||||
|
||||
@@ -541,13 +541,13 @@ func TestImportHandler_Commit_TransientUpload(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// Extract session ID
|
||||
var uploadResp map[string]interface{}
|
||||
var uploadResp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &uploadResp)
|
||||
session := uploadResp["session"].(map[string]interface{})
|
||||
session := uploadResp["session"].(map[string]any)
|
||||
sessionID := session["id"].(string)
|
||||
|
||||
// Now commit the transient upload
|
||||
commitPayload := map[string]interface{}{
|
||||
commitPayload := map[string]any{
|
||||
"session_uuid": sessionID,
|
||||
"resolutions": map[string]string{
|
||||
"uploaded.com": "import",
|
||||
@@ -594,7 +594,7 @@ func TestImportHandler_Commit_TransientMount(t *testing.T) {
|
||||
|
||||
// Commit the mount with a random session ID (transient)
|
||||
sessionID := uuid.NewString()
|
||||
commitPayload := map[string]interface{}{
|
||||
commitPayload := map[string]any{
|
||||
"session_uuid": sessionID,
|
||||
"resolutions": map[string]string{
|
||||
"mounted.com": "import",
|
||||
@@ -646,9 +646,9 @@ func TestImportHandler_Cancel_TransientUpload(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// Extract session ID and file path
|
||||
var uploadResp map[string]interface{}
|
||||
var uploadResp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &uploadResp)
|
||||
session := uploadResp["session"].(map[string]interface{})
|
||||
session := uploadResp["session"].(map[string]any)
|
||||
sessionID := session["id"].(string)
|
||||
sourceFile := session["source_file"].(string)
|
||||
|
||||
@@ -691,7 +691,7 @@ func TestImportHandler_Errors(t *testing.T) {
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
// Commit - Session Not Found
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"session_uuid": "non-existent",
|
||||
"resolutions": map[string]string{},
|
||||
}
|
||||
@@ -760,12 +760,12 @@ func TestImportHandler_DetectImports(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.hasImport, resp["has_imports"])
|
||||
|
||||
imports := resp["imports"].([]interface{})
|
||||
imports := resp["imports"].([]any)
|
||||
assert.Len(t, imports, len(tt.imports))
|
||||
})
|
||||
}
|
||||
@@ -801,7 +801,7 @@ func TestImportHandler_UploadMulti(t *testing.T) {
|
||||
router.POST("/import/upload-multi", handler.UploadMulti)
|
||||
|
||||
t.Run("single Caddyfile", func(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "example.com"},
|
||||
},
|
||||
@@ -815,14 +815,14 @@ func TestImportHandler_UploadMulti(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NotNil(t, resp["session"])
|
||||
assert.NotNil(t, resp["preview"])
|
||||
})
|
||||
|
||||
t.Run("Caddyfile with site files", func(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "import sites/*\n"},
|
||||
{"filename": "sites/site1", "content": "site1.com"},
|
||||
@@ -838,14 +838,14 @@ func TestImportHandler_UploadMulti(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
session := resp["session"].(map[string]interface{})
|
||||
session := resp["session"].(map[string]any)
|
||||
assert.Equal(t, "transient", session["state"])
|
||||
})
|
||||
|
||||
t.Run("missing Caddyfile", func(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "sites/site1", "content": "site1.com"},
|
||||
},
|
||||
@@ -861,7 +861,7 @@ func TestImportHandler_UploadMulti(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("path traversal in filename", func(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "import sites/*\n"},
|
||||
{"filename": "../etc/passwd", "content": "sensitive"},
|
||||
@@ -878,7 +878,7 @@ func TestImportHandler_UploadMulti(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("empty file content", func(t *testing.T) {
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"files": []map[string]string{
|
||||
{"filename": "Caddyfile", "content": "example.com"},
|
||||
{"filename": "sites/site1", "content": " "},
|
||||
@@ -892,7 +892,7 @@ func TestImportHandler_UploadMulti(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Contains(t, resp["error"], "empty")
|
||||
})
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestLogsLifecycle(t *testing.T) {
|
||||
|
||||
var content struct {
|
||||
Filename string `json:"filename"`
|
||||
Logs []interface{} `json:"logs"`
|
||||
Logs []any `json:"logs"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
err = json.Unmarshal(resp.Body.Bytes(), &content)
|
||||
|
||||
@@ -25,11 +25,11 @@ var upgrader = websocket.Upgrader{
|
||||
|
||||
// LogEntry represents a structured log entry sent over WebSocket.
|
||||
type LogEntry struct {
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Source string `json:"source"`
|
||||
Fields map[string]interface{} `json:"fields"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Source string `json:"source"`
|
||||
Fields map[string]any `json:"fields"`
|
||||
}
|
||||
|
||||
// LogsWSHandler handles WebSocket connections for live log streaming.
|
||||
|
||||
@@ -212,7 +212,7 @@ func TestRemoteServerHandler_TestConnectionCustom_Unreachable(t *testing.T) {
|
||||
svc := services.NewRemoteServerService(db)
|
||||
h := NewRemoteServerHandler(svc, nil)
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"host": "192.0.2.1", // TEST-NET - should be unreachable
|
||||
"port": 65535,
|
||||
})
|
||||
|
||||
@@ -338,9 +338,9 @@ func TestNotificationProviderHandler_Preview_WithData(t *testing.T) {
|
||||
svc := services.NewNotificationService(db)
|
||||
h := NewNotificationProviderHandler(svc)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"template": "minimal",
|
||||
"data": map[string]interface{}{
|
||||
"data": map[string]any{
|
||||
"Title": "Custom Title",
|
||||
"Message": "Custom Message",
|
||||
},
|
||||
@@ -363,7 +363,7 @@ func TestNotificationProviderHandler_Preview_InvalidTemplate(t *testing.T) {
|
||||
svc := services.NewNotificationService(db)
|
||||
h := NewNotificationProviderHandler(svc)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"template": "custom",
|
||||
"config": "{{.Invalid",
|
||||
}
|
||||
@@ -524,7 +524,7 @@ func TestNotificationTemplateHandler_Preview_TemplateNotFound(t *testing.T) {
|
||||
svc := services.NewNotificationService(db)
|
||||
h := NewNotificationTemplateHandler(svc)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"template_id": "nonexistent",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -553,9 +553,9 @@ func TestNotificationTemplateHandler_Preview_WithStoredTemplate(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, svc.CreateTemplate(tmpl))
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"template_id": tmpl.ID,
|
||||
"data": map[string]interface{}{
|
||||
"data": map[string]any{
|
||||
"Title": "Test Title",
|
||||
},
|
||||
}
|
||||
@@ -577,7 +577,7 @@ func TestNotificationTemplateHandler_Preview_InvalidTemplate(t *testing.T) {
|
||||
svc := services.NewNotificationService(db)
|
||||
h := NewNotificationTemplateHandler(svc)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"template": "{{.Invalid",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
|
||||
@@ -104,7 +104,7 @@ func (h *NotificationProviderHandler) Templates(c *gin.Context) {
|
||||
|
||||
// Preview renders the template for a provider and returns the resulting JSON object or an error.
|
||||
func (h *NotificationProviderHandler) Preview(c *gin.Context) {
|
||||
var raw map[string]interface{}
|
||||
var raw map[string]any
|
||||
if err := c.ShouldBindJSON(&raw); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -115,13 +115,13 @@ func (h *NotificationProviderHandler) Preview(c *gin.Context) {
|
||||
if b, err := json.Marshal(raw); err == nil {
|
||||
_ = json.Unmarshal(b, &provider)
|
||||
}
|
||||
var payload map[string]interface{}
|
||||
if d, ok := raw["data"].(map[string]interface{}); ok {
|
||||
var payload map[string]any
|
||||
if d, ok := raw["data"].(map[string]any); ok {
|
||||
payload = d
|
||||
}
|
||||
|
||||
if payload == nil {
|
||||
payload = map[string]interface{}{}
|
||||
payload = map[string]any{}
|
||||
}
|
||||
|
||||
// Add some defaults for preview
|
||||
|
||||
@@ -212,7 +212,7 @@ func TestNotificationProviderHandler_Preview(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, resp, "rendered")
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Wikid82/charon/backend/internal/models"
|
||||
"github.com/Wikid82/charon/backend/internal/services"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type NotificationTemplateHandler struct {
|
||||
@@ -63,7 +64,7 @@ func (h *NotificationTemplateHandler) Delete(c *gin.Context) {
|
||||
|
||||
// Preview allows rendering an arbitrary template (provided in request) or a stored template by id.
|
||||
func (h *NotificationTemplateHandler) Preview(c *gin.Context) {
|
||||
var raw map[string]interface{}
|
||||
var raw map[string]any
|
||||
if err := c.ShouldBindJSON(&raw); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -81,8 +82,8 @@ func (h *NotificationTemplateHandler) Preview(c *gin.Context) {
|
||||
tmplStr = s
|
||||
}
|
||||
|
||||
data := map[string]interface{}{}
|
||||
if d, ok := raw["data"].(map[string]interface{}); ok {
|
||||
data := map[string]any{}
|
||||
if d, ok := raw["data"].(map[string]any); ok {
|
||||
data = d
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestNotificationTemplateHandler_CRUDAndPreview(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
var previewResp map[string]interface{}
|
||||
var previewResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &previewResp))
|
||||
require.NotEmpty(t, previewResp["rendered"])
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ func (h *ProxyHostHandler) Create(c *gin.Context) {
|
||||
|
||||
// Validate and normalize advanced config if present
|
||||
if host.AdvancedConfig != "" {
|
||||
var parsed interface{}
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(host.AdvancedConfig), &parsed); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid advanced_config JSON: " + err.Error()})
|
||||
return
|
||||
@@ -129,7 +129,7 @@ func (h *ProxyHostHandler) Create(c *gin.Context) {
|
||||
"proxy_host",
|
||||
"Proxy Host Created",
|
||||
fmt.Sprintf("Proxy Host %s (%s) created", util.SanitizeForLog(host.Name), util.SanitizeForLog(host.DomainNames)),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": util.SanitizeForLog(host.Name),
|
||||
"Domains": util.SanitizeForLog(host.DomainNames),
|
||||
"Action": "created",
|
||||
@@ -164,7 +164,7 @@ func (h *ProxyHostHandler) Update(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Perform a partial update: only mutate fields present in payload
|
||||
var payload map[string]interface{}
|
||||
var payload map[string]any
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
@@ -334,7 +334,7 @@ func (h *ProxyHostHandler) Update(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Locations: replace only if provided
|
||||
if v, ok := payload["locations"].([]interface{}); ok {
|
||||
if v, ok := payload["locations"].([]any); ok {
|
||||
// Rebind to []models.Location
|
||||
b, _ := json.Marshal(v)
|
||||
var locs []models.Location
|
||||
@@ -355,7 +355,7 @@ func (h *ProxyHostHandler) Update(c *gin.Context) {
|
||||
// Advanced config: normalize if provided and changed
|
||||
if v, ok := payload["advanced_config"].(string); ok {
|
||||
if v != "" && v != host.AdvancedConfig {
|
||||
var parsed interface{}
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(v), &parsed); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid advanced_config JSON: " + err.Error()})
|
||||
return
|
||||
@@ -439,7 +439,7 @@ func (h *ProxyHostHandler) Delete(c *gin.Context) {
|
||||
"proxy_host",
|
||||
"Proxy Host Deleted",
|
||||
fmt.Sprintf("Proxy Host %s deleted", host.Name),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": host.Name,
|
||||
"Action": "deleted",
|
||||
},
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestBulkUpdateSecurityHeaders_Success(t *testing.T) {
|
||||
require.NoError(t, db.Create(&host3).Error)
|
||||
|
||||
// Apply profile to all hosts
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{host1.UUID, host2.UUID, host3.UUID},
|
||||
"security_header_profile_id": profile.ID,
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func TestBulkUpdateSecurityHeaders_Success(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Equal(t, float64(3), result["updated"])
|
||||
assert.Empty(t, result["errors"])
|
||||
@@ -150,7 +150,7 @@ func TestBulkUpdateSecurityHeaders_RemoveProfile(t *testing.T) {
|
||||
require.NoError(t, db.Create(&host2).Error)
|
||||
|
||||
// Remove profile from all hosts (set to null)
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{host1.UUID, host2.UUID},
|
||||
"security_header_profile_id": nil,
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func TestBulkUpdateSecurityHeaders_RemoveProfile(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Equal(t, float64(2), result["updated"])
|
||||
|
||||
@@ -192,7 +192,7 @@ func TestBulkUpdateSecurityHeaders_InvalidProfileID(t *testing.T) {
|
||||
|
||||
// Try to apply non-existent profile
|
||||
nonExistentProfileID := uint(99999)
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{host.UUID},
|
||||
"security_header_profile_id": nonExistentProfileID,
|
||||
}
|
||||
@@ -205,7 +205,7 @@ func TestBulkUpdateSecurityHeaders_InvalidProfileID(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Contains(t, result["error"], "security header profile not found")
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func TestBulkUpdateSecurityHeaders_EmptyUUIDs(t *testing.T) {
|
||||
router, _ := setupTestRouterForSecurityHeaders(t)
|
||||
|
||||
// Try to update with empty host UUIDs
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{},
|
||||
"security_header_profile_id": nil,
|
||||
}
|
||||
@@ -227,7 +227,7 @@ func TestBulkUpdateSecurityHeaders_EmptyUUIDs(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Contains(t, result["error"], "host_uuids cannot be empty")
|
||||
}
|
||||
@@ -257,7 +257,7 @@ func TestBulkUpdateSecurityHeaders_PartialFailure(t *testing.T) {
|
||||
|
||||
// Include one valid and one invalid UUID
|
||||
invalidUUID := "non-existent-uuid"
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{host1.UUID, invalidUUID},
|
||||
"security_header_profile_id": profile.ID,
|
||||
}
|
||||
@@ -270,16 +270,16 @@ func TestBulkUpdateSecurityHeaders_PartialFailure(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Equal(t, float64(1), result["updated"])
|
||||
|
||||
// Check errors array
|
||||
errors, ok := result["errors"].([]interface{})
|
||||
errors, ok := result["errors"].([]any)
|
||||
require.True(t, ok)
|
||||
require.Len(t, errors, 1)
|
||||
|
||||
errorMap := errors[0].(map[string]interface{})
|
||||
errorMap := errors[0].(map[string]any)
|
||||
assert.Equal(t, invalidUUID, errorMap["uuid"])
|
||||
assert.Contains(t, errorMap["error"], "proxy host not found")
|
||||
|
||||
@@ -296,7 +296,7 @@ func TestBulkUpdateSecurityHeaders_TransactionRollback(t *testing.T) {
|
||||
// Try to update with all invalid UUIDs
|
||||
invalidUUID1 := "invalid-uuid-1"
|
||||
invalidUUID2 := "invalid-uuid-2"
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{invalidUUID1, invalidUUID2},
|
||||
"security_header_profile_id": nil,
|
||||
}
|
||||
@@ -309,7 +309,7 @@ func TestBulkUpdateSecurityHeaders_TransactionRollback(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Contains(t, result["error"], "All updates failed")
|
||||
assert.Equal(t, float64(0), result["updated"])
|
||||
@@ -383,7 +383,7 @@ func TestBulkUpdateSecurityHeaders_MixedProfileStates(t *testing.T) {
|
||||
require.NoError(t, db.Create(&host3).Error)
|
||||
|
||||
// Apply profile2 to all hosts
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{host1.UUID, host2.UUID, host3.UUID},
|
||||
"security_header_profile_id": profile2.ID,
|
||||
}
|
||||
@@ -396,7 +396,7 @@ func TestBulkUpdateSecurityHeaders_MixedProfileStates(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Equal(t, float64(3), result["updated"])
|
||||
|
||||
@@ -438,7 +438,7 @@ func TestBulkUpdateSecurityHeaders_SingleHost(t *testing.T) {
|
||||
require.NoError(t, db.Create(&host).Error)
|
||||
|
||||
// Apply profile to single host
|
||||
reqBody := map[string]interface{}{
|
||||
reqBody := map[string]any{
|
||||
"host_uuids": []string{host.UUID},
|
||||
"security_header_profile_id": profile.ID,
|
||||
}
|
||||
@@ -451,7 +451,7 @@ func TestBulkUpdateSecurityHeaders_SingleHost(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
assert.Equal(t, float64(1), result["updated"])
|
||||
assert.Empty(t, result["errors"])
|
||||
|
||||
@@ -295,7 +295,7 @@ func TestProxyHostCreate_AdvancedConfig_Normalization(t *testing.T) {
|
||||
|
||||
// Provide an advanced_config value that will be normalized by caddy.NormalizeAdvancedConfig
|
||||
adv := `{"handler":"headers","response":{"set":{"X-Test":"1"}}}`
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "AdvHost",
|
||||
"domain_names": "adv.example.com",
|
||||
"forward_scheme": "http",
|
||||
@@ -318,7 +318,7 @@ func TestProxyHostCreate_AdvancedConfig_Normalization(t *testing.T) {
|
||||
require.NotEmpty(t, created.AdvancedConfig)
|
||||
|
||||
// Confirm it can be unmarshaled and that headers are normalized to array/strings
|
||||
var parsed map[string]interface{}
|
||||
var parsed map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(created.AdvancedConfig), &parsed))
|
||||
// a basic assertion: ensure 'handler' field exists in parsed config when normalized
|
||||
require.Contains(t, parsed, "handler")
|
||||
@@ -513,7 +513,7 @@ func TestProxyHostHandler_BulkUpdateACL_Success(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Equal(t, float64(2), result["updated"])
|
||||
require.Empty(t, result["errors"])
|
||||
@@ -563,7 +563,7 @@ func TestProxyHostHandler_BulkUpdateACL_RemoveACL(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Equal(t, float64(1), result["updated"])
|
||||
require.Empty(t, result["errors"])
|
||||
@@ -607,13 +607,13 @@ func TestProxyHostHandler_BulkUpdateACL_PartialFailure(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Equal(t, float64(1), result["updated"])
|
||||
|
||||
errors := result["errors"].([]interface{})
|
||||
errors := result["errors"].([]any)
|
||||
require.Len(t, errors, 1)
|
||||
errorMap := errors[0].(map[string]interface{})
|
||||
errorMap := errors[0].(map[string]any)
|
||||
require.Equal(t, nonExistentUUID, errorMap["uuid"])
|
||||
require.Equal(t, "proxy host not found", errorMap["error"])
|
||||
|
||||
@@ -635,7 +635,7 @@ func TestProxyHostHandler_BulkUpdateACL_EmptyUUIDs(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Contains(t, result["error"], "host_uuids cannot be empty")
|
||||
}
|
||||
@@ -883,7 +883,7 @@ func TestProxyHostCreate_WithCertificateAndLocations(t *testing.T) {
|
||||
require.NoError(t, db.Create(cert).Error)
|
||||
|
||||
adv := `{"handler":"headers","response":{"set":{"X-Test":"1"}}}`
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "Create With Cert",
|
||||
"domain_names": "cert.example.com",
|
||||
"forward_scheme": "http",
|
||||
@@ -891,7 +891,7 @@ func TestProxyHostCreate_WithCertificateAndLocations(t *testing.T) {
|
||||
"forward_port": 8080,
|
||||
"enabled": true,
|
||||
"certificate_id": cert.ID,
|
||||
"locations": []map[string]interface{}{{"path": "/app", "forward_scheme": "http", "forward_host": "localhost", "forward_port": 8080}},
|
||||
"locations": []map[string]any{{"path": "/app", "forward_scheme": "http", "forward_host": "localhost", "forward_port": 8080}},
|
||||
"advanced_config": adv,
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -930,7 +930,7 @@ func TestProxyHostCreate_WithSecurityHeaderProfile(t *testing.T) {
|
||||
require.NoError(t, db.Create(profile).Error)
|
||||
|
||||
// Create proxy host with security_header_profile_id
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "Host With Security Profile",
|
||||
"domain_names": "secure.example.com",
|
||||
"forward_scheme": "http",
|
||||
@@ -1303,7 +1303,7 @@ func TestProxyHostUpdate_SecurityHeaderProfile_InvalidString(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Contains(t, result["error"], "invalid security_header_profile_id")
|
||||
}
|
||||
@@ -1334,7 +1334,7 @@ func TestProxyHostUpdate_SecurityHeaderProfile_InvalidFloat(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Contains(t, result["error"], "invalid security_header_profile_id")
|
||||
}
|
||||
@@ -1407,7 +1407,7 @@ func TestProxyHostUpdate_SecurityHeaderProfile_UnsupportedType(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &result))
|
||||
require.Contains(t, result["error"], "invalid security_header_profile_id")
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func (h *RemoteServerHandler) Create(c *gin.Context) {
|
||||
"remote_server",
|
||||
"Remote Server Added",
|
||||
fmt.Sprintf("Remote Server %s (%s:%d) added", util.SanitizeForLog(server.Name), util.SanitizeForLog(server.Host), server.Port),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": util.SanitizeForLog(server.Name),
|
||||
"Host": util.SanitizeForLog(server.Host),
|
||||
"Port": server.Port,
|
||||
@@ -143,7 +143,7 @@ func (h *RemoteServerHandler) Delete(c *gin.Context) {
|
||||
"remote_server",
|
||||
"Remote Server Deleted",
|
||||
fmt.Sprintf("Remote Server %s deleted", util.SanitizeForLog(server.Name)),
|
||||
map[string]interface{}{
|
||||
map[string]any{
|
||||
"Name": util.SanitizeForLog(server.Name),
|
||||
"Action": "deleted",
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestRemoteServerHandler_TestConnectionCustom(t *testing.T) {
|
||||
r, _ := setupRemoteServerTest_New(t)
|
||||
|
||||
// Test with a likely closed port
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"host": "127.0.0.1",
|
||||
"port": 54321,
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func TestRemoteServerHandler_TestConnectionCustom(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &result)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, result["reachable"])
|
||||
|
||||
@@ -477,7 +477,7 @@ func (h *SecurityHandler) Disable(c *gin.Context) {
|
||||
|
||||
// GetRateLimitPresets returns predefined rate limit configurations
|
||||
func (h *SecurityHandler) GetRateLimitPresets(c *gin.Context) {
|
||||
presets := []map[string]interface{}{
|
||||
presets := []map[string]any{
|
||||
{
|
||||
"id": "standard",
|
||||
"name": "Standard Web",
|
||||
@@ -518,17 +518,17 @@ func (h *SecurityHandler) GetRateLimitPresets(c *gin.Context) {
|
||||
func (h *SecurityHandler) GetGeoIPStatus(c *gin.Context) {
|
||||
if h.geoipSvc == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"loaded": false,
|
||||
"message": "GeoIP service not initialized",
|
||||
"db_path": "",
|
||||
"loaded": false,
|
||||
"message": "GeoIP service not initialized",
|
||||
"db_path": "",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"loaded": h.geoipSvc.IsLoaded(),
|
||||
"db_path": h.geoipSvc.GetDatabasePath(),
|
||||
"message": "GeoIP service available",
|
||||
"loaded": h.geoipSvc.IsLoaded(),
|
||||
"db_path": h.geoipSvc.GetDatabasePath(),
|
||||
"message": "GeoIP service available",
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestSecurityHandler_GetConfigAndUpdateConfig(t *testing.T) {
|
||||
c.Request = req
|
||||
h.GetConfig(c)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
var body map[string]interface{}
|
||||
var body map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body))
|
||||
// Should return config: null
|
||||
if _, ok := body["config"]; !ok {
|
||||
@@ -57,9 +57,9 @@ func TestSecurityHandler_GetConfigAndUpdateConfig(t *testing.T) {
|
||||
c.Request = req
|
||||
h.GetConfig(c)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
var body2 map[string]interface{}
|
||||
var body2 map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body2))
|
||||
cfgVal, ok := body2["config"].(map[string]interface{})
|
||||
cfgVal, ok := body2["config"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected config object, got %v", body2["config"])
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestSecurityHandler_GetStatus_SQLInjection(t *testing.T) {
|
||||
// Should return 200 and valid JSON despite malicious data
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, resp, "cerberus")
|
||||
@@ -134,7 +134,7 @@ func TestSecurityHandler_UpsertRuleSet_MassivePayload(t *testing.T) {
|
||||
// Try to submit a 3MB payload (should be rejected by service)
|
||||
hugeContent := strings.Repeat("SecRule REQUEST_URI \"@contains /admin\" \"id:1000,phase:1,deny\"\n", 50000)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "huge-ruleset",
|
||||
"content": hugeContent,
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func TestSecurityHandler_UpsertRuleSet_EmptyName(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/api/v1/security/rulesets", h.UpsertRuleSet)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "",
|
||||
"content": "SecRule REQUEST_URI \"@contains /admin\"",
|
||||
}
|
||||
@@ -176,7 +176,7 @@ func TestSecurityHandler_UpsertRuleSet_EmptyName(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Contains(t, resp, "error")
|
||||
}
|
||||
@@ -264,7 +264,7 @@ func TestSecurityHandler_GetStatus_SettingsOverride(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]map[string]interface{}
|
||||
var resp map[string]map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -310,7 +310,7 @@ func TestSecurityHandler_GetStatus_DisabledViaSettings(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]map[string]interface{}
|
||||
var resp map[string]map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -379,7 +379,7 @@ func TestSecurityHandler_UpsertRuleSet_XSSInContent(t *testing.T) {
|
||||
|
||||
// Store content with XSS payload
|
||||
xssPayload := `<script>alert('XSS')</script>`
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "xss-test",
|
||||
"content": xssPayload,
|
||||
}
|
||||
@@ -423,27 +423,27 @@ func TestSecurityHandler_UpdateConfig_RateLimitBounds(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
payload map[string]interface{}
|
||||
payload map[string]any
|
||||
wantOK bool
|
||||
}{
|
||||
{
|
||||
"valid_limits",
|
||||
map[string]interface{}{"rate_limit_requests": 100, "rate_limit_burst": 10, "rate_limit_window_sec": 60},
|
||||
map[string]any{"rate_limit_requests": 100, "rate_limit_burst": 10, "rate_limit_window_sec": 60},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"zero_requests",
|
||||
map[string]interface{}{"rate_limit_requests": 0, "rate_limit_burst": 10},
|
||||
map[string]any{"rate_limit_requests": 0, "rate_limit_burst": 10},
|
||||
true, // Backend accepts, frontend validates
|
||||
},
|
||||
{
|
||||
"negative_burst",
|
||||
map[string]interface{}{"rate_limit_requests": 100, "rate_limit_burst": -1},
|
||||
map[string]any{"rate_limit_requests": 100, "rate_limit_burst": -1},
|
||||
true, // Backend accepts, frontend validates
|
||||
},
|
||||
{
|
||||
"huge_values",
|
||||
map[string]interface{}{"rate_limit_requests": 999999999, "rate_limit_burst": 999999999},
|
||||
map[string]any{"rate_limit_requests": 999999999, "rate_limit_burst": 999999999},
|
||||
true, // Backend accepts (no upper bound validation currently)
|
||||
},
|
||||
}
|
||||
@@ -577,7 +577,7 @@ func TestSecurityHandler_GetStatus_CrowdSecModeValidation(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]map[string]interface{}
|
||||
var resp map[string]map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
|
||||
// Invalid modes should be normalized to "disabled"
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestSecurityHandler_GetStatus_Clean(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
// response body intentionally not printed in clean test
|
||||
@@ -76,10 +76,10 @@ func TestSecurityHandler_Cerberus_DBOverride(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
cerb := response["cerberus"].(map[string]interface{})
|
||||
cerb := response["cerberus"].(map[string]any)
|
||||
assert.Equal(t, true, cerb["enabled"].(bool))
|
||||
}
|
||||
|
||||
@@ -112,10 +112,10 @@ func TestSecurityHandler_ACL_DBOverride(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
acl := response["acl"].(map[string]interface{})
|
||||
acl := response["acl"].(map[string]any)
|
||||
assert.Equal(t, true, acl["enabled"].(bool))
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestSecurityHandler_GenerateBreakGlass_ReturnsToken(t *testing.T) {
|
||||
req, _ := http.NewRequest("POST", "/security/breakglass/generate", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
token, ok := resp["token"].(string)
|
||||
@@ -160,12 +160,12 @@ func TestSecurityHandler_ACL_DisabledWhenCerberusOff(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
cerb := response["cerberus"].(map[string]interface{})
|
||||
cerb := response["cerberus"].(map[string]any)
|
||||
assert.Equal(t, false, cerb["enabled"].(bool))
|
||||
acl := response["acl"].(map[string]interface{})
|
||||
acl := response["acl"].(map[string]any)
|
||||
// ACL must be false because Cerberus is disabled
|
||||
assert.Equal(t, false, acl["enabled"].(bool))
|
||||
}
|
||||
@@ -189,10 +189,10 @@ func TestSecurityHandler_CrowdSec_Mode_DBOverride(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
cs := response["crowdsec"].(map[string]interface{})
|
||||
cs := response["crowdsec"].(map[string]any)
|
||||
assert.Equal(t, "local", cs["mode"].(string))
|
||||
}
|
||||
|
||||
@@ -212,10 +212,10 @@ func TestSecurityHandler_CrowdSec_ExternalMappedToDisabled_DBOverride(t *testing
|
||||
req, _ := http.NewRequest("GET", "/security/status", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
cs := response["crowdsec"].(map[string]interface{})
|
||||
cs := response["crowdsec"].(map[string]any)
|
||||
assert.Equal(t, "disabled", cs["mode"].(string))
|
||||
assert.Equal(t, false, cs["enabled"].(bool))
|
||||
}
|
||||
@@ -236,10 +236,10 @@ func TestSecurityHandler_ExternalModeMappedToDisabled(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/security/status", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
cs := response["crowdsec"].(map[string]interface{})
|
||||
cs := response["crowdsec"].(map[string]any)
|
||||
assert.Equal(t, "disabled", cs["mode"].(string))
|
||||
assert.Equal(t, false, cs["enabled"].(bool))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestSecurityHandler_UpdateConfig_Success(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/config", handler.UpdateConfig)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "default",
|
||||
"admin_whitelist": "192.168.1.0/24",
|
||||
"waf_mode": "monitor",
|
||||
@@ -41,7 +41,7 @@ func TestSecurityHandler_UpdateConfig_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp["config"])
|
||||
@@ -57,7 +57,7 @@ func TestSecurityHandler_UpdateConfig_DefaultName(t *testing.T) {
|
||||
router.POST("/security/config", handler.UpdateConfig)
|
||||
|
||||
// Payload without name - should default to "default"
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"admin_whitelist": "10.0.0.0/8",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -106,7 +106,7 @@ func TestSecurityHandler_GetConfig_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp["config"])
|
||||
@@ -126,7 +126,7 @@ func TestSecurityHandler_GetConfig_NotFound(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, resp["config"])
|
||||
@@ -151,10 +151,10 @@ func TestSecurityHandler_ListDecisions_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
decisions := resp["decisions"].([]interface{})
|
||||
decisions := resp["decisions"].([]any)
|
||||
assert.Len(t, decisions, 2)
|
||||
}
|
||||
|
||||
@@ -177,10 +177,10 @@ func TestSecurityHandler_ListDecisions_WithLimit(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
decisions := resp["decisions"].([]interface{})
|
||||
decisions := resp["decisions"].([]any)
|
||||
assert.Len(t, decisions, 2)
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ func TestSecurityHandler_CreateDecision_Success(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/decisions", handler.CreateDecision)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"ip": "10.0.0.1",
|
||||
"action": "block",
|
||||
"reason": "manual block",
|
||||
@@ -219,7 +219,7 @@ func TestSecurityHandler_CreateDecision_MissingIP(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/decisions", handler.CreateDecision)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"action": "block",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -241,7 +241,7 @@ func TestSecurityHandler_CreateDecision_MissingAction(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/decisions", handler.CreateDecision)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"ip": "10.0.0.1",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -290,10 +290,10 @@ func TestSecurityHandler_ListRuleSets_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
rulesets := resp["rulesets"].([]interface{})
|
||||
rulesets := resp["rulesets"].([]any)
|
||||
assert.Len(t, rulesets, 2)
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ func TestSecurityHandler_UpsertRuleSet_Success(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/rulesets", handler.UpsertRuleSet)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "test-ruleset",
|
||||
"mode": "blocking",
|
||||
"content": "# Test rules",
|
||||
@@ -331,7 +331,7 @@ func TestSecurityHandler_UpsertRuleSet_MissingName(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/rulesets", handler.UpsertRuleSet)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"mode": "blocking",
|
||||
"content": "# Test rules",
|
||||
}
|
||||
@@ -381,7 +381,7 @@ func TestSecurityHandler_DeleteRuleSet_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, resp["deleted"].(bool))
|
||||
@@ -585,7 +585,7 @@ func TestSecurityHandler_Disable_FromLocalhost(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.False(t, resp["enabled"].(bool))
|
||||
}
|
||||
@@ -694,7 +694,7 @@ func TestSecurityHandler_GenerateBreakGlass_NoConfig(t *testing.T) {
|
||||
|
||||
// Should succeed and create a new config with the token
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, resp["token"])
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
name string
|
||||
cfg config.SecurityConfig
|
||||
expectedStatus int
|
||||
expectedBody map[string]interface{}
|
||||
expectedBody map[string]any
|
||||
}{
|
||||
{
|
||||
name: "All Disabled",
|
||||
@@ -30,22 +30,22 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
ACLMode: "disabled",
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: map[string]interface{}{
|
||||
"cerberus": map[string]interface{}{"enabled": false},
|
||||
"crowdsec": map[string]interface{}{
|
||||
expectedBody: map[string]any{
|
||||
"cerberus": map[string]any{"enabled": false},
|
||||
"crowdsec": map[string]any{
|
||||
"mode": "disabled",
|
||||
"api_url": "",
|
||||
"enabled": false,
|
||||
},
|
||||
"waf": map[string]interface{}{
|
||||
"waf": map[string]any{
|
||||
"mode": "disabled",
|
||||
"enabled": false,
|
||||
},
|
||||
"rate_limit": map[string]interface{}{
|
||||
"rate_limit": map[string]any{
|
||||
"mode": "disabled",
|
||||
"enabled": false,
|
||||
},
|
||||
"acl": map[string]interface{}{
|
||||
"acl": map[string]any{
|
||||
"mode": "disabled",
|
||||
"enabled": false,
|
||||
},
|
||||
@@ -61,22 +61,22 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
ACLMode: "enabled",
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: map[string]interface{}{
|
||||
"cerberus": map[string]interface{}{"enabled": true},
|
||||
"crowdsec": map[string]interface{}{
|
||||
expectedBody: map[string]any{
|
||||
"cerberus": map[string]any{"enabled": true},
|
||||
"crowdsec": map[string]any{
|
||||
"mode": "local",
|
||||
"api_url": "",
|
||||
"enabled": true,
|
||||
},
|
||||
"waf": map[string]interface{}{
|
||||
"waf": map[string]any{
|
||||
"mode": "enabled",
|
||||
"enabled": true,
|
||||
},
|
||||
"rate_limit": map[string]interface{}{
|
||||
"rate_limit": map[string]any{
|
||||
"mode": "enabled",
|
||||
"enabled": true,
|
||||
},
|
||||
"acl": map[string]interface{}{
|
||||
"acl": map[string]any{
|
||||
"mode": "enabled",
|
||||
"enabled": true,
|
||||
},
|
||||
@@ -96,12 +96,12 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedJSON, _ := json.Marshal(tt.expectedBody)
|
||||
var expectedNormalized map[string]interface{}
|
||||
var expectedNormalized map[string]any
|
||||
if err := json.Unmarshal(expectedJSON, &expectedNormalized); err != nil {
|
||||
t.Fatalf("failed to unmarshal expected JSON: %v", err)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) {
|
||||
t.Fatalf("Create decision expected status 200, got %d; body: %s", resp.Code, resp.Body.String())
|
||||
}
|
||||
|
||||
var decisionResp map[string]interface{}
|
||||
var decisionResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &decisionResp))
|
||||
require.NotNil(t, decisionResp["decision"])
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) {
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Fatalf("Upsert ruleset expected status 200, got %d; body: %s", resp.Code, resp.Body.String())
|
||||
}
|
||||
var listResp map[string][]map[string]interface{}
|
||||
var listResp map[string][]map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &listResp))
|
||||
require.GreaterOrEqual(t, len(listResp["decisions"]), 1)
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) {
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Fatalf("Upsert ruleset expected status 200, got %d; body: %s", resp.Code, resp.Body.String())
|
||||
}
|
||||
var rsResp map[string]interface{}
|
||||
var rsResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &rsResp))
|
||||
require.NotNil(t, rsResp["ruleset"])
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) {
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Fatalf("List rulesets expected status 200, got %d; body: %s", resp.Code, resp.Body.String())
|
||||
}
|
||||
var listRsResp map[string][]map[string]interface{}
|
||||
var listRsResp map[string][]map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &listRsResp))
|
||||
require.GreaterOrEqual(t, len(listRsResp["rulesets"]), 1)
|
||||
|
||||
@@ -98,7 +98,7 @@ func TestSecurityHandler_CreateAndListDecisionAndRulesets(t *testing.T) {
|
||||
resp = httptest.NewRecorder()
|
||||
r.ServeHTTP(resp, req)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
var delResp map[string]interface{}
|
||||
var delResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &delResp))
|
||||
require.Equal(t, true, delResp["deleted"].(bool))
|
||||
}
|
||||
|
||||
@@ -142,20 +142,20 @@ func TestSecurityHandler_GetStatus_RespectsSettingsTable(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check WAF enabled
|
||||
waf := response["waf"].(map[string]interface{})
|
||||
waf := response["waf"].(map[string]any)
|
||||
assert.Equal(t, tt.expectedWAF, waf["enabled"].(bool), "WAF enabled mismatch")
|
||||
|
||||
// Check Rate Limit enabled
|
||||
rateLimit := response["rate_limit"].(map[string]interface{})
|
||||
rateLimit := response["rate_limit"].(map[string]any)
|
||||
assert.Equal(t, tt.expectedRate, rateLimit["enabled"].(bool), "Rate Limit enabled mismatch")
|
||||
|
||||
// Check CrowdSec enabled
|
||||
crowdsec := response["crowdsec"].(map[string]interface{})
|
||||
crowdsec := response["crowdsec"].(map[string]any)
|
||||
assert.Equal(t, tt.expectedCrowd, crowdsec["enabled"].(bool), "CrowdSec enabled mismatch")
|
||||
})
|
||||
}
|
||||
@@ -185,11 +185,11 @@ func TestSecurityHandler_GetStatus_WAFModeFromSettings(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
waf := response["waf"].(map[string]interface{})
|
||||
waf := response["waf"].(map[string]any)
|
||||
// When enabled via settings, mode should reflect "enabled" state
|
||||
assert.True(t, waf["enabled"].(bool))
|
||||
}
|
||||
@@ -218,10 +218,10 @@ func TestSecurityHandler_GetStatus_RateLimitModeFromSettings(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
rateLimit := response["rate_limit"].(map[string]interface{})
|
||||
rateLimit := response["rate_limit"].(map[string]any)
|
||||
assert.True(t, rateLimit["enabled"].(bool))
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
name string
|
||||
cfg config.SecurityConfig
|
||||
expectedStatus int
|
||||
expectedBody map[string]interface{}
|
||||
expectedBody map[string]any
|
||||
}{
|
||||
{
|
||||
name: "All Disabled",
|
||||
@@ -33,22 +33,22 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
ACLMode: "disabled",
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: map[string]interface{}{
|
||||
"cerberus": map[string]interface{}{"enabled": false},
|
||||
"crowdsec": map[string]interface{}{
|
||||
expectedBody: map[string]any{
|
||||
"cerberus": map[string]any{"enabled": false},
|
||||
"crowdsec": map[string]any{
|
||||
"mode": "disabled",
|
||||
"api_url": "",
|
||||
"enabled": false,
|
||||
},
|
||||
"waf": map[string]interface{}{
|
||||
"waf": map[string]any{
|
||||
"mode": "disabled",
|
||||
"enabled": false,
|
||||
},
|
||||
"rate_limit": map[string]interface{}{
|
||||
"rate_limit": map[string]any{
|
||||
"mode": "disabled",
|
||||
"enabled": false,
|
||||
},
|
||||
"acl": map[string]interface{}{
|
||||
"acl": map[string]any{
|
||||
"mode": "disabled",
|
||||
"enabled": false,
|
||||
},
|
||||
@@ -63,22 +63,22 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
ACLMode: "enabled",
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: map[string]interface{}{
|
||||
"cerberus": map[string]interface{}{"enabled": true},
|
||||
"crowdsec": map[string]interface{}{
|
||||
expectedBody: map[string]any{
|
||||
"cerberus": map[string]any{"enabled": true},
|
||||
"crowdsec": map[string]any{
|
||||
"mode": "local",
|
||||
"api_url": "",
|
||||
"enabled": true,
|
||||
},
|
||||
"waf": map[string]interface{}{
|
||||
"waf": map[string]any{
|
||||
"mode": "enabled",
|
||||
"enabled": true,
|
||||
},
|
||||
"rate_limit": map[string]interface{}{
|
||||
"rate_limit": map[string]any{
|
||||
"mode": "enabled",
|
||||
"enabled": true,
|
||||
},
|
||||
"acl": map[string]interface{}{
|
||||
"acl": map[string]any{
|
||||
"mode": "enabled",
|
||||
"enabled": true,
|
||||
},
|
||||
@@ -98,12 +98,12 @@ func TestSecurityHandler_GetStatus_Fixed(t *testing.T) {
|
||||
|
||||
assert.Equal(t, tt.expectedStatus, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedJSON, _ := json.Marshal(tt.expectedBody)
|
||||
var expectedNormalized map[string]interface{}
|
||||
var expectedNormalized map[string]any
|
||||
if err := json.Unmarshal(expectedJSON, &expectedNormalized); err != nil {
|
||||
t.Fatalf("failed to unmarshal expected JSON: %v", err)
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ func TestSecurityHandler_GetWAFExclusions_Empty(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 0)
|
||||
}
|
||||
|
||||
@@ -57,14 +57,14 @@ func TestSecurityHandler_GetWAFExclusions_WithExclusions(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 2)
|
||||
|
||||
// Verify first exclusion
|
||||
first := exclusions[0].(map[string]interface{})
|
||||
first := exclusions[0].(map[string]any)
|
||||
assert.Equal(t, float64(942100), first["rule_id"])
|
||||
assert.Equal(t, "SQL Injection rule", first["description"])
|
||||
}
|
||||
@@ -88,10 +88,10 @@ func TestSecurityHandler_GetWAFExclusions_InvalidJSON(t *testing.T) {
|
||||
|
||||
// Should return empty array on parse failure
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 0)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestSecurityHandler_AddWAFExclusion_Success(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 942100,
|
||||
"description": "SQL Injection false positive",
|
||||
}
|
||||
@@ -117,11 +117,11 @@ func TestSecurityHandler_AddWAFExclusion_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
exclusion := resp["exclusion"].(map[string]interface{})
|
||||
exclusion := resp["exclusion"].(map[string]any)
|
||||
assert.Equal(t, float64(942100), exclusion["rule_id"])
|
||||
assert.Equal(t, "SQL Injection false positive", exclusion["description"])
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func TestSecurityHandler_AddWAFExclusion_WithTarget(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 942100,
|
||||
"target": "ARGS:password",
|
||||
"description": "Skip password field for SQL injection",
|
||||
@@ -148,11 +148,11 @@ func TestSecurityHandler_AddWAFExclusion_WithTarget(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
exclusion := resp["exclusion"].(map[string]interface{})
|
||||
exclusion := resp["exclusion"].(map[string]any)
|
||||
assert.Equal(t, "ARGS:password", exclusion["target"])
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func TestSecurityHandler_AddWAFExclusion_ToExistingConfig(t *testing.T) {
|
||||
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
||||
|
||||
// Add new exclusion
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 942100,
|
||||
"description": "SQL Injection rule",
|
||||
}
|
||||
@@ -190,9 +190,9 @@ func TestSecurityHandler_AddWAFExclusion_ToExistingConfig(t *testing.T) {
|
||||
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 2)
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func TestSecurityHandler_AddWAFExclusion_Duplicate(t *testing.T) {
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
// Try to add duplicate
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 942100,
|
||||
"description": "Another description",
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func TestSecurityHandler_AddWAFExclusion_DuplicateWithDifferentTarget(t *testing
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
// Add same rule_id with different target - should succeed
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 942100,
|
||||
"target": "ARGS:password",
|
||||
}
|
||||
@@ -263,7 +263,7 @@ func TestSecurityHandler_AddWAFExclusion_MissingRuleID(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"description": "Missing rule_id",
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -286,7 +286,7 @@ func TestSecurityHandler_AddWAFExclusion_InvalidRuleID(t *testing.T) {
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
// Zero rule_id
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 0,
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -308,7 +308,7 @@ func TestSecurityHandler_AddWAFExclusion_NegativeRuleID(t *testing.T) {
|
||||
router := gin.New()
|
||||
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": -1,
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
@@ -359,7 +359,7 @@ func TestSecurityHandler_DeleteWAFExclusion_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.True(t, resp["deleted"].(bool))
|
||||
|
||||
@@ -369,9 +369,9 @@ func TestSecurityHandler_DeleteWAFExclusion_Success(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 1)
|
||||
first := exclusions[0].(map[string]interface{})
|
||||
first := exclusions[0].(map[string]any)
|
||||
assert.Equal(t, float64(941100), first["rule_id"])
|
||||
}
|
||||
|
||||
@@ -402,11 +402,11 @@ func TestSecurityHandler_DeleteWAFExclusion_WithTarget(t *testing.T) {
|
||||
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 1)
|
||||
first := exclusions[0].(map[string]interface{})
|
||||
first := exclusions[0].(map[string]any)
|
||||
assert.Equal(t, float64(942100), first["rule_id"])
|
||||
assert.Empty(t, first["target"])
|
||||
}
|
||||
@@ -513,12 +513,12 @@ func TestSecurityHandler_WAFExclusion_FullWorkflow(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Len(t, resp["exclusions"].([]interface{}), 0)
|
||||
assert.Len(t, resp["exclusions"].([]any), 0)
|
||||
|
||||
// Step 2: Add first exclusion (full rule removal)
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"rule_id": 942100,
|
||||
"description": "SQL Injection false positive",
|
||||
}
|
||||
@@ -530,7 +530,7 @@ func TestSecurityHandler_WAFExclusion_FullWorkflow(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// Step 3: Add second exclusion (targeted)
|
||||
payload = map[string]interface{}{
|
||||
payload = map[string]any{
|
||||
"rule_id": 941100,
|
||||
"target": "ARGS:content",
|
||||
"description": "XSS false positive in content field",
|
||||
@@ -547,7 +547,7 @@ func TestSecurityHandler_WAFExclusion_FullWorkflow(t *testing.T) {
|
||||
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Len(t, resp["exclusions"].([]interface{}), 2)
|
||||
assert.Len(t, resp["exclusions"].([]any), 2)
|
||||
|
||||
// Step 5: Delete first exclusion
|
||||
w = httptest.NewRecorder()
|
||||
@@ -560,9 +560,9 @@ func TestSecurityHandler_WAFExclusion_FullWorkflow(t *testing.T) {
|
||||
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
exclusions := resp["exclusions"].([]interface{})
|
||||
exclusions := resp["exclusions"].([]any)
|
||||
assert.Len(t, exclusions, 1)
|
||||
first := exclusions[0].(map[string]interface{})
|
||||
first := exclusions[0].(map[string]any)
|
||||
assert.Equal(t, float64(941100), first["rule_id"])
|
||||
assert.Equal(t, "ARGS:content", first["target"])
|
||||
}
|
||||
@@ -683,7 +683,7 @@ func TestSecurityConfig_WAFExclusions_JSONArray(t *testing.T) {
|
||||
assert.Equal(t, exclusions, retrieved.WAFExclusions)
|
||||
|
||||
// Verify it can be parsed
|
||||
var parsed []map[string]interface{}
|
||||
var parsed []map[string]any
|
||||
err := json.Unmarshal([]byte(retrieved.WAFExclusions), &parsed)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, parsed, 1)
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestGetProfile_NotFound(t *testing.T) {
|
||||
func TestCreateProfile(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"name": "New Profile",
|
||||
"hsts_enabled": true,
|
||||
"hsts_max_age": 31536000,
|
||||
@@ -145,7 +145,7 @@ func TestCreateProfile(t *testing.T) {
|
||||
func TestCreateProfile_MissingName(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"hsts_enabled": true,
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func TestUpdateProfile(t *testing.T) {
|
||||
}
|
||||
db.Create(&profile)
|
||||
|
||||
updates := map[string]interface{}{
|
||||
updates := map[string]any{
|
||||
"name": "Updated Name",
|
||||
"hsts_enabled": false,
|
||||
"csp_enabled": true,
|
||||
@@ -200,7 +200,7 @@ func TestUpdateProfile_CannotModifyPreset(t *testing.T) {
|
||||
}
|
||||
db.Create(&preset)
|
||||
|
||||
updates := map[string]interface{}{
|
||||
updates := map[string]any{
|
||||
"name": "Modified Preset",
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ func TestGetPresets(t *testing.T) {
|
||||
func TestApplyPreset(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"preset_type": "basic",
|
||||
"name": "My Basic Profile",
|
||||
}
|
||||
@@ -330,7 +330,7 @@ func TestApplyPreset(t *testing.T) {
|
||||
func TestApplyPreset_InvalidType(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"preset_type": "nonexistent",
|
||||
"name": "Test",
|
||||
}
|
||||
@@ -347,7 +347,7 @@ func TestApplyPreset_InvalidType(t *testing.T) {
|
||||
func TestCalculateScore(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"hsts_enabled": true,
|
||||
"hsts_max_age": 31536000,
|
||||
"hsts_include_subdomains": true,
|
||||
@@ -371,7 +371,7 @@ func TestCalculateScore(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, float64(100), response["score"])
|
||||
@@ -382,7 +382,7 @@ func TestCalculateScore(t *testing.T) {
|
||||
func TestValidateCSP_Valid(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"csp": `{"default-src":["'self'"],"script-src":["'self'"]}`,
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ func TestValidateCSP_Valid(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, response["valid"].(bool))
|
||||
@@ -403,7 +403,7 @@ func TestValidateCSP_Valid(t *testing.T) {
|
||||
func TestValidateCSP_Invalid(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"csp": `not valid json`,
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ func TestValidateCSP_Invalid(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, response["valid"].(bool))
|
||||
@@ -425,7 +425,7 @@ func TestValidateCSP_Invalid(t *testing.T) {
|
||||
func TestValidateCSP_UnsafeDirectives(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"csp": `{"default-src":["'self'"],"script-src":["'self'","'unsafe-inline'","'unsafe-eval'"]}`,
|
||||
}
|
||||
|
||||
@@ -437,19 +437,19 @@ func TestValidateCSP_UnsafeDirectives(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, response["valid"].(bool))
|
||||
errors := response["errors"].([]interface{})
|
||||
errors := response["errors"].([]any)
|
||||
assert.NotEmpty(t, errors)
|
||||
}
|
||||
|
||||
func TestBuildCSP(t *testing.T) {
|
||||
router, _ := setupSecurityHeadersTestRouter(t)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"directives": []map[string]interface{}{
|
||||
payload := map[string]any{
|
||||
"directives": []map[string]any{
|
||||
{
|
||||
"directive": "default-src",
|
||||
"values": []string{"'self'"},
|
||||
|
||||
@@ -99,11 +99,11 @@ func TestSecurityHandler_Priority_SettingsOverSecurityConfig(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
waf := response["waf"].(map[string]interface{})
|
||||
waf := response["waf"].(map[string]any)
|
||||
assert.Equal(t, tt.expectedWAFMode, waf["mode"].(string), "WAF mode mismatch")
|
||||
assert.Equal(t, tt.expectedWAFEnable, waf["enabled"].(bool), "WAF enabled mismatch")
|
||||
})
|
||||
@@ -156,21 +156,21 @@ func TestSecurityHandler_Priority_AllModules(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify Settings table took precedence
|
||||
waf := response["waf"].(map[string]interface{})
|
||||
waf := response["waf"].(map[string]any)
|
||||
assert.True(t, waf["enabled"].(bool), "WAF should be enabled via settings")
|
||||
|
||||
rateLimit := response["rate_limit"].(map[string]interface{})
|
||||
rateLimit := response["rate_limit"].(map[string]any)
|
||||
assert.False(t, rateLimit["enabled"].(bool), "Rate Limit should be disabled via settings")
|
||||
|
||||
crowdsec := response["crowdsec"].(map[string]interface{})
|
||||
crowdsec := response["crowdsec"].(map[string]any)
|
||||
assert.Equal(t, "disabled", crowdsec["mode"].(string), "CrowdSec should be disabled via settings")
|
||||
assert.False(t, crowdsec["enabled"].(bool))
|
||||
|
||||
acl := response["acl"].(map[string]interface{})
|
||||
acl := response["acl"].(map[string]any)
|
||||
assert.True(t, acl["enabled"].(bool), "ACL should be enabled via settings")
|
||||
}
|
||||
|
||||
@@ -27,18 +27,18 @@ func TestSecurityHandler_GetRateLimitPresets(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
presets, ok := response["presets"].([]interface{})
|
||||
presets, ok := response["presets"].([]any)
|
||||
require.True(t, ok, "presets should be an array")
|
||||
require.Len(t, presets, 4, "should have 4 presets")
|
||||
|
||||
// Verify preset structure
|
||||
expectedIDs := []string{"standard", "api", "login", "relaxed"}
|
||||
for i, p := range presets {
|
||||
preset := p.(map[string]interface{})
|
||||
preset := p.(map[string]any)
|
||||
assert.Equal(t, expectedIDs[i], preset["id"])
|
||||
assert.NotEmpty(t, preset["name"])
|
||||
assert.NotEmpty(t, preset["description"])
|
||||
@@ -60,12 +60,12 @@ func TestSecurityHandler_GetRateLimitPresets_StandardPreset(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/security/rate-limit/presets", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
presets := response["presets"].([]interface{})
|
||||
standardPreset := presets[0].(map[string]interface{})
|
||||
presets := response["presets"].([]any)
|
||||
standardPreset := presets[0].(map[string]any)
|
||||
|
||||
assert.Equal(t, "standard", standardPreset["id"])
|
||||
assert.Equal(t, "Standard Web", standardPreset["name"])
|
||||
@@ -86,12 +86,12 @@ func TestSecurityHandler_GetRateLimitPresets_LoginPreset(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/security/rate-limit/presets", http.NoBody)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
presets := response["presets"].([]interface{})
|
||||
loginPreset := presets[2].(map[string]interface{})
|
||||
presets := response["presets"].([]any)
|
||||
loginPreset := presets[2].(map[string]any)
|
||||
|
||||
assert.Equal(t, "login", loginPreset["id"])
|
||||
assert.Equal(t, "Login Protection", loginPreset["name"])
|
||||
|
||||
@@ -153,7 +153,7 @@ func TestSettingsHandler_GetSMTPConfig(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, "smtp.example.com", resp["host"])
|
||||
assert.Equal(t, float64(587), resp["port"])
|
||||
@@ -174,7 +174,7 @@ func TestSettingsHandler_GetSMTPConfig_Empty(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["configured"])
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func TestSettingsHandler_UpdateSMTPConfig_NonAdmin(t *testing.T) {
|
||||
})
|
||||
router.PUT("/settings/smtp", handler.UpdateSMTPConfig)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"host": "smtp.example.com",
|
||||
"port": 587,
|
||||
"from_address": "test@example.com",
|
||||
@@ -251,7 +251,7 @@ func TestSettingsHandler_UpdateSMTPConfig_Success(t *testing.T) {
|
||||
})
|
||||
router.PUT("/settings/smtp", handler.UpdateSMTPConfig)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"host": "smtp.example.com",
|
||||
"port": 587,
|
||||
"username": "user@example.com",
|
||||
@@ -287,7 +287,7 @@ func TestSettingsHandler_UpdateSMTPConfig_KeepExistingPassword(t *testing.T) {
|
||||
router.PUT("/settings/smtp", handler.UpdateSMTPConfig)
|
||||
|
||||
// Send masked password (simulating frontend sending back masked value)
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"host": "smtp.example.com",
|
||||
"port": 587,
|
||||
"password": "********", // Masked
|
||||
@@ -342,7 +342,7 @@ func TestSettingsHandler_TestSMTPConfig_NotConfigured(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["success"])
|
||||
}
|
||||
@@ -406,7 +406,7 @@ func TestSettingsHandler_SendTestEmail_NotConfigured(t *testing.T) {
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, false, resp["success"])
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (h *UptimeHandler) GetHistory(c *gin.Context) {
|
||||
|
||||
func (h *UptimeHandler) Update(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var updates map[string]interface{}
|
||||
var updates map[string]any
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
logger.Log().WithError(err).WithField("monitor_id", id).Warn("Invalid JSON payload for monitor update")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
|
||||
@@ -138,7 +138,7 @@ func TestUptimeHandler_Update(t *testing.T) {
|
||||
}
|
||||
db.Create(&monitor)
|
||||
|
||||
updates := map[string]interface{}{
|
||||
updates := map[string]any{
|
||||
"interval": 60,
|
||||
"max_retries": 5,
|
||||
}
|
||||
@@ -172,7 +172,7 @@ func TestUptimeHandler_Update(t *testing.T) {
|
||||
t.Run("not_found", func(t *testing.T) {
|
||||
r, _ := setupUptimeHandlerTest(t)
|
||||
|
||||
updates := map[string]interface{}{
|
||||
updates := map[string]any{
|
||||
"interval": 60,
|
||||
}
|
||||
body, _ := json.Marshal(updates)
|
||||
@@ -226,7 +226,7 @@ func TestUptimeHandler_DeleteAndSync(t *testing.T) {
|
||||
monitor := models.UptimeMonitor{ID: "mon-enable", Name: "ToToggle", Type: "http", URL: "http://example.com", Enabled: true}
|
||||
db.Create(&monitor)
|
||||
|
||||
updates := map[string]interface{}{"enabled": false}
|
||||
updates := map[string]any{"enabled": false}
|
||||
body, _ := json.Marshal(updates)
|
||||
req, _ := http.NewRequest("PUT", "/api/v1/uptime/mon-enable", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -232,7 +232,7 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.DB.Model(&models.User{}).Where("id = ?", userID).Updates(map[string]interface{}{
|
||||
if err := h.DB.Model(&models.User{}).Where("id = ?", userID).Updates(map[string]any{
|
||||
"name": req.Name,
|
||||
"email": req.Email,
|
||||
}).Error; err != nil {
|
||||
@@ -600,7 +600,7 @@ func (h *UserHandler) UpdateUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
updates := make(map[string]interface{})
|
||||
updates := make(map[string]any)
|
||||
|
||||
if req.Name != "" {
|
||||
updates["name"] = req.Name
|
||||
@@ -813,7 +813,7 @@ func (h *UserHandler) AcceptInvite(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.DB.Model(&user).Updates(map[string]interface{}{
|
||||
if err := h.DB.Model(&user).Updates(map[string]any{
|
||||
"name": req.Name,
|
||||
"password_hash": user.PasswordHash,
|
||||
"enabled": true,
|
||||
|
||||
@@ -438,7 +438,7 @@ func TestUserHandler_ListUsers_Admin(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var users []map[string]interface{}
|
||||
var users []map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &users)
|
||||
assert.Len(t, users, 2)
|
||||
}
|
||||
@@ -453,7 +453,7 @@ func TestUserHandler_CreateUser_NonAdmin(t *testing.T) {
|
||||
})
|
||||
r.POST("/users", handler.CreateUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"email": "new@example.com",
|
||||
"name": "New User",
|
||||
"password": "password123",
|
||||
@@ -477,7 +477,7 @@ func TestUserHandler_CreateUser_Admin(t *testing.T) {
|
||||
})
|
||||
r.POST("/users", handler.CreateUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"email": "newuser@example.com",
|
||||
"name": "New User",
|
||||
"password": "password123",
|
||||
@@ -523,7 +523,7 @@ func TestUserHandler_CreateUser_DuplicateEmail(t *testing.T) {
|
||||
})
|
||||
r.POST("/users", handler.CreateUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"email": "existing@example.com",
|
||||
"name": "New User",
|
||||
"password": "password123",
|
||||
@@ -551,7 +551,7 @@ func TestUserHandler_CreateUser_WithPermittedHosts(t *testing.T) {
|
||||
})
|
||||
r.POST("/users", handler.CreateUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"email": "withhosts@example.com",
|
||||
"name": "User With Hosts",
|
||||
"password": "password123",
|
||||
@@ -649,7 +649,7 @@ func TestUserHandler_UpdateUser_NonAdmin(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id", handler.UpdateUser)
|
||||
|
||||
body := map[string]interface{}{"name": "Updated"}
|
||||
body := map[string]any{"name": "Updated"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/users/1", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -669,7 +669,7 @@ func TestUserHandler_UpdateUser_InvalidID(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id", handler.UpdateUser)
|
||||
|
||||
body := map[string]interface{}{"name": "Updated"}
|
||||
body := map[string]any{"name": "Updated"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/users/invalid", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -712,7 +712,7 @@ func TestUserHandler_UpdateUser_NotFound(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id", handler.UpdateUser)
|
||||
|
||||
body := map[string]interface{}{"name": "Updated"}
|
||||
body := map[string]any{"name": "Updated"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/users/999", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -736,7 +736,7 @@ func TestUserHandler_UpdateUser_Success(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id", handler.UpdateUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"name": "Updated Name",
|
||||
"enabled": true,
|
||||
}
|
||||
@@ -855,7 +855,7 @@ func TestUserHandler_UpdateUserPermissions_NonAdmin(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id/permissions", handler.UpdateUserPermissions)
|
||||
|
||||
body := map[string]interface{}{"permission_mode": "allow_all"}
|
||||
body := map[string]any{"permission_mode": "allow_all"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/users/1/permissions", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -875,7 +875,7 @@ func TestUserHandler_UpdateUserPermissions_InvalidID(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id/permissions", handler.UpdateUserPermissions)
|
||||
|
||||
body := map[string]interface{}{"permission_mode": "allow_all"}
|
||||
body := map[string]any{"permission_mode": "allow_all"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/users/invalid/permissions", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -925,7 +925,7 @@ func TestUserHandler_UpdateUserPermissions_NotFound(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id/permissions", handler.UpdateUserPermissions)
|
||||
|
||||
body := map[string]interface{}{"permission_mode": "allow_all"}
|
||||
body := map[string]any{"permission_mode": "allow_all"}
|
||||
jsonBody, _ := json.Marshal(body)
|
||||
req := httptest.NewRequest("PUT", "/users/999/permissions", bytes.NewBuffer(jsonBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -957,7 +957,7 @@ func TestUserHandler_UpdateUserPermissions_Success(t *testing.T) {
|
||||
})
|
||||
r.PUT("/users/:id/permissions", handler.UpdateUserPermissions)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"permission_mode": "deny_all",
|
||||
"permitted_hosts": []uint{host.ID},
|
||||
}
|
||||
@@ -1069,7 +1069,7 @@ func TestUserHandler_ValidateInvite_Success(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.Equal(t, "valid@example.com", resp["email"])
|
||||
}
|
||||
@@ -1249,7 +1249,7 @@ func TestUserHandler_InviteUser_Success(t *testing.T) {
|
||||
})
|
||||
r.POST("/users/invite", handler.InviteUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"email": "newinvite@example.com",
|
||||
"role": "user",
|
||||
}
|
||||
@@ -1261,7 +1261,7 @@ func TestUserHandler_InviteUser_Success(t *testing.T) {
|
||||
|
||||
assert.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NotEmpty(t, resp["invite_token"])
|
||||
// email_sent is false because no SMTP is configured
|
||||
@@ -1303,7 +1303,7 @@ func TestUserHandler_InviteUser_WithPermittedHosts(t *testing.T) {
|
||||
})
|
||||
r.POST("/users/invite", handler.InviteUser)
|
||||
|
||||
body := map[string]interface{}{
|
||||
body := map[string]any{
|
||||
"email": "invitee-perms@example.com",
|
||||
"permission_mode": "deny_all",
|
||||
"permitted_hosts": []uint{host.ID},
|
||||
|
||||
@@ -54,12 +54,12 @@ func TestWebSocketStatusHandler_GetConnections(t *testing.T) {
|
||||
// Verify response
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, float64(2), response["count"])
|
||||
connections, ok := response["connections"].([]interface{})
|
||||
connections, ok := response["connections"].([]any)
|
||||
require.True(t, ok)
|
||||
assert.Len(t, connections, 2)
|
||||
}
|
||||
@@ -81,12 +81,12 @@ func TestWebSocketStatusHandler_GetConnectionsEmpty(t *testing.T) {
|
||||
// Verify response
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, float64(0), response["count"])
|
||||
connections, ok := response["connections"].([]interface{})
|
||||
connections, ok := response["connections"].([]any)
|
||||
require.True(t, ok)
|
||||
assert.Len(t, connections, 0)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func Recovery(verbose bool) gin.HandlerFunc {
|
||||
// Try to get a request-scoped logger; fall back to global logger
|
||||
entry := GetRequestLogger(c)
|
||||
if verbose {
|
||||
entry.WithFields(map[string]interface{}{
|
||||
entry.WithFields(map[string]any{
|
||||
"method": c.Request.Method,
|
||||
"path": SanitizePath(c.Request.URL.Path),
|
||||
"headers": SanitizeHeaders(c.Request.Header),
|
||||
|
||||
@@ -2,6 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Wikid82/charon/backend/internal/logger"
|
||||
"github.com/Wikid82/charon/backend/internal/trace"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -18,7 +19,7 @@ func RequestID() gin.HandlerFunc {
|
||||
c.Set(string(trace.RequestIDKey), rid)
|
||||
c.Writer.Header().Set(RequestIDHeader, rid)
|
||||
// Add to logger fields for this request
|
||||
entry := logger.WithFields(map[string]interface{}{"request_id": rid})
|
||||
entry := logger.WithFields(map[string]any{"request_id": rid})
|
||||
c.Set("logger", entry)
|
||||
// Propagate into the request context so it can be used by services
|
||||
ctx := context.WithValue(c.Request.Context(), trace.RequestIDKey, rid)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/Wikid82/charon/backend/internal/util"
|
||||
"time"
|
||||
|
||||
"github.com/Wikid82/charon/backend/internal/util"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -14,7 +15,7 @@ func RequestLogger() gin.HandlerFunc {
|
||||
c.Next()
|
||||
latency := time.Since(start)
|
||||
entry := GetRequestLogger(c)
|
||||
entry.WithFields(map[string]interface{}{
|
||||
entry.WithFields(map[string]any{
|
||||
"status": c.Writer.Status(),
|
||||
"method": c.Request.Method,
|
||||
"path": SanitizePath(c.Request.URL.Path),
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestInviteToken_MustBeUnguessable(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
|
||||
token := resp["invite_token"].(string)
|
||||
@@ -239,7 +239,7 @@ func TestAcceptInvite_PasswordValidation(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Reset user to pending state for each test
|
||||
db.Model(&user).Updates(map[string]interface{}{
|
||||
db.Model(&user).Updates(map[string]any{
|
||||
"invite_status": "pending",
|
||||
"enabled": false,
|
||||
"password_hash": "",
|
||||
@@ -369,7 +369,7 @@ func TestSMTPConfig_PasswordMasked(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
|
||||
// Password MUST be masked
|
||||
@@ -397,7 +397,7 @@ func TestSMTPConfig_PortValidation(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"host": "smtp.test.com",
|
||||
"port": tc.port,
|
||||
"from_address": "test@test.com",
|
||||
@@ -432,7 +432,7 @@ func TestSMTPConfig_EncryptionValidation(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"host": "smtp.test.com",
|
||||
"port": 587,
|
||||
"from_address": "test@test.com",
|
||||
@@ -549,7 +549,7 @@ func TestUpdatePermissions_ValidModes(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"permission_mode": tc.mode,
|
||||
"permitted_hosts": []int{},
|
||||
})
|
||||
|
||||
@@ -179,7 +179,7 @@ func TestClient_NetworkErrors(t *testing.T) {
|
||||
func TestClient_Load_MarshalFailure(t *testing.T) {
|
||||
// Simulate json.Marshal failure
|
||||
orig := jsonMarshalClient
|
||||
jsonMarshalClient = func(v interface{}) ([]byte, error) { return nil, fmt.Errorf("marshal error") }
|
||||
jsonMarshalClient = func(v any) ([]byte, error) { return nil, fmt.Errorf("marshal error") }
|
||||
defer func() { jsonMarshalClient = orig }()
|
||||
|
||||
client := NewClient("http://localhost")
|
||||
|
||||
@@ -74,12 +74,12 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
}
|
||||
|
||||
if acmeEmail != "" {
|
||||
var issuers []interface{}
|
||||
var issuers []any
|
||||
|
||||
// Configure issuers based on provider preference
|
||||
switch sslProvider {
|
||||
case "letsencrypt":
|
||||
acmeIssuer := map[string]interface{}{
|
||||
acmeIssuer := map[string]any{
|
||||
"module": "acme",
|
||||
"email": acmeEmail,
|
||||
}
|
||||
@@ -88,11 +88,11 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
}
|
||||
issuers = append(issuers, acmeIssuer)
|
||||
case "zerossl":
|
||||
issuers = append(issuers, map[string]interface{}{
|
||||
issuers = append(issuers, map[string]any{
|
||||
"module": "zerossl",
|
||||
})
|
||||
default: // "both" or empty
|
||||
acmeIssuer := map[string]interface{}{
|
||||
acmeIssuer := map[string]any{
|
||||
"module": "acme",
|
||||
"email": acmeEmail,
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
acmeIssuer["ca"] = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
}
|
||||
issuers = append(issuers, acmeIssuer)
|
||||
issuers = append(issuers, map[string]interface{}{
|
||||
issuers = append(issuers, map[string]any{
|
||||
"module": "zerossl",
|
||||
})
|
||||
}
|
||||
@@ -243,8 +243,8 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
// Build a subroute to match these remote IPs and serve 403
|
||||
// Admin whitelist exclusion must be applied: exclude adminWhitelist if present
|
||||
// Build matchParts
|
||||
var matchParts []map[string]interface{}
|
||||
matchParts = append(matchParts, map[string]interface{}{"remote_ip": map[string]interface{}{"ranges": decisionIPs}})
|
||||
var matchParts []map[string]any
|
||||
matchParts = append(matchParts, map[string]any{"remote_ip": map[string]any{"ranges": decisionIPs}})
|
||||
if adminWhitelist != "" {
|
||||
adminParts := strings.Split(adminWhitelist, ",")
|
||||
trims := make([]string, 0)
|
||||
@@ -256,15 +256,15 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
trims = append(trims, p)
|
||||
}
|
||||
if len(trims) > 0 {
|
||||
matchParts = append(matchParts, map[string]interface{}{"not": []map[string]interface{}{{"remote_ip": map[string]interface{}{"ranges": trims}}}})
|
||||
matchParts = append(matchParts, map[string]any{"not": []map[string]any{{"remote_ip": map[string]any{"ranges": trims}}}})
|
||||
}
|
||||
}
|
||||
decHandler := Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
"match": matchParts,
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
{
|
||||
"handler": "static_response",
|
||||
"status_code": 403,
|
||||
@@ -355,12 +355,12 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
// Insert user advanced config (if present) as headers or handlers before the reverse proxy
|
||||
// so user-specified headers/handlers are applied prior to proxying.
|
||||
if host.AdvancedConfig != "" {
|
||||
var parsed interface{}
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(host.AdvancedConfig), &parsed); err != nil {
|
||||
logger.Log().WithField("host", host.UUID).WithError(err).Warn("Failed to parse advanced_config for host")
|
||||
} else {
|
||||
switch v := parsed.(type) {
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
// Append as a handler
|
||||
// Ensure it has a "handler" key
|
||||
if _, ok := v["handler"]; ok {
|
||||
@@ -382,9 +382,9 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
} else {
|
||||
logger.Log().WithField("host", host.UUID).Warn("advanced_config for host is not a handler object")
|
||||
}
|
||||
case []interface{}:
|
||||
case []any:
|
||||
for _, it := range v {
|
||||
if m, ok := it.(map[string]interface{}); ok {
|
||||
if m, ok := it.(map[string]any); ok {
|
||||
if rn, has := m["ruleset_name"]; has {
|
||||
if rnStr, ok := rn.(string); ok && rnStr != "" {
|
||||
if rulesetPaths != nil {
|
||||
@@ -474,7 +474,7 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
}
|
||||
policy := &AutomationPolicy{
|
||||
Subjects: ipSubjects,
|
||||
IssuersRaw: []interface{}{map[string]interface{}{"module": "internal"}},
|
||||
IssuersRaw: []any{map[string]any{"module": "internal"}},
|
||||
}
|
||||
if config.Apps.TLS.Automation == nil {
|
||||
config.Apps.TLS.Automation = &AutomationConfig{}
|
||||
@@ -487,26 +487,26 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir, acmeEmail, frontendDir
|
||||
|
||||
// normalizeHandlerHeaders ensures header values in handlers are arrays of strings
|
||||
// Caddy's JSON schema expects header values to be an array of strings (e.g. ["websocket"]) rather than a single string.
|
||||
func normalizeHandlerHeaders(h map[string]interface{}) {
|
||||
func normalizeHandlerHeaders(h map[string]any) {
|
||||
// normalize top-level headers key
|
||||
if headersRaw, ok := h["headers"].(map[string]interface{}); ok {
|
||||
if headersRaw, ok := h["headers"].(map[string]any); ok {
|
||||
normalizeHeaderOps(headersRaw)
|
||||
}
|
||||
// also normalize in nested request/response if present explicitly
|
||||
for _, side := range []string{"request", "response"} {
|
||||
if sideRaw, ok := h[side].(map[string]interface{}); ok {
|
||||
if sideRaw, ok := h[side].(map[string]any); ok {
|
||||
normalizeHeaderOps(sideRaw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeHeaderOps(headerOps map[string]interface{}) {
|
||||
if setRaw, ok := headerOps["set"].(map[string]interface{}); ok {
|
||||
func normalizeHeaderOps(headerOps map[string]any) {
|
||||
if setRaw, ok := headerOps["set"].(map[string]any); ok {
|
||||
for k, v := range setRaw {
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
setRaw[k] = []string{vv}
|
||||
case []interface{}:
|
||||
case []any:
|
||||
// convert to []string
|
||||
arr := make([]string, 0, len(vv))
|
||||
for _, it := range vv {
|
||||
@@ -527,25 +527,25 @@ func normalizeHeaderOps(headerOps map[string]interface{}) {
|
||||
// NormalizeAdvancedConfig traverses a parsed JSON advanced config (map or array)
|
||||
// and normalizes any headers blocks so that header values are arrays of strings.
|
||||
// It returns the modified config object which can be JSON marshaled again.
|
||||
func NormalizeAdvancedConfig(parsed interface{}) interface{} {
|
||||
func NormalizeAdvancedConfig(parsed any) any {
|
||||
switch v := parsed.(type) {
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
// This might be a handler object
|
||||
normalizeHandlerHeaders(v)
|
||||
// Also inspect nested 'handle' or 'routes' arrays for nested handlers
|
||||
if handles, ok := v["handle"].([]interface{}); ok {
|
||||
if handles, ok := v["handle"].([]any); ok {
|
||||
for _, it := range handles {
|
||||
if m, ok := it.(map[string]interface{}); ok {
|
||||
if m, ok := it.(map[string]any); ok {
|
||||
NormalizeAdvancedConfig(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
if routes, ok := v["routes"].([]interface{}); ok {
|
||||
if routes, ok := v["routes"].([]any); ok {
|
||||
for _, rit := range routes {
|
||||
if rm, ok := rit.(map[string]interface{}); ok {
|
||||
if handles, ok := rm["handle"].([]interface{}); ok {
|
||||
if rm, ok := rit.(map[string]any); ok {
|
||||
if handles, ok := rm["handle"].([]any); ok {
|
||||
for _, it := range handles {
|
||||
if m, ok := it.(map[string]interface{}); ok {
|
||||
if m, ok := it.(map[string]any); ok {
|
||||
NormalizeAdvancedConfig(m)
|
||||
}
|
||||
}
|
||||
@@ -554,9 +554,9 @@ func NormalizeAdvancedConfig(parsed interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
return v
|
||||
case []interface{}:
|
||||
case []any:
|
||||
for _, it := range v {
|
||||
if m, ok := it.(map[string]interface{}); ok {
|
||||
if m, ok := it.(map[string]any); ok {
|
||||
NormalizeAdvancedConfig(m)
|
||||
}
|
||||
}
|
||||
@@ -586,18 +586,18 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
// For whitelist, block when NOT in the list
|
||||
return Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
"match": []map[string]interface{}{
|
||||
"match": []map[string]any{
|
||||
{
|
||||
"not": []map[string]interface{}{
|
||||
"not": []map[string]any{
|
||||
{
|
||||
"expression": expression,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
{
|
||||
"handler": "static_response",
|
||||
"status_code": 403,
|
||||
@@ -613,14 +613,14 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
expression = fmt.Sprintf("{geoip2.country_code} in [%s]", strings.Join(trimmedCodes, ", "))
|
||||
return Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
"match": []map[string]interface{}{
|
||||
"match": []map[string]any{
|
||||
{
|
||||
"expression": expression,
|
||||
},
|
||||
},
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
{
|
||||
"handler": "static_response",
|
||||
"status_code": 403,
|
||||
@@ -638,13 +638,13 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
// Allow only RFC1918 private networks
|
||||
return Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
"match": []map[string]interface{}{
|
||||
"match": []map[string]any{
|
||||
{
|
||||
"not": []map[string]interface{}{
|
||||
"not": []map[string]any{
|
||||
{
|
||||
"remote_ip": map[string]interface{}{
|
||||
"remote_ip": map[string]any{
|
||||
"ranges": []string{
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
@@ -660,7 +660,7 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
},
|
||||
},
|
||||
},
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
{
|
||||
"handler": "static_response",
|
||||
"status_code": 403,
|
||||
@@ -708,20 +708,20 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
}
|
||||
return Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
"match": []map[string]interface{}{
|
||||
"match": []map[string]any{
|
||||
{
|
||||
"not": []map[string]interface{}{
|
||||
"not": []map[string]any{
|
||||
{
|
||||
"remote_ip": map[string]interface{}{
|
||||
"remote_ip": map[string]any{
|
||||
"ranges": cidrs,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
{
|
||||
"handler": "static_response",
|
||||
"status_code": 403,
|
||||
@@ -737,7 +737,7 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
if acl.Type == "blacklist" {
|
||||
// Block these IPs (allow everything else)
|
||||
// For blacklist, add an explicit 'not' clause excluding adminWhitelist ranges from the match
|
||||
var adminExclusion interface{}
|
||||
var adminExclusion any
|
||||
if adminWhitelist != "" {
|
||||
adminParts := strings.Split(adminWhitelist, ",")
|
||||
trims := make([]string, 0)
|
||||
@@ -749,21 +749,21 @@ func buildACLHandler(acl *models.AccessList, adminWhitelist string) (Handler, er
|
||||
trims = append(trims, p)
|
||||
}
|
||||
if len(trims) > 0 {
|
||||
adminExclusion = map[string]interface{}{"not": []map[string]interface{}{{"remote_ip": map[string]interface{}{"ranges": trims}}}}
|
||||
adminExclusion = map[string]any{"not": []map[string]any{{"remote_ip": map[string]any{"ranges": trims}}}}
|
||||
}
|
||||
}
|
||||
// Build matcher parts
|
||||
matchParts := []map[string]interface{}{}
|
||||
matchParts = append(matchParts, map[string]interface{}{"remote_ip": map[string]interface{}{"ranges": cidrs}})
|
||||
matchParts := []map[string]any{}
|
||||
matchParts = append(matchParts, map[string]any{"remote_ip": map[string]any{"ranges": cidrs}})
|
||||
if adminExclusion != nil {
|
||||
matchParts = append(matchParts, adminExclusion.(map[string]interface{}))
|
||||
matchParts = append(matchParts, adminExclusion.(map[string]any))
|
||||
}
|
||||
return Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
"match": matchParts,
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
{
|
||||
"handler": "static_response",
|
||||
"status_code": 403,
|
||||
@@ -876,7 +876,7 @@ func buildWAFHandler(host *models.ProxyHost, rulesets []models.SecurityRuleSet,
|
||||
// If the host provided an advanced_config containing a 'ruleset_name', prefer that value
|
||||
var hostRulesetName string
|
||||
if host != nil && host.AdvancedConfig != "" {
|
||||
var ac map[string]interface{}
|
||||
var ac map[string]any
|
||||
if err := json.Unmarshal([]byte(host.AdvancedConfig), &ac); err == nil {
|
||||
if rn, ok := ac["ruleset_name"]; ok {
|
||||
if rnStr, ok2 := rn.(string); ok2 && rnStr != "" {
|
||||
@@ -1062,8 +1062,8 @@ func buildRateLimitHandler(_ *models.ProxyHost, secCfg *models.SecurityConfig) (
|
||||
// Note: The caddy-ratelimit module uses a sliding window algorithm
|
||||
// and does not have a separate burst parameter
|
||||
rateLimitHandler := Handler{"handler": "rate_limit"}
|
||||
rateLimitHandler["rate_limits"] = map[string]interface{}{
|
||||
"static": map[string]interface{}{
|
||||
rateLimitHandler["rate_limits"] = map[string]any{
|
||||
"static": map[string]any{
|
||||
"key": "{http.request.remote.host}",
|
||||
"window": fmt.Sprintf("%ds", secCfg.RateLimitWindowSec),
|
||||
"max_events": secCfg.RateLimitRequests,
|
||||
@@ -1084,22 +1084,22 @@ func buildRateLimitHandler(_ *models.ProxyHost, secCfg *models.SecurityConfig) (
|
||||
// 2. Everything else -> apply rate limiting
|
||||
return Handler{
|
||||
"handler": "subroute",
|
||||
"routes": []map[string]interface{}{
|
||||
"routes": []map[string]any{
|
||||
{
|
||||
// Route 1: Match bypass IPs - terminal with no handlers (skip rate limiting)
|
||||
"match": []map[string]interface{}{
|
||||
"match": []map[string]any{
|
||||
{
|
||||
"remote_ip": map[string]interface{}{
|
||||
"remote_ip": map[string]any{
|
||||
"ranges": bypassCIDRs,
|
||||
},
|
||||
},
|
||||
},
|
||||
// No handlers - just pass through without rate limiting
|
||||
"handle": []map[string]interface{}{},
|
||||
"handle": []map[string]any{},
|
||||
},
|
||||
{
|
||||
// Route 2: Default - apply rate limiting to everyone else
|
||||
"handle": []map[string]interface{}{
|
||||
"handle": []map[string]any{
|
||||
rateLimitHandler,
|
||||
},
|
||||
},
|
||||
@@ -1238,7 +1238,7 @@ func buildSecurityHeadersHandler(host *models.ProxyHost) (Handler, error) {
|
||||
|
||||
return Handler{
|
||||
"handler": "headers",
|
||||
"response": map[string]interface{}{
|
||||
"response": map[string]any{
|
||||
"set": responseHeaders,
|
||||
},
|
||||
}, nil
|
||||
|
||||
@@ -45,9 +45,9 @@ func TestGenerateConfig_AdvancedInvalidJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateConfig_AdvancedArrayHandler(t *testing.T) {
|
||||
array := []map[string]interface{}{{
|
||||
array := []map[string]any{{
|
||||
"handler": "headers",
|
||||
"response": map[string]interface{}{
|
||||
"response": map[string]any{
|
||||
"set": map[string][]string{"X-Test": {"1"}},
|
||||
},
|
||||
}}
|
||||
@@ -119,12 +119,12 @@ func TestGenerateConfig_AdvancedHeadersStringToArray(t *testing.T) {
|
||||
require.Equal(t, "headers", first["handler"])
|
||||
|
||||
// request.set.Upgrade should be an array
|
||||
if req, ok := first["request"].(map[string]interface{}); ok {
|
||||
if set, ok := req["set"].(map[string]interface{}); ok {
|
||||
if req, ok := first["request"].(map[string]any); ok {
|
||||
if set, ok := req["set"].(map[string]any); ok {
|
||||
switch val := set["Upgrade"].(type) {
|
||||
case []string:
|
||||
require.Equal(t, []string{"websocket"}, val)
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var out []string
|
||||
for _, v := range val {
|
||||
out = append(out, fmt.Sprintf("%v", v))
|
||||
@@ -141,12 +141,12 @@ func TestGenerateConfig_AdvancedHeadersStringToArray(t *testing.T) {
|
||||
}
|
||||
|
||||
// response.set.X-Obj should be an array
|
||||
if resp, ok := first["response"].(map[string]interface{}); ok {
|
||||
if set, ok := resp["set"].(map[string]interface{}); ok {
|
||||
if resp, ok := first["response"].(map[string]any); ok {
|
||||
if set, ok := resp["set"].(map[string]any); ok {
|
||||
switch val := set["X-Obj"].(type) {
|
||||
case []string:
|
||||
require.Equal(t, []string{"1"}, val)
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var out []string
|
||||
for _, v := range val {
|
||||
out = append(out, fmt.Sprintf("%v", v))
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestGenerateConfig_ZerosslAndBothProviders(t *testing.T) {
|
||||
issuers := cfgZ.Apps.TLS.Automation.Policies[0].IssuersRaw
|
||||
foundZerossl := false
|
||||
for _, i := range issuers {
|
||||
m := i.(map[string]interface{})
|
||||
m := i.(map[string]any)
|
||||
if m["module"] == "zerossl" {
|
||||
foundZerossl = true
|
||||
}
|
||||
@@ -251,13 +251,13 @@ func TestGenerateConfig_DecisionAdminPartsEmpty(t *testing.T) {
|
||||
|
||||
func TestNormalizeHeaderOps_PreserveStringArray(t *testing.T) {
|
||||
// Construct a headers map where set has a []string value already
|
||||
set := map[string]interface{}{
|
||||
set := map[string]any{
|
||||
"X-Array": []string{"1", "2"},
|
||||
}
|
||||
headerOps := map[string]interface{}{"set": set}
|
||||
headerOps := map[string]any{"set": set}
|
||||
normalizeHeaderOps(headerOps)
|
||||
// Ensure the value remained a []string
|
||||
if v, ok := headerOps["set"].(map[string]interface{}); ok {
|
||||
if v, ok := headerOps["set"].(map[string]any); ok {
|
||||
if arr, ok := v["X-Array"].([]string); ok {
|
||||
require.Equal(t, []string{"1", "2"}, arr)
|
||||
return
|
||||
@@ -366,8 +366,8 @@ func TestGenerateConfig_RateLimitFromSecCfg(t *testing.T) {
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "rate_limit" {
|
||||
// Check caddy-ratelimit format: rate_limits.static.max_events and window
|
||||
if rateLimits, ok := h["rate_limits"].(map[string]interface{}); ok {
|
||||
if static, ok := rateLimits["static"].(map[string]interface{}); ok {
|
||||
if rateLimits, ok := h["rate_limits"].(map[string]any); ok {
|
||||
if static, ok := rateLimits["static"].(map[string]any); ok {
|
||||
if maxEvents, ok := static["max_events"].(int); ok && maxEvents == 10 {
|
||||
if window, ok := static["window"].(string); ok && window == "60s" {
|
||||
found = true
|
||||
@@ -463,7 +463,7 @@ func TestGenerateConfig_DefaultAcmeStaging(t *testing.T) {
|
||||
issuers := cfg.Apps.TLS.Automation.Policies[0].IssuersRaw
|
||||
found := false
|
||||
for _, i := range issuers {
|
||||
if m, ok := i.(map[string]interface{}); ok {
|
||||
if m, ok := i.(map[string]any); ok {
|
||||
if m["module"] == "acme" {
|
||||
if _, ok := m["ca"]; ok {
|
||||
found = true
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestBuildSecurityHeadersHandler_AllEnabled(t *testing.T) {
|
||||
assert.NotNil(t, handler)
|
||||
assert.Equal(t, "headers", handler["handler"])
|
||||
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
assert.Contains(t, headers["Strict-Transport-Security"][0], "max-age=31536000")
|
||||
@@ -73,7 +73,7 @@ func TestBuildSecurityHeadersHandler_HSTSOnly(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, handler)
|
||||
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
assert.Contains(t, headers["Strict-Transport-Security"][0], "max-age=31536000")
|
||||
@@ -103,7 +103,7 @@ func TestBuildSecurityHeadersHandler_CSPOnly(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, handler)
|
||||
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
assert.NotContains(t, headers, "Strict-Transport-Security")
|
||||
@@ -129,7 +129,7 @@ func TestBuildSecurityHeadersHandler_CSPReportOnly(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, handler)
|
||||
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
assert.NotContains(t, headers, "Content-Security-Policy")
|
||||
@@ -147,7 +147,7 @@ func TestBuildSecurityHeadersHandler_NoProfile(t *testing.T) {
|
||||
assert.NotNil(t, handler)
|
||||
|
||||
// Should use defaults
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
assert.Contains(t, headers, "Strict-Transport-Security")
|
||||
@@ -314,7 +314,7 @@ func TestBuildSecurityHeadersHandler_PermissionsPolicy(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, handler)
|
||||
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
assert.Contains(t, headers, "Permissions-Policy")
|
||||
@@ -341,7 +341,7 @@ func TestBuildSecurityHeadersHandler_InvalidCSPJSON(t *testing.T) {
|
||||
assert.NotNil(t, handler)
|
||||
|
||||
// Should skip CSP if invalid JSON
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
assert.NotContains(t, headers, "Content-Security-Policy")
|
||||
// But should include the other header
|
||||
@@ -394,7 +394,7 @@ func TestBuildSecurityHeadersHandler_APIFriendlyPreset(t *testing.T) {
|
||||
assert.NotNil(t, handler)
|
||||
assert.Equal(t, "headers", handler["handler"])
|
||||
|
||||
response := handler["response"].(map[string]interface{})
|
||||
response := handler["response"].(map[string]any)
|
||||
headers := response["set"].(map[string][]string)
|
||||
|
||||
// Verify HSTS is present
|
||||
|
||||
@@ -170,7 +170,7 @@ func TestGenerateConfig_IPHostsSkipAutoHTTPS(t *testing.T) {
|
||||
if p.Subjects[0] == "192.0.2.10" {
|
||||
foundIPPolicy = true
|
||||
require.Len(t, p.IssuersRaw, 1)
|
||||
issuer := p.IssuersRaw[0].(map[string]interface{})
|
||||
issuer := p.IssuersRaw[0].(map[string]any)
|
||||
require.Equal(t, "internal", issuer["module"])
|
||||
}
|
||||
}
|
||||
@@ -259,7 +259,7 @@ func TestGenerateConfig_ACMEStaging(t *testing.T) {
|
||||
issuers := config.Apps.TLS.Automation.Policies[0].IssuersRaw
|
||||
require.Len(t, issuers, 1)
|
||||
|
||||
acmeIssuer := issuers[0].(map[string]interface{})
|
||||
acmeIssuer := issuers[0].(map[string]any)
|
||||
require.Equal(t, "acme", acmeIssuer["module"])
|
||||
require.Equal(t, "admin@example.com", acmeIssuer["email"])
|
||||
require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", acmeIssuer["ca"])
|
||||
@@ -274,7 +274,7 @@ func TestGenerateConfig_ACMEStaging(t *testing.T) {
|
||||
issuers = config.Apps.TLS.Automation.Policies[0].IssuersRaw
|
||||
require.Len(t, issuers, 1)
|
||||
|
||||
acmeIssuer = issuers[0].(map[string]interface{})
|
||||
acmeIssuer = issuers[0].(map[string]any)
|
||||
require.Equal(t, "acme", acmeIssuer["module"])
|
||||
require.Equal(t, "admin@example.com", acmeIssuer["email"])
|
||||
_, hasCA := acmeIssuer["ca"]
|
||||
@@ -401,10 +401,10 @@ func TestBuildRateLimitHandler_ValidConfig(t *testing.T) {
|
||||
require.Equal(t, "rate_limit", h["handler"])
|
||||
|
||||
// Verify rate_limits structure
|
||||
rateLimits, ok := h["rate_limits"].(map[string]interface{})
|
||||
rateLimits, ok := h["rate_limits"].(map[string]any)
|
||||
require.True(t, ok, "rate_limits should be a map")
|
||||
|
||||
staticZone, ok := rateLimits["static"].(map[string]interface{})
|
||||
staticZone, ok := rateLimits["static"].(map[string]any)
|
||||
require.True(t, ok, "static zone should be a map")
|
||||
|
||||
// Verify caddy-ratelimit specific fields
|
||||
@@ -498,9 +498,9 @@ func TestBuildRateLimitHandler_UsesBurst(t *testing.T) {
|
||||
// Handler should be a plain rate_limit (no bypass list)
|
||||
require.Equal(t, "rate_limit", h["handler"])
|
||||
|
||||
rateLimits, ok := h["rate_limits"].(map[string]interface{})
|
||||
rateLimits, ok := h["rate_limits"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
staticZone, ok := rateLimits["static"].(map[string]interface{})
|
||||
staticZone, ok := rateLimits["static"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
|
||||
// Verify burst field is NOT present (not supported by caddy-ratelimit)
|
||||
@@ -519,9 +519,9 @@ func TestBuildRateLimitHandler_DefaultBurst(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, h)
|
||||
|
||||
rateLimits, ok := h["rate_limits"].(map[string]interface{})
|
||||
rateLimits, ok := h["rate_limits"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
staticZone, ok := rateLimits["static"].(map[string]interface{})
|
||||
staticZone, ok := rateLimits["static"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
|
||||
// Verify burst field is NOT present
|
||||
@@ -538,9 +538,9 @@ func TestBuildRateLimitHandler_DefaultBurst(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, h2)
|
||||
|
||||
rateLimits2, ok := h2["rate_limits"].(map[string]interface{})
|
||||
rateLimits2, ok := h2["rate_limits"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
staticZone2, ok := rateLimits2["static"].(map[string]interface{})
|
||||
staticZone2, ok := rateLimits2["static"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
|
||||
// Verify no burst field here either
|
||||
|
||||
@@ -44,7 +44,7 @@ type CaddyHTTP struct {
|
||||
type CaddyServer struct {
|
||||
Listen []string `json:"listen,omitempty"`
|
||||
Routes []*CaddyRoute `json:"routes,omitempty"`
|
||||
TLSConnectionPolicies interface{} `json:"tls_connection_policies,omitempty"`
|
||||
TLSConnectionPolicies any `json:"tls_connection_policies,omitempty"`
|
||||
}
|
||||
|
||||
// CaddyRoute represents a single route with matchers and handlers.
|
||||
@@ -60,10 +60,10 @@ type CaddyMatcher struct {
|
||||
|
||||
// CaddyHandler represents a handler in the route.
|
||||
type CaddyHandler struct {
|
||||
Handler string `json:"handler"`
|
||||
Upstreams interface{} `json:"upstreams,omitempty"`
|
||||
Headers interface{} `json:"headers,omitempty"`
|
||||
Routes interface{} `json:"routes,omitempty"` // For subroute handlers
|
||||
Handler string `json:"handler"`
|
||||
Upstreams any `json:"upstreams,omitempty"`
|
||||
Headers any `json:"headers,omitempty"`
|
||||
Routes any `json:"routes,omitempty"` // For subroute handlers
|
||||
}
|
||||
|
||||
// ParsedHost represents a single host detected during Caddyfile import.
|
||||
@@ -139,21 +139,21 @@ func (i *Importer) extractHandlers(handles []*CaddyHandler) []*CaddyHandler {
|
||||
}
|
||||
|
||||
// It's a subroute; extract handlers from its first route
|
||||
routes, ok := handler.Routes.([]interface{})
|
||||
routes, ok := handler.Routes.([]any)
|
||||
if !ok || len(routes) == 0 {
|
||||
continue
|
||||
}
|
||||
subroute, ok := routes[0].(map[string]interface{})
|
||||
subroute, ok := routes[0].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
subhandles, ok := subroute["handle"].([]interface{})
|
||||
subhandles, ok := subroute["handle"].([]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// Convert the subhandles to CaddyHandler objects
|
||||
for _, sh := range subhandles {
|
||||
shMap, ok := sh.(map[string]interface{})
|
||||
shMap, ok := sh.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -227,9 +227,9 @@ func (i *Importer) ExtractHosts(caddyJSON []byte) (*ImportResult, error) {
|
||||
|
||||
for _, handler := range handlers {
|
||||
if handler.Handler == "reverse_proxy" {
|
||||
upstreams, _ := handler.Upstreams.([]interface{})
|
||||
upstreams, _ := handler.Upstreams.([]any)
|
||||
if len(upstreams) > 0 {
|
||||
if upstream, ok := upstreams[0].(map[string]interface{}); ok {
|
||||
if upstream, ok := upstreams[0].(map[string]any); ok {
|
||||
dial, _ := upstream["dial"].(string)
|
||||
if dial != "" {
|
||||
hostStr, portStr, err := net.SplitHostPort(dial)
|
||||
@@ -258,8 +258,8 @@ func (i *Importer) ExtractHosts(caddyJSON []byte) (*ImportResult, error) {
|
||||
}
|
||||
|
||||
// Check for websocket support
|
||||
if headers, ok := handler.Headers.(map[string]interface{}); ok {
|
||||
if upgrade, ok := headers["Upgrade"].([]interface{}); ok {
|
||||
if headers, ok := handler.Headers.(map[string]any); ok {
|
||||
if upgrade, ok := headers["Upgrade"].([]any); ok {
|
||||
for _, v := range upgrade {
|
||||
if v == "websocket" {
|
||||
host.WebsocketSupport = true
|
||||
@@ -286,7 +286,7 @@ func (i *Importer) ExtractHosts(caddyJSON []byte) (*ImportResult, error) {
|
||||
}
|
||||
|
||||
// Store raw JSON for this route
|
||||
routeJSON, _ := json.Marshal(map[string]interface{}{
|
||||
routeJSON, _ := json.Marshal(map[string]any{
|
||||
"server": serverName,
|
||||
"route": routeIdx,
|
||||
"data": route,
|
||||
|
||||
@@ -22,13 +22,13 @@ func TestImporter_ExtractHosts_TLSConnectionPolicyAndDialWithoutPort(t *testing.
|
||||
{
|
||||
Match: []*CaddyMatcher{{Host: []string{"example.com"}}},
|
||||
Handle: []*CaddyHandler{
|
||||
{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "app:9000"}}},
|
||||
{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "app:9000"}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Match: []*CaddyMatcher{{Host: []string{"nport.example.com"}}},
|
||||
Handle: []*CaddyHandler{
|
||||
{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "app"}}},
|
||||
{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "app"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -52,7 +52,7 @@ func TestImporter_ExtractHosts_TLSConnectionPolicyAndDialWithoutPort(t *testing.
|
||||
func TestExtractHandlers_Subroute_WithUnsupportedSubhandle(t *testing.T) {
|
||||
// Build a handler with subroute whose handle contains a non-map item
|
||||
h := []*CaddyHandler{
|
||||
{Handler: "subroute", Routes: []interface{}{map[string]interface{}{"handle": []interface{}{"not-a-map", map[string]interface{}{"handler": "reverse_proxy"}}}}},
|
||||
{Handler: "subroute", Routes: []any{map[string]any{"handle": []any{"not-a-map", map[string]any{"handler": "reverse_proxy"}}}}},
|
||||
}
|
||||
importer := NewImporter("")
|
||||
res := importer.extractHandlers(h)
|
||||
@@ -63,7 +63,7 @@ func TestExtractHandlers_Subroute_WithUnsupportedSubhandle(t *testing.T) {
|
||||
|
||||
func TestExtractHandlers_Subroute_WithNonMapRoutes(t *testing.T) {
|
||||
h := []*CaddyHandler{
|
||||
{Handler: "subroute", Routes: []interface{}{"not-a-map"}},
|
||||
{Handler: "subroute", Routes: []any{"not-a-map"}},
|
||||
}
|
||||
importer := NewImporter("")
|
||||
res := importer.extractHandlers(h)
|
||||
@@ -76,7 +76,7 @@ func TestImporter_ExtractHosts_UpstreamsNonMapAndWarnings(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"warn.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{"nonnmap"}}, {Handler: "rewrite"}, {Handler: "file_server"}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{"nonnmap"}}, {Handler: "rewrite"}, {Handler: "file_server"}},
|
||||
}},
|
||||
}}}},
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func TestBackupCaddyfile_ReadFailure(t *testing.T) {
|
||||
func TestExtractHandlers_Subroute_EmptyAndHandleNotArray(t *testing.T) {
|
||||
// Empty routes array
|
||||
h := []*CaddyHandler{
|
||||
{Handler: "subroute", Routes: []interface{}{}},
|
||||
{Handler: "subroute", Routes: []any{}},
|
||||
}
|
||||
importer := NewImporter("")
|
||||
res := importer.extractHandlers(h)
|
||||
@@ -107,7 +107,7 @@ func TestExtractHandlers_Subroute_EmptyAndHandleNotArray(t *testing.T) {
|
||||
|
||||
// Routes with a map but handle is not an array
|
||||
h2 := []*CaddyHandler{
|
||||
{Handler: "subroute", Routes: []interface{}{map[string]interface{}{"handle": "not-an-array"}}},
|
||||
{Handler: "subroute", Routes: []any{map[string]any{"handle": "not-an-array"}}},
|
||||
}
|
||||
res2 := importer.extractHandlers(h2)
|
||||
require.Len(t, res2, 0)
|
||||
@@ -118,7 +118,7 @@ func TestImporter_ExtractHosts_ReverseProxyNoUpstreams(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"noups.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -147,16 +147,16 @@ func TestBackupCaddyfile_Success(t *testing.T) {
|
||||
|
||||
func TestExtractHandlers_Subroute_WithHeadersUpstreams(t *testing.T) {
|
||||
h := []*CaddyHandler{
|
||||
{Handler: "subroute", Routes: []interface{}{map[string]interface{}{"handle": []interface{}{map[string]interface{}{"handler": "reverse_proxy", "upstreams": []interface{}{map[string]interface{}{"dial": "app:8080"}}, "headers": map[string]interface{}{"Upgrade": []interface{}{"websocket"}}}}}}},
|
||||
{Handler: "subroute", Routes: []any{map[string]any{"handle": []any{map[string]any{"handler": "reverse_proxy", "upstreams": []any{map[string]any{"dial": "app:8080"}}, "headers": map[string]any{"Upgrade": []any{"websocket"}}}}}}},
|
||||
}
|
||||
importer := NewImporter("")
|
||||
res := importer.extractHandlers(h)
|
||||
require.Len(t, res, 1)
|
||||
require.Equal(t, "reverse_proxy", res[0].Handler)
|
||||
// Upstreams should be present in extracted handler
|
||||
_, ok := res[0].Upstreams.([]interface{})
|
||||
_, ok := res[0].Upstreams.([]any)
|
||||
require.True(t, ok)
|
||||
_, ok = res[0].Headers.(map[string]interface{})
|
||||
_, ok = res[0].Headers.(map[string]any)
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
@@ -169,14 +169,14 @@ func TestImporter_ExtractHosts_DuplicateHost(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"dup.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "one:80"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "one:80"}}}},
|
||||
}},
|
||||
},
|
||||
"srv2": {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"dup.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "two:80"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "two:80"}}}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
@@ -215,7 +215,7 @@ func TestImporter_ExtractHosts_SSLForcedByDomainScheme(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"https://secure.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "one:80"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "one:80"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -232,7 +232,7 @@ func TestImporter_ExtractHosts_MultipleHostsInMatch(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"m1.example.com", "m2.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "one:80"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "one:80"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -247,7 +247,7 @@ func TestImporter_ExtractHosts_UpgradeHeaderAsString(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"ws.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "one:80"}}, Headers: map[string]interface{}{"Upgrade": []string{"websocket"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "one:80"}}, Headers: map[string]any{"Upgrade": []string{"websocket"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -265,7 +265,7 @@ func TestImporter_ExtractHosts_SscanfFailureOnPort(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"sscanf.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "127.0.0.1:eighty"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "127.0.0.1:eighty"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -283,7 +283,7 @@ func TestImporter_ExtractHosts_PartsSscanfFail(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"parts.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "tcp/127.0.0.1:badport"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "tcp/127.0.0.1:badport"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -300,7 +300,7 @@ func TestImporter_ExtractHosts_PartsEmptyPortField(t *testing.T) {
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"emptyparts.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "tcp/127.0.0.1:"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "tcp/127.0.0.1:"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -321,7 +321,7 @@ func TestImporter_ExtractHosts_ForceSplitFallback_PartsNumericPort(t *testing.T)
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"forced.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "127.0.0.1:8181"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "127.0.0.1:8181"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
@@ -343,7 +343,7 @@ func TestImporter_ExtractHosts_ForceSplitFallback_PartsSscanfFail(t *testing.T)
|
||||
Listen: []string{":80"},
|
||||
Routes: []*CaddyRoute{{
|
||||
Match: []*CaddyMatcher{{Host: []string{"forcedfail.example.com"}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []interface{}{map[string]interface{}{"dial": "127.0.0.1:notnum"}}}},
|
||||
Handle: []*CaddyHandler{{Handler: "reverse_proxy", Upstreams: []any{map[string]any{"dial": "127.0.0.1:notnum"}}}},
|
||||
}},
|
||||
}}}}}
|
||||
b, _ := json.Marshal(cfg)
|
||||
|
||||
@@ -61,18 +61,18 @@ func TestExtractHandlers_Subroute(t *testing.T) {
|
||||
t.Fatal("Upstreams should not be nil")
|
||||
}
|
||||
|
||||
upstreams, ok := handlers[1].Upstreams.([]interface{})
|
||||
upstreams, ok := handlers[1].Upstreams.([]any)
|
||||
if !ok {
|
||||
t.Fatal("Upstreams should be []interface{}")
|
||||
t.Fatal("Upstreams should be []any")
|
||||
}
|
||||
|
||||
if len(upstreams) == 0 {
|
||||
t.Fatal("Upstreams should not be empty")
|
||||
}
|
||||
|
||||
upstream, ok := upstreams[0].(map[string]interface{})
|
||||
upstream, ok := upstreams[0].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("First upstream should be map[string]interface{}")
|
||||
t.Fatal("First upstream should be map[string]any")
|
||||
}
|
||||
|
||||
dial, ok := upstream["dial"].(string)
|
||||
|
||||
@@ -231,7 +231,7 @@ func (m *Manager) ApplyConfig(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Debug logging: WAF configuration state for troubleshooting integration issues
|
||||
logger.Log().WithFields(map[string]interface{}{
|
||||
logger.Log().WithFields(map[string]any{
|
||||
"waf_enabled": wafEnabled,
|
||||
"waf_mode": secCfg.WAFMode,
|
||||
"waf_rules_source": secCfg.WAFRulesSource,
|
||||
@@ -239,7 +239,7 @@ func (m *Manager) ApplyConfig(ctx context.Context) error {
|
||||
"ruleset_paths_len": len(rulesetPaths),
|
||||
}).Debug("WAF configuration state")
|
||||
for rsName, rsPath := range rulesetPaths {
|
||||
logger.Log().WithFields(map[string]interface{}{
|
||||
logger.Log().WithFields(map[string]any{
|
||||
"ruleset_name": rsName,
|
||||
"ruleset_path": rsPath,
|
||||
}).Debug("WAF ruleset path mapping")
|
||||
|
||||
@@ -373,7 +373,7 @@ func TestManager_SaveSnapshot_MarshalError(t *testing.T) {
|
||||
manager := NewManager(nil, nil, tmp, "", false, config.SecurityConfig{})
|
||||
// Stub jsonMarshallFunc to return error
|
||||
orig := jsonMarshalFunc
|
||||
jsonMarshalFunc = func(v interface{}, prefix, indent string) ([]byte, error) {
|
||||
jsonMarshalFunc = func(v any, prefix, indent string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("marshal fail")
|
||||
}
|
||||
defer func() { jsonMarshalFunc = orig }()
|
||||
@@ -915,12 +915,12 @@ func TestManager_ApplyConfig_ReappliesOnFlagChange(t *testing.T) {
|
||||
if h == "flag.example.com" {
|
||||
for _, handle := range r.Handle {
|
||||
if handlerName, ok := handle["handler"].(string); ok && handlerName == "subroute" {
|
||||
if routes, ok := handle["routes"].([]interface{}); ok {
|
||||
if routes, ok := handle["routes"].([]any); ok {
|
||||
for _, rt := range routes {
|
||||
if rtMap, ok := rt.(map[string]interface{}); ok {
|
||||
if inner, ok := rtMap["handle"].([]interface{}); ok {
|
||||
if rtMap, ok := rt.(map[string]any); ok {
|
||||
if inner, ok := rtMap["handle"].([]any); ok {
|
||||
for _, itm := range inner {
|
||||
if itmMap, ok := itm.(map[string]interface{}); ok {
|
||||
if itmMap, ok := itm.(map[string]any); ok {
|
||||
if body, ok := itmMap["body"].(string); ok {
|
||||
if strings.Contains(body, "Access denied") {
|
||||
found = true
|
||||
@@ -959,12 +959,12 @@ func TestManager_ApplyConfig_ReappliesOnFlagChange(t *testing.T) {
|
||||
if h == "flag.example.com" {
|
||||
for _, handle := range r.Handle {
|
||||
if handlerName, ok := handle["handler"].(string); ok && handlerName == "subroute" {
|
||||
if routes, ok := handle["routes"].([]interface{}); ok {
|
||||
if routes, ok := handle["routes"].([]any); ok {
|
||||
for _, rt := range routes {
|
||||
if rtMap, ok := rt.(map[string]interface{}); ok {
|
||||
if inner, ok := rtMap["handle"].([]interface{}); ok {
|
||||
if rtMap, ok := rt.(map[string]any); ok {
|
||||
if inner, ok := rtMap["handle"].([]any); ok {
|
||||
for _, itm := range inner {
|
||||
if itmMap, ok := itm.(map[string]interface{}); ok {
|
||||
if itmMap, ok := itm.(map[string]any); ok {
|
||||
if body, ok := itmMap["body"].(string); ok {
|
||||
if strings.Contains(body, "Access denied") {
|
||||
found = true
|
||||
@@ -1162,7 +1162,7 @@ func TestManager_ApplyConfig_DebugMarshalFailure(t *testing.T) {
|
||||
|
||||
// Stub jsonMarshalDebugFunc to return an error (exercises the else branch in debug logging)
|
||||
origMarshalDebug := jsonMarshalDebugFunc
|
||||
jsonMarshalDebugFunc = func(v interface{}) ([]byte, error) {
|
||||
jsonMarshalDebugFunc = func(v any) ([]byte, error) {
|
||||
return nil, fmt.Errorf("simulated marshal error")
|
||||
}
|
||||
defer func() { jsonMarshalDebugFunc = origMarshalDebug }()
|
||||
|
||||
@@ -10,18 +10,18 @@ import (
|
||||
|
||||
func TestNormalizeAdvancedConfig_MapWithNestedHandles(t *testing.T) {
|
||||
// Build a map with nested 'handle' array containing headers with string values
|
||||
raw := map[string]interface{}{
|
||||
raw := map[string]any{
|
||||
"handler": "subroute",
|
||||
"routes": []interface{}{
|
||||
map[string]interface{}{
|
||||
"handle": []interface{}{
|
||||
map[string]interface{}{
|
||||
"routes": []any{
|
||||
map[string]any{
|
||||
"handle": []any{
|
||||
map[string]any{
|
||||
"handler": "headers",
|
||||
"request": map[string]interface{}{
|
||||
"set": map[string]interface{}{"Upgrade": "websocket"},
|
||||
"request": map[string]any{
|
||||
"set": map[string]any{"Upgrade": "websocket"},
|
||||
},
|
||||
"response": map[string]interface{}{
|
||||
"set": map[string]interface{}{"X-Obj": "1"},
|
||||
"response": map[string]any{
|
||||
"set": map[string]any{"X-Obj": "1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -31,21 +31,21 @@ func TestNormalizeAdvancedConfig_MapWithNestedHandles(t *testing.T) {
|
||||
|
||||
out := NormalizeAdvancedConfig(raw)
|
||||
// Verify nested header values normalized
|
||||
outMap, ok := out.(map[string]interface{})
|
||||
outMap, ok := out.(map[string]any)
|
||||
require.True(t, ok)
|
||||
routes := outMap["routes"].([]interface{})
|
||||
routes := outMap["routes"].([]any)
|
||||
require.Len(t, routes, 1)
|
||||
r := routes[0].(map[string]interface{})
|
||||
handles := r["handle"].([]interface{})
|
||||
r := routes[0].(map[string]any)
|
||||
handles := r["handle"].([]any)
|
||||
require.Len(t, handles, 1)
|
||||
hdr := handles[0].(map[string]interface{})
|
||||
hdr := handles[0].(map[string]any)
|
||||
|
||||
// request.set.Upgrade
|
||||
req := hdr["request"].(map[string]interface{})
|
||||
set := req["set"].(map[string]interface{})
|
||||
// Could be []interface{} or []string depending on code path; normalize to []string representation
|
||||
req := hdr["request"].(map[string]any)
|
||||
set := req["set"].(map[string]any)
|
||||
// Could be []any or []string depending on code path; normalize to []string representation
|
||||
switch v := set["Upgrade"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr []string
|
||||
for _, it := range v {
|
||||
outArr = append(outArr, fmt.Sprintf("%v", it))
|
||||
@@ -58,10 +58,10 @@ func TestNormalizeAdvancedConfig_MapWithNestedHandles(t *testing.T) {
|
||||
}
|
||||
|
||||
// response.set.X-Obj
|
||||
resp := hdr["response"].(map[string]interface{})
|
||||
rset := resp["set"].(map[string]interface{})
|
||||
resp := hdr["response"].(map[string]any)
|
||||
rset := resp["set"].(map[string]any)
|
||||
switch v := rset["X-Obj"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr []string
|
||||
for _, it := range v {
|
||||
outArr = append(outArr, fmt.Sprintf("%v", it))
|
||||
@@ -75,23 +75,23 @@ func TestNormalizeAdvancedConfig_MapWithNestedHandles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNormalizeAdvancedConfig_ArrayTopLevel(t *testing.T) {
|
||||
// Top-level array containing a headers handler with array value as []interface{}
|
||||
raw := []interface{}{
|
||||
map[string]interface{}{
|
||||
// Top-level array containing a headers handler with array value as []any
|
||||
raw := []any{
|
||||
map[string]any{
|
||||
"handler": "headers",
|
||||
"response": map[string]interface{}{
|
||||
"set": map[string]interface{}{"X-Obj": []interface{}{"1"}},
|
||||
"response": map[string]any{
|
||||
"set": map[string]any{"X-Obj": []any{"1"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
out := NormalizeAdvancedConfig(raw)
|
||||
outArr := out.([]interface{})
|
||||
outArr := out.([]any)
|
||||
require.Len(t, outArr, 1)
|
||||
hdr := outArr[0].(map[string]interface{})
|
||||
resp := hdr["response"].(map[string]interface{})
|
||||
set := resp["set"].(map[string]interface{})
|
||||
hdr := outArr[0].(map[string]any)
|
||||
resp := hdr["response"].(map[string]any)
|
||||
set := resp["set"].(map[string]any)
|
||||
switch v := set["X-Obj"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr2 []string
|
||||
for _, it := range v {
|
||||
outArr2 = append(outArr2, fmt.Sprintf("%v", it))
|
||||
@@ -114,13 +114,13 @@ func TestNormalizeAdvancedConfig_DefaultPrimitives(t *testing.T) {
|
||||
|
||||
func TestNormalizeAdvancedConfig_CoerceNonStandardTypes(t *testing.T) {
|
||||
// Use a header value that is numeric and ensure it's coerced to string
|
||||
raw := map[string]interface{}{"handler": "headers", "response": map[string]interface{}{"set": map[string]interface{}{"X-Num": 1}}}
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]interface{})
|
||||
resp := out["response"].(map[string]interface{})
|
||||
set := resp["set"].(map[string]interface{})
|
||||
raw := map[string]any{"handler": "headers", "response": map[string]any{"set": map[string]any{"X-Num": 1}}}
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
||||
resp := out["response"].(map[string]any)
|
||||
set := resp["set"].(map[string]any)
|
||||
// Should be a []string with "1"
|
||||
switch v := set["X-Num"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr []string
|
||||
for _, it := range v {
|
||||
outArr = append(outArr, fmt.Sprintf("%v", it))
|
||||
@@ -135,28 +135,28 @@ func TestNormalizeAdvancedConfig_CoerceNonStandardTypes(t *testing.T) {
|
||||
|
||||
func TestNormalizeAdvancedConfig_JSONRoundtrip(t *testing.T) {
|
||||
// Ensure normalized config can be marshaled back to JSON and unmarshaled
|
||||
raw := map[string]interface{}{"handler": "headers", "request": map[string]interface{}{"set": map[string]interface{}{"Upgrade": "websocket"}}}
|
||||
raw := map[string]any{"handler": "headers", "request": map[string]any{"set": map[string]any{"Upgrade": "websocket"}}}
|
||||
out := NormalizeAdvancedConfig(raw)
|
||||
b, err := json.Marshal(out)
|
||||
require.NoError(t, err)
|
||||
// Marshal back and read result
|
||||
var parsed interface{}
|
||||
var parsed any
|
||||
require.NoError(t, json.Unmarshal(b, &parsed))
|
||||
}
|
||||
|
||||
func TestNormalizeAdvancedConfig_TopLevelHeaders(t *testing.T) {
|
||||
// Top-level 'headers' key should be normalized similar to request/response
|
||||
raw := map[string]interface{}{
|
||||
raw := map[string]any{
|
||||
"handler": "headers",
|
||||
"headers": map[string]interface{}{
|
||||
"set": map[string]interface{}{"Upgrade": "websocket"},
|
||||
"headers": map[string]any{
|
||||
"set": map[string]any{"Upgrade": "websocket"},
|
||||
},
|
||||
}
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]interface{})
|
||||
hdrs := out["headers"].(map[string]interface{})
|
||||
set := hdrs["set"].(map[string]interface{})
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
||||
hdrs := out["headers"].(map[string]any)
|
||||
set := hdrs["set"].(map[string]any)
|
||||
switch v := set["Upgrade"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr []string
|
||||
for _, it := range v {
|
||||
outArr = append(outArr, fmt.Sprintf("%v", it))
|
||||
@@ -171,17 +171,17 @@ func TestNormalizeAdvancedConfig_TopLevelHeaders(t *testing.T) {
|
||||
|
||||
func TestNormalizeAdvancedConfig_HeadersAlreadyArray(t *testing.T) {
|
||||
// If the header value is already a []string it should be left as-is
|
||||
raw := map[string]interface{}{
|
||||
raw := map[string]any{
|
||||
"handler": "headers",
|
||||
"headers": map[string]interface{}{
|
||||
"set": map[string]interface{}{"X-Test": []string{"a", "b"}},
|
||||
"headers": map[string]any{
|
||||
"set": map[string]any{"X-Test": []string{"a", "b"}},
|
||||
},
|
||||
}
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]interface{})
|
||||
hdrs := out["headers"].(map[string]interface{})
|
||||
set := hdrs["set"].(map[string]interface{})
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
||||
hdrs := out["headers"].(map[string]any)
|
||||
set := hdrs["set"].(map[string]any)
|
||||
switch v := set["X-Test"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr []string
|
||||
for _, it := range v {
|
||||
outArr = append(outArr, fmt.Sprintf("%v", it))
|
||||
@@ -195,23 +195,23 @@ func TestNormalizeAdvancedConfig_HeadersAlreadyArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNormalizeAdvancedConfig_MapWithTopLevelHandle(t *testing.T) {
|
||||
raw := map[string]interface{}{
|
||||
raw := map[string]any{
|
||||
"handler": "subroute",
|
||||
"handle": []interface{}{
|
||||
map[string]interface{}{
|
||||
"handle": []any{
|
||||
map[string]any{
|
||||
"handler": "headers",
|
||||
"request": map[string]interface{}{"set": map[string]interface{}{"Upgrade": "websocket"}},
|
||||
"request": map[string]any{"set": map[string]any{"Upgrade": "websocket"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]interface{})
|
||||
handles := out["handle"].([]interface{})
|
||||
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
||||
handles := out["handle"].([]any)
|
||||
require.Len(t, handles, 1)
|
||||
hdr := handles[0].(map[string]interface{})
|
||||
req := hdr["request"].(map[string]interface{})
|
||||
set := req["set"].(map[string]interface{})
|
||||
hdr := handles[0].(map[string]any)
|
||||
req := hdr["request"].(map[string]any)
|
||||
set := req["set"].(map[string]any)
|
||||
switch v := set["Upgrade"].(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
var outArr []string
|
||||
for _, it := range v {
|
||||
outArr = append(outArr, fmt.Sprintf("%v", it))
|
||||
|
||||
@@ -119,7 +119,7 @@ type Match struct {
|
||||
|
||||
// Handler is the interface for all handler types.
|
||||
// Actual types will implement handler-specific fields.
|
||||
type Handler map[string]interface{}
|
||||
type Handler map[string]any
|
||||
|
||||
// ReverseProxyHandler creates a reverse_proxy handler.
|
||||
// application: "none", "plex", "jellyfin", "emby", "homeassistant", "nextcloud", "vaultwarden"
|
||||
@@ -128,14 +128,14 @@ func ReverseProxyHandler(dial string, enableWS bool, application string, enableS
|
||||
h := Handler{
|
||||
"handler": "reverse_proxy",
|
||||
"flush_interval": -1, // Disable buffering for better streaming performance (Plex, etc.)
|
||||
"upstreams": []map[string]interface{}{
|
||||
"upstreams": []map[string]any{
|
||||
{"dial": dial},
|
||||
},
|
||||
}
|
||||
|
||||
// Build headers configuration
|
||||
headers := make(map[string]interface{})
|
||||
requestHeaders := make(map[string]interface{})
|
||||
headers := make(map[string]any)
|
||||
requestHeaders := make(map[string]any)
|
||||
setHeaders := make(map[string][]string)
|
||||
|
||||
// STEP 1: Standard proxy headers (if feature enabled)
|
||||
@@ -202,7 +202,7 @@ func ReverseProxyHandler(dial string, enableWS bool, application string, enableS
|
||||
func HeaderHandler(headers map[string][]string) Handler {
|
||||
return Handler{
|
||||
"handler": "headers",
|
||||
"response": map[string]interface{}{
|
||||
"response": map[string]any{
|
||||
"set": headers,
|
||||
},
|
||||
}
|
||||
@@ -260,5 +260,5 @@ type AutomationConfig struct {
|
||||
// AutomationPolicy defines certificate management for specific domains.
|
||||
type AutomationPolicy struct {
|
||||
Subjects []string `json:"subjects,omitempty"`
|
||||
IssuersRaw []interface{} `json:"issuers,omitempty"`
|
||||
IssuersRaw []any `json:"issuers,omitempty"`
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ func TestReverseProxyHandler_PlexAndOthers(t *testing.T) {
|
||||
h := ReverseProxyHandler("app:32400", false, "plex", true)
|
||||
require.Equal(t, "reverse_proxy", h["handler"])
|
||||
// Assert headers exist
|
||||
if hdrs, ok := h["headers"].(map[string]interface{}); ok {
|
||||
req := hdrs["request"].(map[string]interface{})
|
||||
if hdrs, ok := h["headers"].(map[string]any); ok {
|
||||
req := hdrs["request"].(map[string]any)
|
||||
set := req["set"].(map[string][]string)
|
||||
require.Contains(t, set, "X-Plex-Client-Identifier")
|
||||
require.Contains(t, set, "X-Real-IP")
|
||||
@@ -27,8 +27,8 @@ func TestReverseProxyHandler_PlexAndOthers(t *testing.T) {
|
||||
// Jellyfin should include X-Real-IP and standard headers when enabled
|
||||
h2 := ReverseProxyHandler("app:8096", true, "jellyfin", true)
|
||||
require.Equal(t, "reverse_proxy", h2["handler"])
|
||||
if hdrs, ok := h2["headers"].(map[string]interface{}); ok {
|
||||
req := hdrs["request"].(map[string]interface{})
|
||||
if hdrs, ok := h2["headers"].(map[string]any); ok {
|
||||
req := hdrs["request"].(map[string]any)
|
||||
set := req["set"].(map[string][]string)
|
||||
require.Contains(t, set, "X-Real-IP")
|
||||
require.Contains(t, set, "X-Forwarded-Proto")
|
||||
@@ -52,10 +52,10 @@ func TestReverseProxyHandler_WebSocketHeaders(t *testing.T) {
|
||||
h := ReverseProxyHandler("app:8080", true, "none", true)
|
||||
require.Equal(t, "reverse_proxy", h["handler"])
|
||||
|
||||
hdrs, ok := h["headers"].(map[string]interface{})
|
||||
hdrs, ok := h["headers"].(map[string]any)
|
||||
require.True(t, ok, "expected headers map when enableWS=true and enableStandardHeaders=true")
|
||||
|
||||
req, ok := hdrs["request"].(map[string]interface{})
|
||||
req, ok := hdrs["request"].(map[string]any)
|
||||
require.True(t, ok, "expected request headers")
|
||||
|
||||
set, ok := req["set"].(map[string][]string)
|
||||
@@ -97,10 +97,10 @@ func TestReverseProxyHandler_StandardProxyHeadersAlwaysSet(t *testing.T) {
|
||||
require.Equal(t, "reverse_proxy", h["handler"])
|
||||
|
||||
// With enableStandardHeaders=true, headers should exist
|
||||
hdrs, ok := h["headers"].(map[string]interface{})
|
||||
hdrs, ok := h["headers"].(map[string]any)
|
||||
require.True(t, ok, "expected headers map when enableStandardHeaders=true")
|
||||
|
||||
req, ok := hdrs["request"].(map[string]interface{})
|
||||
req, ok := hdrs["request"].(map[string]any)
|
||||
require.True(t, ok, "expected request headers")
|
||||
|
||||
set, ok := req["set"].(map[string][]string)
|
||||
@@ -136,8 +136,8 @@ func TestReverseProxyHandler_StandardProxyHeadersAlwaysSet(t *testing.T) {
|
||||
func TestReverseProxyHandler_ApplicationSpecificHeaders(t *testing.T) {
|
||||
// Test Plex with standard headers enabled
|
||||
hPlex := ReverseProxyHandler("app:32400", false, "plex", true)
|
||||
hdrs := hPlex["headers"].(map[string]interface{})
|
||||
set := hdrs["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrs := hPlex["headers"].(map[string]any)
|
||||
set := hdrs["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
// Verify Plex-specific headers
|
||||
require.Contains(t, set, "X-Plex-Client-Identifier")
|
||||
@@ -156,8 +156,8 @@ func TestReverseProxyHandler_ApplicationSpecificHeaders(t *testing.T) {
|
||||
|
||||
// Test Jellyfin with standard headers enabled
|
||||
hJellyfin := ReverseProxyHandler("app:8096", false, "jellyfin", true)
|
||||
hdrsJ := hJellyfin["headers"].(map[string]interface{})
|
||||
setJ := hdrsJ["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrsJ := hJellyfin["headers"].(map[string]any)
|
||||
setJ := hdrsJ["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
// Verify standard headers present for Jellyfin
|
||||
require.Contains(t, setJ, "X-Real-IP")
|
||||
@@ -175,8 +175,8 @@ func TestReverseProxyHandler_WebSocketWithApplication(t *testing.T) {
|
||||
h := ReverseProxyHandler("app:8096", true, "jellyfin", true)
|
||||
require.Equal(t, "reverse_proxy", h["handler"])
|
||||
|
||||
hdrs := h["headers"].(map[string]interface{})
|
||||
set := hdrs["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrs := h["headers"].(map[string]any)
|
||||
set := hdrs["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
// Verify all 6 headers present (4 standard + 2 WebSocket)
|
||||
require.Contains(t, set, "X-Real-IP")
|
||||
@@ -210,8 +210,8 @@ func TestReverseProxyHandler_FeatureFlagDisabled(t *testing.T) {
|
||||
|
||||
// Test: Standard headers disabled with Plex (backward compatibility)
|
||||
hPlex := ReverseProxyHandler("app:32400", false, "plex", false)
|
||||
hdrsPlex := hPlex["headers"].(map[string]interface{})
|
||||
setPlex := hdrsPlex["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrsPlex := hPlex["headers"].(map[string]any)
|
||||
setPlex := hdrsPlex["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
// Should still have X-Real-IP and X-Forwarded-Host from application logic
|
||||
require.Contains(t, setPlex, "X-Real-IP")
|
||||
@@ -225,23 +225,23 @@ func TestReverseProxyHandler_FeatureFlagDisabled(t *testing.T) {
|
||||
func TestReverseProxyHandler_XForwardedForNotDuplicated(t *testing.T) {
|
||||
// Test with standard headers enabled
|
||||
h := ReverseProxyHandler("app:8080", false, "none", true)
|
||||
hdrs := h["headers"].(map[string]interface{})
|
||||
set := hdrs["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrs := h["headers"].(map[string]any)
|
||||
set := hdrs["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
// Verify X-Forwarded-For is NOT in the setHeaders map
|
||||
require.NotContains(t, set, "X-Forwarded-For", "X-Forwarded-For must NOT be explicitly set (Caddy handles it natively)")
|
||||
|
||||
// Test with WebSocket enabled
|
||||
h2 := ReverseProxyHandler("app:8080", true, "none", true)
|
||||
hdrs2 := h2["headers"].(map[string]interface{})
|
||||
set2 := hdrs2["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrs2 := h2["headers"].(map[string]any)
|
||||
set2 := hdrs2["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
require.NotContains(t, set2, "X-Forwarded-For", "X-Forwarded-For must NOT be explicitly set even with WebSocket")
|
||||
|
||||
// Test with application
|
||||
h3 := ReverseProxyHandler("app:32400", false, "plex", true)
|
||||
hdrs3 := h3["headers"].(map[string]interface{})
|
||||
set3 := hdrs3["request"].(map[string]interface{})["set"].(map[string][]string)
|
||||
hdrs3 := h3["headers"].(map[string]any)
|
||||
set3 := hdrs3["request"].(map[string]any)["set"].(map[string][]string)
|
||||
|
||||
require.NotContains(t, set3, "X-Forwarded-For", "X-Forwarded-For must NOT be explicitly set even with Plex")
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func validateHandler(handler Handler) error {
|
||||
}
|
||||
|
||||
func validateReverseProxy(handler Handler) error {
|
||||
upstreams, ok := handler["upstreams"].([]map[string]interface{})
|
||||
upstreams, ok := handler["upstreams"].([]map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("reverse_proxy missing upstreams")
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestValidateListenAddr_InvalidPortNonNumeric(t *testing.T) {
|
||||
func TestValidate_MarshalError(t *testing.T) {
|
||||
// stub jsonMarshalValidate to cause Marshal error
|
||||
orig := jsonMarshalValidate
|
||||
jsonMarshalValidate = func(v interface{}) ([]byte, error) { return nil, fmt.Errorf("marshal error") }
|
||||
jsonMarshalValidate = func(v any) ([]byte, error) { return nil, fmt.Errorf("marshal error") }
|
||||
defer func() { jsonMarshalValidate = orig }()
|
||||
|
||||
cfg := &Config{Apps: Apps{HTTP: &HTTPApp{Servers: map[string]*Server{"srv": {Listen: []string{":80"}, Routes: []*Route{{Match: []Match{{Host: []string{"x.com"}}}, Handle: []Handler{{"handler": "file_server"}}}}}}}}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user