8.3 KiB
11 — Code Quality and Security
Testing Requirements
Verification preparation below is the contributor default. Confirmed maintainers may adjust scope, timing, and evidence reuse under AGENTS.md; shared correctness, compatibility, isolation, and honest reporting still apply. Full-suite triggers follow docs/testing.md and the affected behavior, not a directory name alone.
What to Run
# 至少运行与本次改动直接相关的测试
uv run --locked --no-sync pytest tests/test_<domain>.py
# 改动影响公共模块、启动流程、CLI 或智能体运行行为时扩大测试范围
uv run --locked --no-sync pytest
When to Expand Scope
Use the following as likely shared-impact boundaries. Run the full test suite when changes affect their shared behavior, lifecycle, or compatibility contract:
app/runtime/,app/adapters/, orapp/runtime/compat/- config, events, managers, adapters, and compatibility boundariesapp/chain/base.py— chain base classapp/modules/__init__.py— module base classapp/main.py— application startup- The CLI entrypoint (
moviepilot) - Agent runtime (
app/agent/) - Any shared schema in
app/schemas/types.py
Honest Reporting
- For documentation tasks, report the actual text/structure checks and documentation contract tests; distinguish these from product tests that were not run.
- Do not claim "all tests pass" unless you ran them.
- Do not describe unexecuted checks as completed.
Writing New Tests
- When fixing a bug, prefer adding a test that reproduces it first.
- When adding a feature, add at minimum the smallest useful test coverage.
- Test files go in
tests/, namedtest_<domain>.py. - Use the patterns established in adjacent test files (fixtures, mock patterns, assertion style).
- Agent-related tests are under
tests/test_agent_*.py. Integration-style tests may be intests/cases/ortests/manual/.
Static Analysis
# 生成全应用检查报告;改动文件检查按 AGENTS.md 规定选择路径
uv run --locked --no-sync pylint app/
- Changed-file pylint must pass the workflow's configured check under the applicable preparation arrangement; select the PR-base plus uncommitted union from
AGENTS.md. The full application report is advisory for ordinary scoped changes. - Do not introduce error-level issues; handle warning-level findings according to the configured changed-file check rather than treating all warnings as exempt.
- Do not suppress pylint warnings with
# pylint: disablewithout a documented reason.
Dependency Security Scan
uv export --quiet --locked --no-dev --no-emit-project \
--output-file /tmp/moviepilot-audit-requirements.txt
uvx --from pip-audit pip-audit \
--require-hashes --disable-pip --strict --progress-spinner off \
--requirement /tmp/moviepilot-audit-requirements.txt
- Run after runtime dependency changes; the release workflow audits the same locked dependency set before publishing images.
- Any Python vulnerability reported by this audit blocks publishing until the dependency or explicit audit policy is updated.
- Docker builds use official base images and install required runtime packages only. Image vulnerability scans and scan-driven system-package patches are not part of the release process; the locked Python dependency audit remains required.
- If upstream has no fix, assess reachability and impact before changing the audit policy; PR documentation alone does not bypass the gate.
Authentication and Authorization
API Authentication
All REST and MCP API endpoints require authentication. The project supports two mechanisms:
| Method | Format |
|---|---|
| Request header | X-API-KEY: <api_key> |
| Query parameter | ?apikey=<api_key> |
The API_TOKEN value in settings is the source of truth. It is set at initialization and never exposed in logs or API responses.
Login Throttling
Password login (/api/v1/login/access-token) is protected by app/application/security/throttle.py: consecutive failures keyed by (client address, normalized username) trigger exponential lockouts (5 failures → 30s, doubling up to 15 minutes), and the endpoint answers 429 with a Retry-After header while locked. A correct password that still needs MFA does not count as a failure; a successful login clears the record. The state lives in process memory only, and X-Forwarded-For is deliberately not trusted because there is no trusted-proxy setting.
Endpoint Authorization
- API-token authenticated integration endpoints are administrator-level surfaces unless a specific endpoint documents a narrower contract.
- Do not infer user-scoped authorization from a valid
API_TOKEN; use an explicit user identity dependency when behavior must be scoped to a logged-in user. - Use the existing FastAPI dependency functions (e.g.,
get_current_user,get_current_active_superuser) — checkapp/api/endpoints/for usage patterns. - Do not add manual token parsing inside endpoint functions. Always use the project's dependency injection.
- Superuser-only operations must explicitly require the superuser dependency.
Input Validation
- Validate user input at the endpoint layer only, using Pydantic models.
- Do not duplicate validation logic in chain or module code. Trust that the endpoint has already validated what it passes down.
- For external API responses, validate using Pydantic models or explicit
Nonechecks before accessing fields.
Secrets Management
- Never hardcode secrets (API keys, passwords, tokens) in source code.
- All secrets are configured via environment variables or
.moviepilot.envand accessed throughsettings. - Never log or serialize
settings.API_TOKEN,settings.DB_PASSWORD, or any field withSecretin its name. - Do not commit
.moviepilot.env,*.db, or any file underconfig/— these are local runtime state.
SQL Injection Prevention
- All database access goes through SQLAlchemy ORM via the Oper classes in
app/db/oper/. No raw SQL string construction. - If a raw SQL query is ever genuinely necessary, use SQLAlchemy's
text()with parameterized binds — never string interpolation.
XSS and Injection in Notifications
- When constructing notification messages that include user-provided data (media titles, filenames, usernames), treat those values as untrusted strings.
- Do not render user data in HTML contexts without escaping. Notification channels that render HTML (e.g., Telegram with
parse_mode=HTML) must escape user-controlled strings.
File Path Security
- Use
pathlib.Pathfor all file path operations. - Never construct file paths by concatenating user-provided strings.
- When transferring files to a user-configured path, verify the destination is within an allowed base directory before writing.
Contributor Pre-Submission Checklist
Before contributor submission, check applicable items below. Maintainer execution arrangements follow AGENTS.md; a local anchor does not imply completion or acceptance. The quality contracts remain shared.
- Related pytest tests pass
- Changed-file pylint passes for the PR-base plus uncommitted union defined in
AGENTS.md; use fullpylint app/for broad Python changes - If dependencies changed: the package is in the correct
pyproject.tomlgroup,uv.lockis current, the locked project consistency check and runtime dependency audit pass - If CLI behavior changed:
docs/cli.mdand related tests are updated - If MCP/API behavior changed:
docs/mcp-api.mdand related skill files are updated - If database schema changed: a new Alembic migration exists under
database/versions/ - No secrets are included in code, logs, or committed files
- Added and modified objects comply with 08 — Comments and Documentation Style: Chinese explanations, multiline triple-double-quoted docstrings, shared parameter/return field lists, and complete required module, class, function, and method documentation
- Every affected dataclass, Pydantic, and Protocol declaration has accurate documentation; Pydantic field descriptions reach the Schema, and comment migration has not changed model behavior
- Older file styles and passing static checks do not replace comment review; existing migration work does not justify new violations
Last Updated: 2026-10-17