Files
golib/static/example_test.go
nabbar 43241f78ba [ file/progress ]
- ADD flag to register temp file creation
    - ADD function to check flag is temp

[ static ]
    - FIX bugs & race detection
    - UPDATE code: refactor & optimize code, improve security &
      preformances
    - ADD Path Security: add options & code to improve security
    - ADD Rate Limiting: add option to limit capabilities of burst request
    - ADD HTTP Security Headers: add option to customize header, improve
      security & allow cache crontol
    - ADD Suspicious Access Detection: add option to identify & log
      suspicious request
    - ADD Security Backend Integration: add option to plug WAF/IDF/EDR
      backend (with CEF Format or not)
    - ADD documentation: add enhanced README and TESTING guidelines
    - ADD tests: complete test suites with benchmarks, concurrency, and edge cases
2025-11-23 19:30:27 +01:00

419 lines
12 KiB
Go

/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package static_test
import (
"context"
"embed"
"fmt"
"time"
"github.com/nabbar/golib/static"
)
//go:embed testdata
var exampleContent embed.FS
// Example_basic shows the simplest usage of the static package.
// This example serves files from an embedded filesystem with no security features.
func Example_basic() {
// Create a static file handler
_ = static.New(context.Background(), exampleContent, "testdata")
// The handler can now be registered with Gin router
// router := gin.Default()
// handler.RegisterRouter("/static", router.GET)
// router.Run(":8080")
// Files will be accessible at http://localhost:8080/static/*
fmt.Println("Basic static file handler created")
// Output: Basic static file handler created
}
// Example_pathSecurity demonstrates path traversal protection.
func Example_pathSecurity() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Enable path security with default settings
handler.SetPathSecurity(static.DefaultPathSecurityConfig())
// Or customize the configuration
handler.SetPathSecurity(static.PathSecurityConfig{
Enabled: true,
AllowDotFiles: false, // Block .env, .git, etc.
MaxPathDepth: 10,
BlockedPatterns: []string{
".git",
".svn",
"node_modules",
},
})
// Check if a path is safe
safe := handler.IsPathSafe("/static/test.txt")
fmt.Printf("Path is safe: %v\n", safe)
// Output: Path is safe: true
}
// Example_rateLimit demonstrates IP-based rate limiting.
func Example_rateLimit() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Configure rate limiting
handler.SetRateLimit(static.RateLimitConfig{
Enabled: true,
MaxRequests: 100, // Max 100 unique files
Window: time.Minute, // Per minute
CleanupInterval: 5 * time.Minute, // Cleanup every 5 minutes
WhitelistIPs: []string{
"127.0.0.1", // Localhost
"::1", // IPv6 localhost
},
})
// Check if an IP is rate limited
limited := handler.IsRateLimited("192.168.1.100")
fmt.Printf("IP is rate limited: %v\n", limited)
// Output: IP is rate limited: false
}
// Example_httpCaching demonstrates HTTP caching with ETag and Cache-Control.
func Example_httpCaching() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Configure HTTP headers with default settings
handler.SetHeaders(static.DefaultHeadersConfig())
// Or customize
handler.SetHeaders(static.HeadersConfig{
EnableCacheControl: true,
CacheMaxAge: 3600, // 1 hour
CachePublic: true, // Allow CDN caching
EnableETag: true, // Enable ETag validation
EnableContentType: true,
CustomMimeTypes: map[string]string{
".wasm": "application/wasm",
},
})
fmt.Println("HTTP caching configured")
// Output: HTTP caching configured
}
// Example_mimeTypeValidation demonstrates MIME type filtering.
func Example_mimeTypeValidation() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Block dangerous file types
handler.SetHeaders(static.HeadersConfig{
EnableContentType: true,
DenyMimeTypes: []string{
"application/x-executable",
"application/x-msdownload",
"application/x-sh",
},
})
// Or whitelist only specific types
handler.SetHeaders(static.HeadersConfig{
EnableContentType: true,
AllowedMimeTypes: []string{
"text/html",
"text/css",
"application/javascript",
"image/png",
"image/jpeg",
},
})
fmt.Println("MIME type validation configured")
// Output: MIME type validation configured
}
// Example_suspiciousDetection demonstrates suspicious access pattern detection.
func Example_suspiciousDetection() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Enable suspicious access detection
handler.SetSuspicious(static.DefaultSuspiciousConfig())
// Or customize
handler.SetSuspicious(static.SuspiciousConfig{
Enabled: true,
LogSuccessfulAccess: true, // Log even successful suspicious requests
SuspiciousPatterns: []string{
".env",
".git",
"wp-admin",
"phpmyadmin",
},
SuspiciousExtensions: []string{
".php",
".exe",
},
})
fmt.Println("Suspicious access detection enabled")
// Output: Suspicious access detection enabled
}
// Example_securityBackend demonstrates WAF/IDS/EDR integration.
func Example_securityBackend() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Configure security backend with webhook
handler.SetSecurityBackend(static.SecurityConfig{
Enabled: true,
WebhookURL: "https://waf.example.com/events",
WebhookHeaders: map[string]string{
"Authorization": "Bearer secret-token",
},
WebhookTimeout: 5 * time.Second,
WebhookAsync: true, // Non-blocking
MinSeverity: "medium", // Only medium, high, critical
})
fmt.Println("Security backend configured")
// Output: Security backend configured
}
// Example_securityBackendBatch demonstrates batch event processing.
func Example_securityBackendBatch() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Configure batch processing for efficiency
handler.SetSecurityBackend(static.SecurityConfig{
Enabled: true,
WebhookURL: "https://siem.example.com/batch",
BatchSize: 100, // Send every 100 events
BatchTimeout: 30 * time.Second, // Or every 30 seconds
MinSeverity: "low", // All severity levels
})
fmt.Println("Batch security backend configured")
// Output: Batch security backend configured
}
// Example_securityBackendCEF demonstrates CEF format for SIEM systems.
func Example_securityBackendCEF() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Configure CEF format for SIEM compatibility
handler.SetSecurityBackend(static.SecurityConfig{
Enabled: true,
WebhookURL: "https://siem.example.com/cef",
EnableCEFFormat: true, // Common Event Format
MinSeverity: "high",
})
fmt.Println("CEF format configured")
// Output: CEF format configured
}
// Example_indexFiles demonstrates index file configuration.
func Example_indexFiles() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Set index file for root
handler.SetIndex("", "/", "index.html")
// Set index for specific routes
handler.SetIndex("", "/docs", "docs/index.html")
fmt.Println("Index files configured")
// Output: Index files configured
}
// Example_downloadFiles demonstrates download configuration.
func Example_downloadFiles() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Mark files to be downloaded instead of displayed
handler.SetDownload("/static/document.pdf", true)
handler.SetDownload("/static/archive.zip", true)
// Check if a file should be downloaded
shouldDownload := handler.IsDownload("/static/document.pdf")
fmt.Printf("Should download: %v\n", shouldDownload)
// Output: Should download: false
}
// Example_redirects demonstrates URL redirection.
func Example_redirects() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Configure redirects (HTTP 301)
handler.SetRedirect("", "/old-path", "", "/new-path")
handler.SetRedirect("", "/legacy", "", "/modern")
fmt.Println("Redirects configured")
// Output: Redirects configured
}
// Example_production demonstrates a complete production-ready configuration.
func Example_production() {
handler := static.New(context.Background(), exampleContent, "testdata")
// 1. Path Security
handler.SetPathSecurity(static.PathSecurityConfig{
Enabled: true,
AllowDotFiles: false,
MaxPathDepth: 10,
BlockedPatterns: []string{
".git", ".svn", ".env",
"node_modules", "vendor",
},
})
// 2. Rate Limiting
handler.SetRateLimit(static.RateLimitConfig{
Enabled: true,
MaxRequests: 1000,
Window: time.Minute,
CleanupInterval: 5 * time.Minute,
WhitelistIPs: []string{"127.0.0.1"},
})
// 3. HTTP Caching
handler.SetHeaders(static.HeadersConfig{
EnableCacheControl: true,
CacheMaxAge: 3600,
CachePublic: true,
EnableETag: true,
EnableContentType: true,
DenyMimeTypes: []string{
"application/x-executable",
},
})
// 4. Suspicious Access Detection
handler.SetSuspicious(static.SuspiciousConfig{
Enabled: true,
LogSuccessfulAccess: true,
SuspiciousPatterns: []string{".env", ".git"},
})
// 5. Security Backend
handler.SetSecurityBackend(static.SecurityConfig{
Enabled: true,
WebhookURL: "https://waf.example.com/events",
WebhookAsync: true,
MinSeverity: "medium",
BatchSize: 100,
BatchTimeout: 30 * time.Second,
})
// 6. Index Files
handler.SetIndex("", "/", "index.html")
fmt.Println("Production configuration complete")
// Output: Production configuration complete
}
// Example_development demonstrates a minimal development configuration.
func Example_development() {
// Minimal setup for local development
handler := static.New(context.Background(), exampleContent, "testdata")
// Only enable basic security
handler.SetPathSecurity(static.PathSecurityConfig{
Enabled: true,
AllowDotFiles: false,
})
fmt.Println("Development configuration complete")
// Output: Development configuration complete
}
// Example_cdn demonstrates configuration optimized for CDN usage.
func Example_cdn() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Aggressive caching for CDN
handler.SetHeaders(static.HeadersConfig{
EnableCacheControl: true,
CacheMaxAge: 31536000, // 1 year
CachePublic: true, // Allow CDN caching
EnableETag: true,
})
// Relaxed rate limiting (CDN handles most requests)
handler.SetRateLimit(static.RateLimitConfig{
Enabled: true,
MaxRequests: 10000,
Window: time.Minute,
})
fmt.Println("CDN configuration complete")
// Output: CDN configuration complete
}
// Example_apiAssets demonstrates serving assets for an API.
func Example_apiAssets() {
handler := static.New(context.Background(), exampleContent, "testdata")
// Strict security
handler.SetPathSecurity(static.PathSecurityConfig{
Enabled: true,
AllowDotFiles: false,
MaxPathDepth: 5,
})
// Conservative rate limiting
handler.SetRateLimit(static.RateLimitConfig{
Enabled: true,
MaxRequests: 100,
Window: time.Minute,
})
// Long cache duration
handler.SetHeaders(static.HeadersConfig{
EnableCacheControl: true,
CacheMaxAge: 86400, // 24 hours
EnableETag: true,
})
fmt.Println("API assets configuration complete")
// Output: API assets configuration complete
}
// Example_fileOperations demonstrates file operations on embedded filesystem.
func Example_fileOperations() {
_ = static.New(context.Background(), exampleContent, "testdata")
// Check if file exists
// exists := handler.Has("test.txt")
// List all files
// files, _ := handler.List("testdata")
// Get file info
// info, _ := handler.Info("test.txt")
fmt.Println("File operations available")
// Output: File operations available
}