Merge pull request #300 from Surya-0823/feat/xxe-detection-rule

Fix XXE detection consistency and harden development infrastructure
This commit is contained in:
Carter Perez 2026-07-06 10:07:33 -04:00 committed by GitHub
commit 757f9d6782
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 146 additions and 122 deletions

View File

@ -5,11 +5,11 @@ rules.py
Cold-start rule-based detection engine inspired by
ModSecurity Core Rule Set
RuleEngine.score_request evaluates requests against 9
RuleEngine.score_request evaluates requests against 10
regex-based _PatternRules (LOG4SHELL 0.95, COMMAND_
INJECTION 0.90, SQL_INJECTION 0.85, XSS 0.80, FILE_
INCLUSION 0.75, SSRF 0.70, CRLF_INJECTION 0.65,
PATH_TRAVERSAL 0.60, OPEN_REDIRECT 0.55),
INJECTION 0.90, SQL_INJECTION 0.85, XXE_INJECTION 0.82,
XSS 0.80, FILE_INCLUSION 0.75, SSRF 0.70, CRLF_INJECTION
0.65, PATH_TRAVERSAL 0.60, OPEN_REDIRECT 0.55),
double-encoding detection (0.40), scanner user-agent
signature matching (0.35), and 2 _ThresholdRules
(RATE_ANOMALY >100 req/min 0.30, HIGH_ERROR_RATE >50%
@ -46,6 +46,7 @@ from app.core.features.patterns import (
SQLI,
SSRF,
XSS,
XXE_INJECTION,
)
from app.core.features.signatures import SCANNER_USER_AGENTS
from app.core.detection.ensemble import classify_severity as _classify_severity
@ -77,11 +78,12 @@ _PATTERN_RULES: list[_PatternRule] = [
_PatternRule("LOG4SHELL", LOG4SHELL, 0.95),
_PatternRule("COMMAND_INJECTION", COMMAND_INJECTION, 0.90),
_PatternRule("SQL_INJECTION", SQLI, 0.85),
_PatternRule("XXE_INJECTION", XXE_INJECTION, 0.82),
_PatternRule("XSS", XSS, 0.80),
_PatternRule("FILE_INCLUSION", FILE_INCLUSION, 0.75),
_PatternRule("SSRF", SSRF, 0.70),
_PatternRule("PATH_TRAVERSAL", PATH_TRAVERSAL, 0.60),
_PatternRule("CRLF_INJECTION", CRLF_INJECTION, 0.65),
_PatternRule("PATH_TRAVERSAL", PATH_TRAVERSAL, 0.60),
_PatternRule("OPEN_REDIRECT", OPEN_REDIRECT, 0.55),
]

View File

@ -3,7 +3,7 @@
patterns.py
Compiled regex patterns for web attack detection covering
9 OWASP categories plus encoding anomalies
10 OWASP categories plus encoding anomalies
Defines case-insensitive compiled patterns for: SQLI
(union select, sleep, benchmark, information_schema, hex
@ -14,13 +14,16 @@ document.cookie/write, eval/alert/prompt), PATH_TRAVERSAL
etc/passwd, .git/config, .env), COMMAND_INJECTION
(semicolon/pipe chaining to shell commands, $() and
backtick substitution, ${} expansion), FILE_INCLUSION
(php://, file://, data://, phar:// wrapper schemes), SSRF
(cloud metadata IPs 169.254.169.254, localhost with paths,
dict:// and gopher://), LOG4SHELL (${jndi, ${lower, ${:-
patterns), CRLF_INJECTION (%0d%0a sequences, bare CR/LF),
(php://, file://, data://, phar:// wrapper schemes),
XXE_INJECTION (<!ENTITY / <!DOCTYPE declarations, SYSTEM
"file:///" references, xmlns:xi includes, and their
URL-encoded variants), SSRF (cloud metadata IPs
169.254.169.254, localhost with paths, dict:// and
gopher://), LOG4SHELL (${jndi, ${lower, ${:- patterns),
CRLF_INJECTION (%0d%0a sequences, bare CR/LF),
OPEN_REDIRECT (redirect params pointing to external URLs).
Also provides ENCODED_CHARS, DOUBLE_ENCODED for evasion
detection, and ATTACK_COMBINED unioning all 9 patterns
detection, and ATTACK_COMBINED unioning all 10 patterns
Connects to:
core/features/
@ -103,6 +106,14 @@ _FILE_INCLUSION = (r"(?:php://|"
r"phar://|"
r"glob://)")
_XXE_INJECTION = (
r"(?:<!\s*(?:ENTITY|DOCTYPE)\b|"
r"SYSTEM\s+[\"']\s*(?:file|http|https|ftp|php|data)://|"
r"xmlns\s*:\s*xi\s*=|"
r"%(?:25)?3[Cc]!(?:ENTITY|DOCTYPE)|"
r"&(?:xxe|xml|ext|file);|"
r"%(?:25)?(?:53|73)%(?:25)?(?:59|79)%(?:25)?(?:53|73)%(?:25)?(?:54|74)%(?:25)?(?:45|65)%(?:25)?(?:4[Dd]))")
_SSRF = (
r"(?:169\.254\.169\.254|"
r"metadata\.google\.internal|"
@ -129,6 +140,7 @@ XSS = re.compile(_XSS, re.IGNORECASE)
PATH_TRAVERSAL = re.compile(_PATH_TRAVERSAL, re.IGNORECASE)
COMMAND_INJECTION = re.compile(_COMMAND_INJECTION, re.IGNORECASE)
FILE_INCLUSION = re.compile(_FILE_INCLUSION, re.IGNORECASE)
XXE_INJECTION = re.compile(_XXE_INJECTION, re.IGNORECASE)
SSRF = re.compile(_SSRF, re.IGNORECASE)
LOG4SHELL = re.compile(_LOG4SHELL, re.IGNORECASE)
CRLF_INJECTION = re.compile(_CRLF_INJECTION, re.IGNORECASE)
@ -137,7 +149,7 @@ OPEN_REDIRECT = re.compile(_OPEN_REDIRECT, re.IGNORECASE)
ATTACK_COMBINED = re.compile(
r"|".join((
_SQLI, _XSS, _PATH_TRAVERSAL, _COMMAND_INJECTION,
_FILE_INCLUSION, _SSRF, _LOG4SHELL,
_FILE_INCLUSION, _XXE_INJECTION, _SSRF, _LOG4SHELL,
_CRLF_INJECTION, _OPEN_REDIRECT,
)),
re.IGNORECASE,

View File

@ -26,7 +26,7 @@ services:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD required}
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
volumes:
- postgres_data:/var/lib/postgresql/data
- postgres_data:/var/lib/postgresql
networks:
- vigil_network
healthcheck:

View File

@ -6,16 +6,17 @@ simulate.py
HTTP traffic simulator for generating realistic attack and
normal log patterns against the dev-log target application
Provides 10 traffic modes via argparse: normal (benign
Provides 11 traffic modes via argparse: normal (benign
browsing with GET/POST mix), sqli (12 SQL injection
payloads), xss (10 script/event handler vectors),
traversal (10 dot-dot-slash and encoding variants), cmdi
(7 shell command injection payloads), log4shell (4 JNDI
lookup variants), ssrf (5 cloud metadata and internal
service targets), scanner (20 recon paths with 11 scanner
user-agents), flood (rapid-fire requests), and mixed
(50/10/40 normal/scanner/attack split). Uses urllib for
HTTP requests with configurable count, delay, and target
service targets), xxe (5 XML external entity payloads),
scanner (20 recon paths with 11 scanner user-agents),
flood (rapid-fire requests), and mixed (50/10/40
normal/scanner/attack split). Uses urllib for HTTP
requests with configurable count, delay, and target
URL. Checks /health reachability before starting
Connects to:
@ -29,6 +30,7 @@ import random
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
DEFAULT_TARGET = "http://localhost:58319"
@ -117,6 +119,14 @@ SSRF_PAYLOADS = [
"/api/search?url=http://metadata.google.internal/computeMetadata/v1/",
]
XXE_PAYLOADS = [
"/api/test?data=%3C!DOCTYPE+foo+%5B%3C!ENTITY+xxe+SYSTEM+%22file%3A%2F%2F%2Fetc%2Fpasswd%22%3E%5D%3E",
"/api/test?xml=<!DOCTYPE+root+[<!ENTITY+ext+SYSTEM+'file:///etc/hosts'>]>",
"/api/test?body=<root+xmlns:xi='http://www.w3.org/2001/XInclude'><xi:include+href='file:///etc/shadow'/></root>",
"/api/parse?input=%3C%21ENTITY+evil+SYSTEM+%22file%3A%2F%2F%2Fproc%2Fself%2Fenviron%22%3E",
"/api/test?content=<!DOCTYPE+x+[<!ENTITY+test+SYSTEM+'http://evil.com/xxe'>]>&q=&xxe;",
]
SCANNER_USER_AGENTS = [
"Nikto/2.1.6",
"sqlmap/1.7.2#stable (https://sqlmap.org)",
@ -149,11 +159,18 @@ ATTACK_POOLS = {
"cmdi": COMMAND_INJECTION_PAYLOADS,
"log4shell": LOG4SHELL_PAYLOADS,
"ssrf": SSRF_PAYLOADS,
"xxe": XXE_PAYLOADS,
}
def send(target, path, user_agent=None, method="GET"):
url = f"{target}{path}"
if "?" in path:
_raw_path, _raw_query = path.split("?", 1)
_encoded_path = urllib.parse.quote(_raw_path, safe="/%")
_url_path = f"{_encoded_path}?{_raw_query}"
else:
_url_path = urllib.parse.quote(path, safe="/%\\")
url = f"{target}{_url_path}"
ua = user_agent or random.choice(NORMAL_USER_AGENTS)
req = urllib.request.Request(url, headers={"User-Agent": ua})
if method == "POST":
@ -270,6 +287,7 @@ MODES = {
"cmdi": lambda t, c, d: run_attack(t, c, d, "cmdi"),
"log4shell": lambda t, c, d: run_attack(t, c, d, "log4shell"),
"ssrf": lambda t, c, d: run_attack(t, c, d, "ssrf"),
"xxe": lambda t, c, d: run_attack(t, c, d, "xxe"),
"scanner": lambda t, c, d: run_scanner(t, c, d),
"flood": lambda t, c, d: run_flood(t, c, d),
"mixed": lambda t, c, d: run_mixed(t, c, d),

View File

@ -25,7 +25,7 @@ services:
ports:
- "${POSTGRES_HOST_PORT:-16969}:5432"
volumes:
- postgres_dev:/var/lib/postgresql/data
- postgres_dev:/var/lib/postgresql
networks:
- vigil_dev
healthcheck:
@ -72,6 +72,7 @@ services:
ports:
- "${BACKEND_HOST_PORT:-36969}:8000"
volumes:
- ./backend:/app
- model_data_dev:/app/data/models
- nginx_logs_dev:/var/log/nginx
depends_on:
@ -95,6 +96,7 @@ services:
container_name: vigil-frontend-dev
environment:
VITE_API_TARGET: http://backend:8000
CI: "true"
ports:
- "${FRONTEND_HOST_PORT:-46969}:5173"
volumes:

View File

@ -42,9 +42,12 @@
"vite": "npm:rolldown-vite@7.3.1",
"vite-tsconfig-paths": "^6.0.5"
},
"overrides": {
"vite": "npm:rolldown-vite@7.3.1"
},
"pnpm": {
"overrides": {
"vite": "npm:rolldown-vite@7.2.5"
}
"onlyBuiltDependencies": [
"@parcel/watcher"
]
}
}

View File

@ -4,9 +4,6 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
vite: npm:rolldown-vite@7.2.5
importers:
.:
@ -62,7 +59,7 @@ importers:
version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react':
specifier: ^5.1.2
version: 5.1.4(rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3))
version: 5.1.4(rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3))
sass:
specifier: ^1.97.3
version: 1.97.3
@ -79,11 +76,11 @@ importers:
specifier: ~5.9.3
version: 5.9.3
vite:
specifier: npm:rolldown-vite@7.2.5
version: rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3)
specifier: npm:rolldown-vite@7.3.1
version: rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3)
vite-tsconfig-paths:
specifier: ^6.0.5
version: 6.1.1(rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3))(typescript@5.9.3)
version: 6.1.1(rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3))(typescript@5.9.3)
packages:
@ -321,12 +318,12 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@oxc-project/runtime@0.97.0':
resolution: {integrity: sha512-yH0zw7z+jEws4dZ4IUKoix5Lh3yhqIJWF9Dc8PWvhpo7U7O+lJrv7ZZL4BeRO0la8LBQFwcCewtLBnVV7hPe/w==}
'@oxc-project/runtime@0.101.0':
resolution: {integrity: sha512-t3qpfVZIqSiLQ5Kqt/MC4Ge/WCOGrrcagAdzTcDaggupjiGxUx4nJF2v6wUCXWSzWHn5Ns7XLv13fCJEwCOERQ==}
engines: {node: ^20.19.0 || >=22.12.0}
'@oxc-project/types@0.97.0':
resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==}
'@oxc-project/types@0.101.0':
resolution: {integrity: sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==}
'@parcel/watcher-android-arm64@2.5.6':
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
@ -416,95 +413,89 @@ packages:
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
'@rolldown/binding-android-arm64@1.0.0-beta.50':
resolution: {integrity: sha512-XlEkrOIHLyGT3avOgzfTFSjG+f+dZMw+/qd+Y3HLN86wlndrB/gSimrJCk4gOhr1XtRtEKfszpadI3Md4Z4/Ag==}
'@rolldown/binding-android-arm64@1.0.0-beta.53':
resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-darwin-arm64@1.0.0-beta.50':
resolution: {integrity: sha512-+JRqKJhoFlt5r9q+DecAGPLZ5PxeLva+wCMtAuoFMWPoZzgcYrr599KQ+Ix0jwll4B4HGP43avu9My8KtSOR+w==}
'@rolldown/binding-darwin-arm64@1.0.0-beta.53':
resolution: {integrity: sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.0.0-beta.50':
resolution: {integrity: sha512-fFXDjXnuX7/gQZQm/1FoivVtRcyAzdjSik7Eo+9iwPQ9EgtA5/nB2+jmbzaKtMGG3q+BnZbdKHCtOacmNrkIDA==}
'@rolldown/binding-darwin-x64@1.0.0-beta.53':
resolution: {integrity: sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-freebsd-x64@1.0.0-beta.50':
resolution: {integrity: sha512-F1b6vARy49tjmT/hbloplzgJS7GIvwWZqt+tAHEstCh0JIh9sa8FAMVqEmYxDviqKBaAI8iVvUREm/Kh/PD26Q==}
'@rolldown/binding-freebsd-x64@1.0.0-beta.53':
resolution: {integrity: sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50':
resolution: {integrity: sha512-U6cR76N8T8M6lHj7EZrQ3xunLPxSvYYxA8vJsBKZiFZkT8YV4kjgCO3KwMJL0NOjQCPGKyiXO07U+KmJzdPGRw==}
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53':
resolution: {integrity: sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50':
resolution: {integrity: sha512-ONgyjofCrrE3bnh5GZb8EINSFyR/hmwTzZ7oVuyUB170lboza1VMCnb8jgE6MsyyRgHYmN8Lb59i3NKGrxrYjw==}
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53':
resolution: {integrity: sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.50':
resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==}
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.53':
resolution: {integrity: sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.50':
resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==}
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.53':
resolution: {integrity: sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-beta.50':
resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==}
'@rolldown/binding-linux-x64-musl@1.0.0-beta.53':
resolution: {integrity: sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-beta.50':
resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==}
'@rolldown/binding-openharmony-arm64@1.0.0-beta.53':
resolution: {integrity: sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-wasm32-wasi@1.0.0-beta.50':
resolution: {integrity: sha512-nmCN0nIdeUnmgeDXiQ+2HU6FT162o+rxnF7WMkBm4M5Ds8qTU7Dzv2Wrf22bo4ftnlrb2hKK6FSwAJSAe2FWLg==}
'@rolldown/binding-wasm32-wasi@1.0.0-beta.53':
resolution: {integrity: sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50':
resolution: {integrity: sha512-7kcNLi7Ua59JTTLvbe1dYb028QEPaJPJQHqkmSZ5q3tJueUeb6yjRtx8mw4uIqgWZcnQHAR3PrLN4XRJxvgIkA==}
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53':
resolution: {integrity: sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50':
resolution: {integrity: sha512-lL70VTNvSCdSZkDPPVMwWn/M2yQiYvSoXw9hTLgdIWdUfC3g72UaruezusR6ceRuwHCY1Ayu2LtKqXkBO5LIwg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ia32]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.50':
resolution: {integrity: sha512-4qU4x5DXWB4JPjyTne/wBNPqkbQU8J45bl21geERBKtEittleonioACBL1R0PsBu0Aq21SwMK5a9zdBkWSlQtQ==}
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.53':
resolution: {integrity: sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/pluginutils@1.0.0-beta.50':
resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==}
'@rolldown/pluginutils@1.0.0-beta.53':
resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
'@rolldown/pluginutils@1.0.0-rc.3':
resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==}
@ -1197,14 +1188,14 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rolldown-vite@7.2.5:
resolution: {integrity: sha512-u09tdk/huMiN8xwoiBbig197jKdCamQTtOruSalOzbqGje3jdHiV0njQlAW0YvzoahkirFePNQ4RYlfnRQpXZA==}
rolldown-vite@7.3.1:
resolution: {integrity: sha512-LYzdNAjRHhF2yA4JUQm/QyARyi216N2rpJ0lJZb8E9FU2y5v6Vk+xq/U4XBOxMefpWixT5H3TslmAHm1rqIq2w==}
engines: {node: ^20.19.0 || >=22.12.0}
deprecated: Use 7.3.1 for migration purposes. For the most recent updates, migrate to Vite 8 once you're ready.
deprecated: Use this package to migrate from Vite 7 to Vite 8. For the most recent updates, migrate to Vite 8 once you're ready.
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
esbuild: ^0.25.0
esbuild: ^0.27.0
jiti: '>=1.21.0'
less: ^4.0.0
sass: ^1.70.0
@ -1238,8 +1229,8 @@ packages:
yaml:
optional: true
rolldown@1.0.0-beta.50:
resolution: {integrity: sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==}
rolldown@1.0.0-beta.53:
resolution: {integrity: sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
@ -1692,9 +1683,9 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
'@oxc-project/runtime@0.97.0': {}
'@oxc-project/runtime@0.101.0': {}
'@oxc-project/types@0.97.0': {}
'@oxc-project/types@0.101.0': {}
'@parcel/watcher-android-arm64@2.5.6':
optional: true
@ -1757,51 +1748,48 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.6
optional: true
'@rolldown/binding-android-arm64@1.0.0-beta.50':
'@rolldown/binding-android-arm64@1.0.0-beta.53':
optional: true
'@rolldown/binding-darwin-arm64@1.0.0-beta.50':
'@rolldown/binding-darwin-arm64@1.0.0-beta.53':
optional: true
'@rolldown/binding-darwin-x64@1.0.0-beta.50':
'@rolldown/binding-darwin-x64@1.0.0-beta.53':
optional: true
'@rolldown/binding-freebsd-x64@1.0.0-beta.50':
'@rolldown/binding-freebsd-x64@1.0.0-beta.53':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50':
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50':
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53':
optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.50':
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.53':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.50':
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.53':
optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-beta.50':
'@rolldown/binding-linux-x64-musl@1.0.0-beta.53':
optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-beta.50':
'@rolldown/binding-openharmony-arm64@1.0.0-beta.53':
optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-beta.50':
'@rolldown/binding-wasm32-wasi@1.0.0-beta.53':
dependencies:
'@napi-rs/wasm-runtime': 1.1.1
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50':
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53':
optional: true
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50':
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.53':
optional: true
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.50':
optional: true
'@rolldown/pluginutils@1.0.0-beta.50': {}
'@rolldown/pluginutils@1.0.0-beta.53': {}
'@rolldown/pluginutils@1.0.0-rc.3': {}
@ -1860,7 +1848,7 @@ snapshots:
dependencies:
csstype: 3.2.3
'@vitejs/plugin-react@5.1.4(rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3))':
'@vitejs/plugin-react@5.1.4(rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
@ -1868,7 +1856,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-rc.3
'@types/babel__core': 7.20.5
react-refresh: 0.18.0
vite: rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3)
vite: rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3)
transitivePeerDependencies:
- supports-color
@ -2383,39 +2371,38 @@ snapshots:
reusify@1.1.0: {}
rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3):
rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3):
dependencies:
'@oxc-project/runtime': 0.97.0
'@oxc-project/runtime': 0.101.0
fdir: 6.5.0(picomatch@4.0.3)
lightningcss: 1.31.1
picomatch: 4.0.3
postcss: 8.5.6
rolldown: 1.0.0-beta.50
rolldown: 1.0.0-beta.53
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 25.3.2
fsevents: 2.3.3
sass: 1.97.3
rolldown@1.0.0-beta.50:
rolldown@1.0.0-beta.53:
dependencies:
'@oxc-project/types': 0.97.0
'@rolldown/pluginutils': 1.0.0-beta.50
'@oxc-project/types': 0.101.0
'@rolldown/pluginutils': 1.0.0-beta.53
optionalDependencies:
'@rolldown/binding-android-arm64': 1.0.0-beta.50
'@rolldown/binding-darwin-arm64': 1.0.0-beta.50
'@rolldown/binding-darwin-x64': 1.0.0-beta.50
'@rolldown/binding-freebsd-x64': 1.0.0-beta.50
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.50
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.50
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.50
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.50
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.50
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.50
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.50
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.50
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.50
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.50
'@rolldown/binding-android-arm64': 1.0.0-beta.53
'@rolldown/binding-darwin-arm64': 1.0.0-beta.53
'@rolldown/binding-darwin-x64': 1.0.0-beta.53
'@rolldown/binding-freebsd-x64': 1.0.0-beta.53
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.53
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.53
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.53
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.53
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.53
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.53
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.53
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.53
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.53
run-parallel@1.2.0:
dependencies:
@ -2603,12 +2590,12 @@ snapshots:
util-deprecate@1.0.2: {}
vite-tsconfig-paths@6.1.1(rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3))(typescript@5.9.3):
vite-tsconfig-paths@6.1.1(rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3))(typescript@5.9.3):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.9.3)
vite: rolldown-vite@7.2.5(@types/node@25.3.2)(sass@1.97.3)
vite: rolldown-vite@7.3.1(@types/node@25.3.2)(sass@1.97.3)
transitivePeerDependencies:
- supports-color
- typescript

View File

@ -31,11 +31,11 @@ COPY backend/pyproject.toml ./
RUN uv pip install --system -e ".[dev,ml]"
COPY backend/ .
COPY infra/docker/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh && \
COPY infra/docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh && \
mkdir -p /app/data/models
EXPOSE 8000
ENTRYPOINT ["/app/entrypoint.sh"]
ENTRYPOINT ["/entrypoint.sh"]
CMD ["uvicorn", "app.factory:create_app", "--factory", "--host", "0.0.0.0", "--port", "8000", "--reload"]

View File

@ -16,7 +16,7 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY frontend/package.json ./
COPY frontend/package.json frontend/pnpm-lock.yaml* frontend/pnpm-workspace.yaml* ./
RUN pnpm install
COPY frontend/ .

View File

@ -16,7 +16,7 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY frontend/package.json ./
COPY frontend/package.json frontend/pnpm-lock.yaml* frontend/pnpm-workspace.yaml* ./
RUN pnpm install --frozen-lockfile
COPY frontend/ .