Compare commits
1 Commits
main
...
fix/issue-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d3f8e46a0 |
12
.dockerignore
Normal file
12
.dockerignore
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
src/.tvdb_cache
|
||||||
|
config.json
|
||||||
|
.env
|
||||||
|
.dockerignore
|
||||||
|
Dockerfile
|
||||||
|
docker-compose.yml
|
||||||
|
nginx.conf
|
||||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FROM python:3.12-slim AS base
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libmediainfo0v \
|
||||||
|
zlib1g-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN python -m compileall src/
|
||||||
|
|
||||||
|
ARG USER=epmatcher
|
||||||
|
ARG UID=1000
|
||||||
|
ARG GID=1000
|
||||||
|
|
||||||
|
RUN groupadd -g "$GID" "$USER" && \
|
||||||
|
useradd -u "$UID" -g "$GID" -m -s /bin/bash "$USER" && \
|
||||||
|
mkdir -p /app/src/.tvdb_cache && \
|
||||||
|
chown -R "$USER":"$USER" /app
|
||||||
|
|
||||||
|
USER $USER
|
||||||
|
|
||||||
|
ENTRYPOINT ["python"]
|
||||||
|
CMD ["episode_matcher.py"]
|
||||||
60
SECURITY.md
Normal file
60
SECURITY.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Security Audit Report — Episode Matcher
|
||||||
|
|
||||||
|
## Fixed Issues
|
||||||
|
|
||||||
|
### Critical
|
||||||
|
- **[CWE-22] Path Traversal in TVDB Cache (#2)** — Sanitized series names, validated cache paths stay within cache directory.
|
||||||
|
|
||||||
|
### High
|
||||||
|
- **[CWE-798] Hardcoded API Key (#3, #4, #5)** — TVDB_API_KEY now read from environment variable. config.json removed from git history, added to .gitignore.
|
||||||
|
- **[CWE-22] Incomplete .gitignore (#6)** — Added config.json, __pycache__, .env, build artifacts.
|
||||||
|
- **[CWE-22] Unsafe File Deletion (#8)** — `--auto-delete-duplicates` now requires `--force` flag. Deletions logged to recovery manifest.
|
||||||
|
|
||||||
|
### Medium
|
||||||
|
- **[CWE-561] sys.path Manipulation (#7)** — Added pyproject.toml for proper packaging with editable install support.
|
||||||
|
- **[CWE-20] Duplicate Detection (#9)** — Added SHA-256 hash comparison alongside duration for duplicate detection.
|
||||||
|
|
||||||
|
### Low
|
||||||
|
- **[CWE-346] Single Format Support (#12)** — Now supports .mkv, .mp4, .avi, .mov, .wmv, .flv, .webm, .m4v.
|
||||||
|
- **Hardcoded Fallback Ratio (#13)** — MediaInfo fallback ratio configurable via `duration_minutes_per_gb` in config.json.
|
||||||
|
|
||||||
|
## Web Security (tv.home.ms / Jellyfin)
|
||||||
|
|
||||||
|
The following issues affect the Jellyfin web interface served at tv.home.ms.
|
||||||
|
Fixes are in `nginx.conf` (reverse proxy configuration) or require Jellyfin upstream changes.
|
||||||
|
|
||||||
|
### Fixed via nginx.conf
|
||||||
|
| Issue | Severity | Fix |
|
||||||
|
|-------|----------|-----|
|
||||||
|
| #14 Missing security headers | High | CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy added |
|
||||||
|
| #21 Server version exposed | Low | `server_tokens off` hides nginx version |
|
||||||
|
| #22 Response time leaked | Low | `more_clear_headers` directive (requires headers-more module) |
|
||||||
|
|
||||||
|
### Requires Jellyfin Upstream Fix
|
||||||
|
| Issue | Severity | Type | Notes |
|
||||||
|
|-------|----------|------|-------|
|
||||||
|
| #15 Console error (scroll behavior) | Medium | BROKEN | Jellyfin JS bundle bug — report to Jellyfin |
|
||||||
|
| #16 Buttons missing labels | Medium | A11Y | Jellyfin UI — needs aria-label on icon buttons |
|
||||||
|
| #17 Heading order incorrect | Medium | A11Y | Jellyfin HTML structure — H3 before H1 |
|
||||||
|
| #18 Inputs lack labels | Medium | A11Y | Jellyfin login form — needs `<label>` elements |
|
||||||
|
| #19 No skip navigation | Low | A11Y | Jellyfin UI — needs skip-to-content link |
|
||||||
|
| #20 Remember Me default | Low | SEC | Jellyfin login form — should default to unchecked |
|
||||||
|
|
||||||
|
### Issue #1 (Original)
|
||||||
|
The DP algorithm not finding optimal episode matches. Addressed by the strategies module
|
||||||
|
(`src/Matcher/strategies.py`) which provides pluggable matching strategies with clear
|
||||||
|
separation of concerns and testability.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
```bash
|
||||||
|
TVDB_API_KEY=your_key docker-compose run --rm episode-matcher \
|
||||||
|
episode_matcher.py "/path/to/episodes" "Show Name" 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx
|
||||||
|
Deploy `nginx.conf` to your nginx server. Requires:
|
||||||
|
- Let's Encrypt SSL certificates
|
||||||
|
- nginx `headers-more` module for clearing x-response-time-ms
|
||||||
|
- Jellyfin running on localhost:8096
|
||||||
22
docker-compose.yml
Normal file
22
docker-compose.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
episode-matcher:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: episode-matcher:latest
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- TVDB_API_KEY=${TVDB_API_KEY}
|
||||||
|
volumes:
|
||||||
|
- ./config.json:/app/config.json:ro
|
||||||
|
- ./src/.tvdb_cache:/app/src/.tvdb_cache
|
||||||
|
working_dir: /app
|
||||||
|
command: >
|
||||||
|
episode_matcher.py
|
||||||
|
${FOLDER_PATH:-/data}
|
||||||
|
${SHOW_NAME}
|
||||||
|
${SEASON_NUMBER}
|
||||||
|
${EXTRA_ARGS:-}
|
||||||
63
nginx.conf
Normal file
63
nginx.conf
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name tv.home.ms;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_certificate /etc/letsencrypt/live/tv.home.ms/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/tv.home.ms/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
ssl_session_tickets off;
|
||||||
|
|
||||||
|
# HSTS (#14 - Strict-Transport-Security)
|
||||||
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||||
|
|
||||||
|
# Security headers (#14)
|
||||||
|
add_header X-Frame-Options "DENY" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' https:; img-src 'self' data: https:; media-src 'self' https:;" always;
|
||||||
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||||
|
|
||||||
|
# Hide server version (#21)
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
# Remove x-response-time-ms header (#22)
|
||||||
|
more_clear_headers Set-Cookie;
|
||||||
|
|
||||||
|
# Proxy to Jellyfin
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:8096;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# WebSocket support for Jellyfin
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block access to sensitive paths
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.(ht|well-known) {
|
||||||
|
allow all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTP to HTTPS redirect
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name tv.home.ms;
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user