The Bambuddy team takes security seriously. We appreciate your efforts to responsibly disclose your findings.
Please DO NOT report security vulnerabilities through public GitHub.
Instead, please report them via email to:
security@bambuddy.cool
Please include the following information in your report:
| Version | Supported |
|---|---|
| 0.1.x | :white_check_mark: |
| 0.2.x | :white_check_mark: |
Bambuddy communicates with your printers over your local network using:
The following are in scope for security reports:
The following are out of scope:
The following rules apply to every PR that touches authentication, authorization, permission gating, secret handling, or any code that decides whether to allow or deny an action. They are not aspirational — each one is enforced by a CI test that fails the build on violation.
At any security boundary, the safe default is to deny and the exceptions are listed explicitly. Denylists fail open on growth — every new resource added to the codebase is implicitly granted access until someone remembers to deny it. Allowlists fail closed: an unmapped new resource gets a 403, which is loud and recoverable.
Concretely:
_APIKEY_SCOPE_BY_PERMISSION in backend/app/core/auth.py is the
load-bearing API-key authorization map. Every Permission enum value
must be either present here with a scope flag, or present in
_APIKEY_DENIED_PERMISSIONS. Unmapped permissions return 403.Depends(require_*) decorator must be listed in the route-audit
PUBLIC_ROUTES allowlist with a justification comment, or CI fails.No except Exception: (or bare except:) in authentication,
authorization, or permission code may return a permissive value
(None, True, an admin user, an empty filter that lets everything
through, etc.). The catch-all either re-raises or returns a denial.
This is CWE-636 "Not Failing Securely" — see
https://cwe.mitre.org/data/definitions/636.html.
The lint scope is backend/app/core/auth.py,
backend/app/core/permissions.py,
backend/app/api/routes/auth*.py. Any except Exception: block in
those files must be tagged # SEC-AUTH-EXC: <reason> on the same
line; CI fails otherwise. (We use a standalone marker rather than
# noqa: ... because ruff reserves the latter syntax for its own
error codes.)
Production secrets (JWT signing keys, encryption keys, OAuth client
secrets, API tokens) have no string-literal fallback in source. The
codebase reads them from env vars or generates them on first run; if a
secret is missing AND cannot be generated, the app refuses to start
rather than booting with a known value. CI greps the source for
-change-in-production-shaped strings and fails on any hit.
Any PR that adds or modifies an auth dependency, permission check, or scope flag includes tests for the negative paths:
A test asserting the happy path passes is necessary but not sufficient. The failure modes are where the vulnerabilities live. The structural backstops above catch categories of regression; the negative-path tests catch specific regressions in the new code.
| Rule | Enforcement | Location |
|---|---|---|
| 1. Allowlist over denylist (Permission) | test_every_permission_has_a_classification |
backend/tests/integration/test_auth_apikey_rbac.py |
| 1. Allowlist over denylist (routes) | test_routes_have_explicit_auth_deps |
backend/tests/unit/test_route_auth_coverage.py |
| 2. Fail-closed in auth code | test_no_fail_open_in_auth_modules |
backend/tests/unit/test_no_fail_open_in_auth.py |
| 3. No hardcoded fallback secrets | test_no_hardcoded_secrets |
backend/tests/unit/test_no_hardcoded_secrets.py |
| 4. Negative-path tests required | Reviewer responsibility (no automated CI gate yet) | PR review |
If you are adding a CI rule, update this table. If you are removing a CI rule, you are removing a security backstop and the PR description must explain why.
Thank you for helping keep Bambuddy and its users safe!