Files
golib/static/suspicious_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

281 lines
9.5 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 (
"net/http"
"github.com/nabbar/golib/static"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Suspicious Access Detection", func() {
Describe("Configuration", func() {
Context("when setting suspicious detection config", func() {
It("should store and retrieve configuration", func() {
handler := newTestStatic().(static.Static)
cfg := static.SuspiciousConfig{
Enabled: true,
LogSuccessfulAccess: true,
SuspiciousPatterns: []string{".env", "admin"},
SuspiciousExtensions: []string{".php", ".exe"},
}
handler.SetSuspicious(cfg)
retrieved := handler.GetSuspicious()
Expect(retrieved.Enabled).To(BeTrue())
Expect(retrieved.LogSuccessfulAccess).To(BeTrue())
Expect(retrieved.SuspiciousPatterns).To(ContainElement(".env"))
Expect(retrieved.SuspiciousExtensions).To(ContainElement(".php"))
})
It("should use default config", func() {
cfg := static.DefaultSuspiciousConfig()
Expect(cfg.Enabled).To(BeTrue())
Expect(cfg.LogSuccessfulAccess).To(BeTrue())
Expect(cfg.SuspiciousPatterns).To(ContainElement(".env"))
Expect(cfg.SuspiciousPatterns).To(ContainElement(".git"))
Expect(cfg.SuspiciousExtensions).To(ContainElement(".php"))
})
})
})
Describe("Suspicious Pattern Detection", func() {
Context("when accessing suspicious files", func() {
It("should log access to .env files (blocked)", func() {
handler := newTestStatic().(static.Static)
// Enable all security features
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.DefaultPathSecurityConfig())
engine := setupTestRouter(handler, "/static")
// This should be logged as suspicious AND blocked
w := performRequest(engine, "GET", "/static/.env")
Expect(w.Code).To(Equal(http.StatusForbidden))
})
It("should log access to config.php files (404 but logged)", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// This should be logged as suspicious (404)
w := performRequest(engine, "GET", "/static/config.php")
Expect(w.Code).To(Equal(http.StatusNotFound))
})
It("should log access to admin panels", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Admin panel access attempts
_ = performRequest(engine, "GET", "/static/admin/login.php")
_ = performRequest(engine, "GET", "/static/wp-admin/")
_ = performRequest(engine, "GET", "/static/phpmyadmin/")
})
It("should log backup file access attempts", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Backup files
_ = performRequest(engine, "GET", "/static/config.bak")
_ = performRequest(engine, "GET", "/static/backup.old")
_ = performRequest(engine, "GET", "/static/index.php.save")
})
})
})
Describe("Attack Pattern Detection", func() {
Context("when detecting scanning patterns", func() {
It("should detect directory traversal scanning", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.DefaultPathSecurityConfig())
engine := setupTestRouter(handler, "/static")
// Directory traversal - should log pattern
w := performRequest(engine, "GET", "/static/../../../etc/passwd")
Expect(w.Code).To(Equal(http.StatusForbidden))
})
It("should detect path manipulation attempts", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Double slashes
_ = performRequest(engine, "GET", "/static//test.txt")
_ = performRequest(engine, "GET", "/static\\\\test.txt")
})
It("should detect config file scanning", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Config scanning
_ = performRequest(engine, "GET", "/static/config.php")
_ = performRequest(engine, "GET", "/static/configuration.inc")
})
})
})
Describe("Successful Suspicious Access", func() {
Context("when suspicious file exists and is served", func() {
It("should log even successful requests when enabled", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.SuspiciousConfig{
Enabled: true,
LogSuccessfulAccess: true, // Log even successful access
SuspiciousExtensions: []string{".txt"}, // Make .txt suspicious for this test
})
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// This file exists and will return 200, but should still be logged
w := performRequest(engine, "GET", "/static/test.txt")
Expect(w.Code).To(Equal(http.StatusOK))
})
It("should not log successful requests when disabled", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.SuspiciousConfig{
Enabled: true,
LogSuccessfulAccess: false, // Don't log successful access
SuspiciousExtensions: []string{".txt"},
})
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// This won't be logged since LogSuccessfulAccess is false
w := performRequest(engine, "GET", "/static/test.txt")
Expect(w.Code).To(Equal(http.StatusOK))
})
})
})
Describe("Integration with SecurityBackend Features", func() {
Context("when combining with path security", func() {
It("should log and block dangerous combinations", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.DefaultPathSecurityConfig())
handler.SetRateLimit(static.RateLimitConfig{
Enabled: true,
MaxRequests: 100,
Window: 60000000000,
})
engine := setupTestRouter(handler, "/static")
// Combination attack: path traversal + sensitive file
w := performRequest(engine, "GET", "/static/../.env")
Expect(w.Code).To(Equal(http.StatusForbidden))
})
})
})
Describe("Real-World Attack Scenarios", func() {
Context("when simulating real attacks", func() {
It("should detect WordPress scanning", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Typical WordPress scanner
_ = performRequest(engine, "GET", "/static/wp-admin/")
_ = performRequest(engine, "GET", "/static/wp-login.php")
_ = performRequest(engine, "GET", "/static/wp-config.php")
_ = performRequest(engine, "GET", "/static/xmlrpc.php")
})
It("should detect database file access attempts", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Database files
_ = performRequest(engine, "GET", "/static/database.sql")
_ = performRequest(engine, "GET", "/static/backup.db")
_ = performRequest(engine, "GET", "/static/data.sqlite")
})
It("should detect source code exposure attempts", func() {
handler := newTestStatic().(static.Static)
handler.SetSuspicious(static.DefaultSuspiciousConfig())
handler.SetPathSecurity(static.PathSecurityConfig{Enabled: false})
engine := setupTestRouter(handler, "/static")
// Source code
_ = performRequest(engine, "GET", "/static/source.tar.gz")
_ = performRequest(engine, "GET", "/static/backup.zip")
})
})
})
})