项目文件夹

文件
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

3.5 KiB

ADR-0003: Reject universal raise-without-from enforcement

Date: 2026-03-28 Status: Accepted

Context

PR #3225 proposed a pre-commit hook (check-raise-without-from) to enforce raise X(...) from e inside all except blocks, citing PEP 3134 (explicit exception chaining). The hook would flag any raise NewException(...) inside an except handler that omits the from clause.

The conflict with existing security hooks

The codebase already enforces exception sanitization to prevent PII leakage through three mechanisms:

  1. check-sensitive-logging.py blocks exception variables in logger.warning/error/critical() calls and forbids exc_info=True on production log levels. Only logger.exception() and logger.debug(..., exc_info=True) are permitted.

  2. fix-exception-logging.py auto-removes exception variable references from f-strings and exc_info=True from non-debug logs.

  3. Intentional chain-breaking pattern used across the codebase: catch a broad exception, log full details with logger.exception(), then raise a sanitized application exception without from e:

    except Exception as e:
        logger.exception("Error getting news feed")    # full details logged
        raise NewsFeedGenerationException(str(e), ...)  # no "from e"
    

    This pattern appears in news/api.py, notifications/service.py, utilities/es_utils.py, and others. Flask error handlers then return only error_code and message to the client — never stack traces.

Why raise X from e is harmful here

raise X from e preserves the full original exception chain at the Python runtime level. Even if logs are sanitized, the chain propagates to:

  • Error tracking services (Sentry, DataDog) that capture __cause__
  • Any downstream handler that logs with exc_info=True
  • Test output and development tooling that prints full tracebacks

If the original exception contains PII (SQL errors with user data, API responses with auth tokens, file paths with usernames), preserving the chain defeats the sanitization strategy.

When from e IS appropriate

Explicit chaining is valuable in infrastructure code where:

  • The original exception contains no user data (e.g., config parsing errors)
  • Both exceptions will be caught and handled before reaching any boundary
  • Debugging requires understanding the root cause chain

The seven existing from e usages in the codebase (mcp/client.py, config/llm_config.py, security/path_validator.py, exporters/) follow this pattern correctly.

Decision

Do not add a universal raise-without-from enforcement hook. The existing pattern of intentional chain-breaking for PII protection takes precedence over PEP 3134 compliance.

Developers should use their judgment:

  • Use raise X from e when the original exception is safe to propagate (no user data, internal infrastructure errors)
  • Use raise X from None when you want to explicitly suppress the chain and document the intent
  • Omit from when wrapping user-facing exceptions to sanitize error details (the current default pattern)

Consequences

  • The allowlisted files in the rejected PR do not need remediation
  • Exception chain decisions remain a code review concern, not an automated enforcement
  • The existing check-sensitive-logging and fix-exception-logging hooks remain the primary defense against PII leakage through exceptions
  • raise X from None is available for cases where developers want to explicitly document chain suppression, but is not required