T09 · Insecure Skill Coding Practices
- Location
scripts/venice-router.py:344- Finding
Private-Only Mode Can Silently Route Sensitive Data to Non-Private Models
- Content
View full analysis
Vulnerability Details
File Location:
scripts/venice-router.py:344-348
Related bypass:scripts/venice-router.py:1071-1074
Vulnerability Type: Fail-open privacy enforcement
Risk Level: HighVulnerable Code
python # Apply private_only filter if private_only: candidates = [m for m in models if m.get("private", False)] if not candidates: # Fall back to all models if no private ones in this tier candidates = models else: candidates = modelsThe direct model-selection path also bypasses the private-model constraint:
python if args.model: model_id = args.model model_name = args.model tier_name = "custom"Technical Analysis
The
--private-onlyoption is documented as restricting inference to private models with zero data retention and no external-provider proxying. The implementation does not enforce this as a security invariant.When the selected tier has no private candidates,
select_model()silently restores the complete model list. The premium tier consists entirely of models markedprivate: False; therefore, selecting that tier while private-only mode is enabled can still select an anonymized model that Venice may proxy to an external provider.The direct
--modelpath also accepts an arbitrary model identifier without checking whether it is private. This permits a caller to combine private-only mode with a known non-private model while the status output still includes theprivate-onlyflag.Before transmission, the program constructs
messagesfrom the system prompt, full conversation history, and current user prompt. It then serializes that content into the API request. Consequently, this flaw affects all sensitive content submitted under the purported privacy guarantee, not merely routing metadata.Attack Path
- A user enables
--private-onlyor setsVENICE_PRIVATE_ONLY=truebecause the prompt or conversation contains confidential information. - On ...[truncated 1355 chars]
- A user enables
- Remediation
View remediation
Remediation Suggestions
-
Fail closed when no private model is available
Replace the fallback with an explicit error:
python if private_only: candidates = [m for m in models if m.get("private", False)] if not candidates: raise ValueError( f"No private model is available in tier {tier!r}; " "refusing to relax private-only mode." ) else: candidates = models -
Validate explicit model overrides
Resolve
--modelagainst authoritative model metadata. When private-only mode is active, reject unknown models and any model whoseprivateproperty is not explicitlyTrue. -
Enforce the invariant immediately before transmission
Add a final check before calling
venice_chat():python if private_only: if not model_info or model_info.get("private") is not True: print( "Private-only mode requires a verified private model.", file=sys.stderr, ) sys.exit(1)This defense-in-depth check prevents future routing branches from accidentally bypassing privacy enforcement.
-
Do not label a request private-only unless enforcement succeeded
Only display the private-only status after the final selected model has been verified as private.
-
Add regression tests
Cover at least:
- Private-only mode with the premium tier
- Private-only mode with an explicit non-private model
- Private-only mode with an unknown custom model
- Automatic premium classification under private-only mode
- Interactions with tier caps, uncensored mode, thinking mode, and budget downgrades
- Verification that no network request occurs after privacy validation fails
-
Clarify trust boundaries
Documentation should accurately explain that prompts are transmitted to Venice and that privacy properties depend on the selected model and Venice's service guarantees. Av ...[truncated 101 chars]
-
