Compare commits

...

15 Commits

Author SHA1 Message Date
Jeremy
b1778ecb3d Merge branch 'development' into main 2025-12-17 09:32:46 -05:00
Jeremy
230f9bba70 Merge pull request #417 from Wikid82/renovate/npm-minorpatch
chore(deps): update dependency knip to ^5.75.1
2025-12-17 09:32:29 -05:00
Jeremy
40156be788 Merge branch 'development' into renovate/npm-minorpatch 2025-12-17 09:32:16 -05:00
Jeremy
647f9c2cf7 Merge pull request #416 from Wikid82/renovate/github-codeql-action-4.x
chore(deps): update github/codeql-action action to v4.31.9
2025-12-17 09:31:57 -05:00
Jeremy
3a3dccbb5a Merge branch 'development' into renovate/github-codeql-action-4.x 2025-12-17 09:31:09 -05:00
Jeremy
e3b596176c Merge pull request #415 from Wikid82/renovate/github-codeql-action-digest
chore(deps): update github/codeql-action digest to 5d4e8d1
2025-12-17 09:30:52 -05:00
renovate[bot]
8005858593 chore(deps): update dependency knip to ^5.75.1 2025-12-17 14:26:03 +00:00
renovate[bot]
793315336a chore(deps): update github/codeql-action action to v4.31.9 2025-12-17 14:25:51 +00:00
renovate[bot]
711ed07df7 chore(deps): update github/codeql-action digest to 5d4e8d1 2025-12-17 14:25:45 +00:00
Jeremy
7e31a9c41a Merge pull request #413 from Wikid82:copilot/sub-pr-411
fix: secure WebSocket authentication using HttpOnly cookies instead of query parameters
2025-12-17 09:22:30 -05:00
Jeremy
c0fee50fa9 Merge branch 'main' into copilot/sub-pr-411 2025-12-17 07:59:09 -05:00
copilot-swe-agent[bot]
6718431bc4 fix: improve test error handling with proper error checks
Co-authored-by: Wikid82 <176516789+Wikid82@users.noreply.github.com>
2025-12-17 12:58:02 +00:00
copilot-swe-agent[bot]
36a8b408b8 test: add comprehensive tests for secure WebSocket authentication priority
Co-authored-by: Wikid82 <176516789+Wikid82@users.noreply.github.com>
2025-12-17 12:56:46 +00:00
copilot-swe-agent[bot]
e1474e42aa feat: switch WebSocket auth from query params to HttpOnly cookies for security
Co-authored-by: Wikid82 <176516789+Wikid82@users.noreply.github.com>
2025-12-17 12:54:35 +00:00
copilot-swe-agent[bot]
a01bcb8d4a Initial plan 2025-12-17 12:46:47 +00:00
11 changed files with 263 additions and 58 deletions

View File

@@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Initialize CodeQL
uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
with:
languages: ${{ matrix.language }}
@@ -45,9 +45,9 @@ jobs:
go-version: '1.25.5'
- name: Autobuild
uses: github/codeql-action/autobuild@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
with:
category: "/language:${{ matrix.language }}"

View File

@@ -152,7 +152,7 @@ jobs:
- name: Upload Trivy results
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true' && steps.trivy-check.outputs.exists == 'true'
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
with:
sarif_file: 'trivy-results.sarif'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -157,7 +157,7 @@ jobs:
- name: Upload Trivy results
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true' && steps.trivy-check.outputs.exists == 'true'
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
with:
sarif_file: 'trivy-results.sarif'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -97,7 +97,7 @@ jobs:
severity: 'CRITICAL,HIGH,MEDIUM'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
with:
sarif_file: 'trivy-weekly-results.sarif'

View File

@@ -13,14 +13,17 @@ func AuthMiddleware(authService *services.AuthService) gin.HandlerFunc {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
// Try cookie first for browser flows
// Try cookie first for browser flows (including WebSocket upgrades)
if cookie, err := c.Cookie("auth_token"); err == nil && cookie != "" {
authHeader = "Bearer " + cookie
}
}
// DEPRECATED: Query parameter authentication for WebSocket connections
// This fallback exists only for backward compatibility and will be removed in a future version.
// Query parameters are logged in access logs and should not be used for sensitive data.
// Use HttpOnly cookies instead, which are automatically sent by browsers and not logged.
if authHeader == "" {
// Try query param (token passthrough)
if token := c.Query("token"); token != "" {
authHeader = "Bearer " + token
}

View File

@@ -184,3 +184,58 @@ func TestRequireRole_MissingRoleInContext(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, w.Code)
}
func TestAuthMiddleware_QueryParamFallback(t *testing.T) {
authService := setupAuthService(t)
user, err := authService.Register("test@example.com", "password", "Test User")
require.NoError(t, err)
token, err := authService.GenerateToken(user)
require.NoError(t, err)
gin.SetMode(gin.TestMode)
r := gin.New()
r.Use(AuthMiddleware(authService))
r.GET("/test", func(c *gin.Context) {
userID, _ := c.Get("userID")
assert.Equal(t, user.ID, userID)
c.Status(http.StatusOK)
})
// Test that query param auth still works (deprecated fallback)
req, err := http.NewRequest("GET", "/test?token="+token, http.NoBody)
require.NoError(t, err)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
func TestAuthMiddleware_PrefersCookieOverQueryParam(t *testing.T) {
authService := setupAuthService(t)
// Create two different users
cookieUser, _ := authService.Register("cookie@example.com", "password", "Cookie User")
cookieToken, _ := authService.GenerateToken(cookieUser)
queryUser, _ := authService.Register("query@example.com", "password", "Query User")
queryToken, _ := authService.GenerateToken(queryUser)
gin.SetMode(gin.TestMode)
r := gin.New()
r.Use(AuthMiddleware(authService))
r.GET("/test", func(c *gin.Context) {
userID, _ := c.Get("userID")
// Should use the cookie user, not the query param user
assert.Equal(t, cookieUser.ID, userID)
c.Status(http.StatusOK)
})
// Both cookie and query param provided - cookie should win
req, err := http.NewRequest("GET", "/test?token="+queryToken, http.NoBody)
require.NoError(t, err)
req.AddCookie(&http.Cookie{Name: "auth_token", Value: cookieToken})
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}

View File

@@ -25,42 +25,50 @@
```text
Frontend Backend
──────── ───────
localStorage.getItem('charon_auth_token')
Query param: ?token=<jwt> ────────► AuthMiddleware:
1. Check Authorization header
2. Check auth_token cookie
3. Check token query param ◄── MATCHES
ValidateToken(jwt) → OK
Upgrade to WebSocket
User logs in
Backend sets HttpOnly auth_token cookie ──► AuthMiddleware:
1. Check Authorization header
2. Check auth_token cookie ◄── SECURE METHOD
3. (Deprecated) Check token query param
WebSocket connection initiated
(Cookie sent automatically by browser) ValidateToken(jwt) → OK
└──────────────────────────────────► Upgrade to WebSocket
```
**Security Note:** Authentication now uses HttpOnly cookies instead of query parameters.
This prevents JWT tokens from being logged in access logs, proxies, and other telemetry.
The browser automatically sends the cookie with WebSocket upgrade requests.
### Logic Gap Analysis
**ANSWER: NO - There is NO logic gap between Frontend and Backend.**
| Question | Answer |
|----------|--------|
| Frontend auth method | Query param `?token=<jwt>` from `localStorage.getItem('charon_auth_token')` |
| Backend auth method | Accepts: Header → Cookie → Query param `token` ✅ |
| Frontend auth method | HttpOnly cookie (`auth_token`) sent automatically by browser ✅ SECURE |
| Backend auth method | Accepts: Header → Cookie (preferred) → Query param (deprecated) ✅ |
| Filter params | Both use `source`, `level`, `ip`, `host`, `blocked_only` ✅ |
| Data format | `SecurityLogEntry` struct matches frontend TypeScript type ✅ |
| Security | Tokens no longer logged in access logs or exposed to XSS ✅ |
---
## 1. VERIFICATION STATUS
### ✅ localStorage Key IS Correct
### ✅ Authentication Method Updated for Security
Both WebSocket functions in `frontend/src/api/logs.ts` correctly use `charon_auth_token`:
WebSocket authentication now uses HttpOnly cookies instead of query parameters:
- **Line 119-122** (`connectLiveLogs`): `localStorage.getItem('charon_auth_token')`
- **Line 178-181** (`connectSecurityLogs`): `localStorage.getItem('charon_auth_token')`
- **`connectLiveLogs`** (frontend/src/api/logs.ts): Uses browser's automatic cookie transmission
- **`connectSecurityLogs`** (frontend/src/api/logs.ts): Uses browser's automatic cookie transmission
- **Backend middleware**: Prioritizes cookie-based auth, query param is deprecated
This change prevents JWT tokens from appearing in access logs, proxy logs, and other telemetry.
---
@@ -186,12 +194,13 @@ The `showBlockedOnly` state in useEffect dependencies causes reconnection when t
| Component | Status | Notes |
|-----------|--------|-------|
| localStorage key | ✅ Fixed | Now uses `charon_auth_token` |
| Auth middleware | ✅ Working | Accepts query param `token` |
| WebSocket authentication | ✅ Secured | Now uses HttpOnly cookies instead of query parameters |
| Auth middleware | ✅ Updated | Cookie-based auth prioritized, query param deprecated |
| WebSocket endpoint | ✅ Working | Protected route, upgrades correctly |
| LogWatcher service | ✅ Working | Tails access.log successfully |
| **Frontend memoization** | ✅ Fixed | `useMemo` in Security.tsx |
| **Stable default props** | ✅ Fixed | Constants in LiveLogViewer.tsx |
| **Security improvement** | ✅ Complete | Tokens no longer exposed in logs |
---
@@ -221,7 +230,9 @@ docker logs charon 2>&1 | grep -i "cerberus.*websocket" | tail -10
**Logic Gap Between Frontend/Backend:** **NO** - Both are correctly aligned
**Current Status:** ✅ All fixes applied and working
**Security Enhancement:** WebSocket authentication now uses HttpOnly cookies instead of query parameters, preventing token leakage in logs
**Current Status:** ✅ All fixes applied and working securely
---

View File

@@ -0,0 +1,131 @@
# WebSocket Authentication Security
## Overview
This document explains the security improvements made to WebSocket authentication in Charon to prevent JWT tokens from being exposed in access logs.
## Security Issue
### Before (Insecure)
Previously, WebSocket connections authenticated by passing the JWT token as a query parameter:
```
wss://example.com/api/v1/logs/live?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Security Risk:**
- Query parameters are logged in web server access logs (Caddy, nginx, Apache, etc.)
- Tokens appear in proxy logs
- Tokens may be stored in browser history
- Tokens can be captured in monitoring and telemetry systems
- An attacker with access to these logs can replay the token to impersonate a user
### After (Secure)
WebSocket connections now authenticate using HttpOnly cookies:
```
wss://example.com/api/v1/logs/live?source=waf&level=error
```
The browser automatically sends the `auth_token` cookie with the WebSocket upgrade request.
**Security Benefits:**
- ✅ HttpOnly cookies are **not logged** by web servers
- ✅ HttpOnly cookies **cannot be accessed** by JavaScript (XSS protection)
- ✅ Cookies are **not visible** in browser history
- ✅ Cookies are **not captured** in URL-based monitoring
- ✅ Token replay attacks are mitigated (tokens still have expiration)
## Implementation Details
### Frontend Changes
**Location:** `frontend/src/api/logs.ts`
Removed:
```typescript
const token = localStorage.getItem('charon_auth_token');
if (token) {
params.append('token', token);
}
```
The browser automatically sends the `auth_token` cookie when establishing WebSocket connections due to:
1. The cookie is set by the backend during login with `HttpOnly`, `Secure`, and `SameSite` flags
2. The axios client has `withCredentials: true`, enabling cookie transmission
### Backend Changes
**Location:** `backend/internal/api/middleware/auth.go`
Authentication priority order:
1. **Authorization header** (Bearer token) - for API clients
2. **auth_token cookie** (HttpOnly) - **preferred for browsers and WebSockets**
3. **token query parameter** - **deprecated**, kept for backward compatibility only
The query parameter fallback is marked as deprecated and will be removed in a future version.
### Cookie Configuration
**Location:** `backend/internal/api/handlers/auth_handler.go`
The `auth_token` cookie is set with security best practices:
- **HttpOnly**: `true` - prevents JavaScript access (XSS protection)
- **Secure**: `true` (in production with HTTPS) - prevents transmission over HTTP
- **SameSite**: `Strict` (HTTPS) or `Lax` (HTTP/IP) - CSRF protection
- **Path**: `/` - available for all routes
- **MaxAge**: 24 hours - automatic expiration
## Verification
### Test Coverage
**Location:** `backend/internal/api/middleware/auth_test.go`
- `TestAuthMiddleware_Cookie` - verifies cookie authentication works
- `TestAuthMiddleware_QueryParamFallback` - verifies deprecated query param still works
- `TestAuthMiddleware_PrefersCookieOverQueryParam` - verifies cookie is prioritized over query param
- `TestAuthMiddleware_PrefersAuthorizationHeader` - verifies header takes highest priority
### Log Verification
To verify tokens are not logged:
1. **Before the fix:** Check Caddy access logs for token exposure:
```bash
docker logs charon 2>&1 | grep "token=" | grep -o "token=[^&]*"
```
Would show: `token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`
2. **After the fix:** Check that WebSocket URLs are clean:
```bash
docker logs charon 2>&1 | grep "/logs/live\|/cerberus/logs/ws"
```
Shows: `/api/v1/logs/live?source=waf&level=error` (no token)
## Migration Path
### For Users
No action required. The change is transparent:
- Login sets the HttpOnly cookie
- WebSocket connections automatically use the cookie
- Existing sessions continue to work
### For API Clients
API clients using Authorization headers are unaffected.
### Deprecation Timeline
1. **Current:** Query parameter authentication is deprecated but still functional
2. **Future (v2.0):** Query parameter authentication will be removed entirely
3. **Recommendation:** Any custom scripts or tools should migrate to using Authorization headers or cookie-based authentication
## Related Documentation
- [Authentication Flow](../plans/prev_spec_websocket_fix_dec16.md#authentication-flow)
- [Security Best Practices](https://owasp.org/www-community/HttpOnly)
- [WebSocket Security](https://datatracker.ietf.org/doc/html/rfc6455#section-10)

View File

@@ -47,7 +47,7 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"jsdom": "^27.3.0",
"knip": "^5.74.0",
"knip": "^5.75.1",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3",
@@ -163,6 +163,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -522,6 +523,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -568,6 +570,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3259,8 +3262,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -3348,6 +3350,7 @@
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -3358,6 +3361,7 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -3397,6 +3401,7 @@
"integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.50.0",
"@typescript-eslint/types": "8.50.0",
@@ -3777,6 +3782,7 @@
"integrity": "sha512-rkoPH+RqWopVxDnCBE/ysIdfQ2A7j1eDmW8tCxxrR9nnFBa9jKf86VgsSAzxBd1x+ny0GC4JgiD3SNfRHv3pOg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/utils": "4.0.16",
"fflate": "^0.8.2",
@@ -3812,6 +3818,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4042,6 +4049,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -4244,7 +4252,8 @@
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"peer": true
},
"node_modules/data-urls": {
"version": "6.0.0",
@@ -4333,8 +4342,7 @@
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/dunder-proto": {
"version": "1.0.1",
@@ -4498,6 +4506,7 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -5390,6 +5399,7 @@
"integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@acemir/cssom": "^0.9.28",
"@asamuzakjp/dom-selector": "^6.7.6",
@@ -5476,9 +5486,9 @@
}
},
"node_modules/knip": {
"version": "5.74.0",
"resolved": "https://registry.npmjs.org/knip/-/knip-5.74.0.tgz",
"integrity": "sha512-xSG+vn403ONBkQtSBf1+kcE8ulzyQHLWIDQAxvu3W7HnM0jZJqVUPlK5w6FZNUyKnp+4FInsYQW77eapDpmcNA==",
"version": "5.75.1",
"resolved": "https://registry.npmjs.org/knip/-/knip-5.75.1.tgz",
"integrity": "sha512-raguBFxTUO5JKrv8rtC8wrOtzrDwWp/fOu1F1GhrHD1F3TD2fqI1Z74JB+PyFZubL+RxqOkhGStdPAvaaXSOWQ==",
"dev": true,
"funding": [
{
@@ -5836,7 +5846,6 @@
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -6250,6 +6259,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -6279,7 +6289,6 @@
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -6294,7 +6303,6 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
}
@@ -6304,7 +6312,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
},
@@ -6352,6 +6359,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6361,6 +6369,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -6405,8 +6414,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/react-refresh": {
"version": "0.18.0",
@@ -6961,6 +6969,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -6998,8 +7007,7 @@
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.2.2",
@@ -7090,6 +7098,7 @@
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -7165,6 +7174,7 @@
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.0.16",
"@vitest/mocker": "4.0.16",
@@ -7402,6 +7412,7 @@
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@@ -66,7 +66,7 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"jsdom": "^27.3.0",
"knip": "^5.74.0",
"knip": "^5.75.1",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3",

View File

@@ -128,11 +128,8 @@ export const connectLiveLogs = (
if (filters.level) params.append('level', filters.level);
if (filters.source) params.append('source', filters.source);
// Get auth token from localStorage (key: charon_auth_token)
const token = localStorage.getItem('charon_auth_token');
if (token) {
params.append('token', token);
}
// Authentication is handled via HttpOnly cookies sent automatically by the browser
// This prevents tokens from being logged in access logs or exposed to XSS attacks
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/api/v1/logs/live?${params.toString()}`;
@@ -196,11 +193,8 @@ export const connectSecurityLogs = (
if (filters.host) params.append('host', filters.host);
if (filters.blocked_only) params.append('blocked_only', 'true');
// Get auth token from localStorage (key: charon_auth_token)
const token = localStorage.getItem('charon_auth_token');
if (token) {
params.append('token', token);
}
// Authentication is handled via HttpOnly cookies sent automatically by the browser
// This prevents tokens from being logged in access logs or exposed to XSS attacks
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/api/v1/cerberus/logs/ws?${params.toString()}`;