If you discover a security vulnerability, please do not file a public issue. Instead, email the maintainers directly.
- Use
.envfiles (see.env.example) - Never hardcode API keys in source code
- The
.gitignoreis configured to exclude.envfiles
- Do not upload confidential papers or private documents as examples
- The
examples/directory should only contain public-domain or permissively licensed content - Review your output files before sharing them publicly
When future phases add real image generation API support:
- Always use environment variables for API keys
- Never log or display API keys in output
- Use
.env.exampleas a template, never commit your actual.env - Rotate keys if accidentally committed
Status: local-only — no external image APIs are called.
Current image adapters:
| Adapter | Status | External API | API key | Description |
|---|---|---|---|---|
| placeholder | available | no | no | Generates local SVG placeholder images with style presets |
| fixture | experimental | no | no | Deterministic SVG for CI/testing with style presets |
Image style presets (Phase 4B):
| Style | Description |
|---|---|
| clean-cartoon-explainer | Clean cartoon-style, blue palette (default) |
| whiteboard | Whiteboard sketch, dark marker accents |
| storybook | Warm storybook, amber palette |
| technical-diagram | Precise diagram, green palette |
Safety guarantees:
-
Current image adapters are local SVG renderers.
placeholderandfixturegenerate SVG files purely locally.- No network requests, no API keys, no external services.
-
Future real image adapters must fail closed.
- Any future adapter that calls external image APIs (DALL-E, Stable Diffusion, etc.) must require explicit opt-in.
- Must require
--allow-external-image-apior equivalent flag. - Must validate API keys before creating any output directory.
-
Image prompts should not include secrets.
- Image prompts are derived from card titles and visual metaphors.
- No API keys, passwords, or environment variables appear in prompts.
- Prompt content is always safe for display in HTML/Markdown output.
-
Transparent manifest.
image_manifest.jsondisclosesstyle,generated_locally, andexternal_image_api.- Every image record includes
safety_notesconfirming local-only generation.
Status: experimental — opt-in only.
Fail-closed pattern (matches OpenAI provider):
-
allow_external_imagesflag — defaults toFalse.- Set via
--allow-external-imagesCLI flag. - Adapter constructor sets
self.allow_external_images = False. - Every
generate_images()call runs_check_fail_closed().
- Set via
-
API key validation —
OPENAI_API_KEYfromos.environonly.- Key is NEVER printed, logged, or written to any file.
- Key is validated for format (
sk-orssk-prefix) before any network call. - Key is set inside
urllib.request.Requestheaders ONLY — never stored in adapter state.
-
No prompt logging.
- Image prompts are NOT written to logs, stdout, or files.
image_manifest.jsonstores only a short prompt preview (first 120 chars).- Source excerpts are NEVER sent to the image API.
-
Transport is mock-injectable.
OpenAIImageAdapter._call_transportis the injection point.- CI and tests replace it with
run_mock_openai_image_transport(). - Mock transport returns a placeholder SVG with "NO real API call was made" note.
-
Manifest disclosure.
image_manifest.jsonsetsuses_external_api: trueandrequires_api_key: true.adapter_notesincludes the model name and endpoint.safety_noteson each image record confirms "External API call was made".
-
Error handling.
- On API failure, a placeholder SVG is generated (no crash).
- Error messages are sanitized — no API key or prompt in error text.
- Network errors are caught and reported without sensitive data.
Safety guarantees:
-
Default is safe. Without
--allow-external-images, all image generation fails closed with a clear error message. -
API key is protected. Read from
os.environonly, never cached, never logged. -
Prompts are clean. No secrets, no source excerpts, no API keys in prompts.
-
Transport is mockable. All CI tests use mock transport — zero real API calls.
-
Manifest is transparent.
image_manifest.jsonalways discloses external API usage. -
Errors don't leak data. Sanitized error messages only.
-
No real images.
- Phase 4B does NOT call DALL-E, Stable Diffusion, or any other image generation API.
- Generated SVGs are educational illustrations — no photorealistic content.
-
No document upload.
- Image adapters only consume card metadata (title, prompt) — source text is never sent to external services.
- Prompt content is limited to card titles and visual metaphor descriptions.<|end▁of▁thinking|>
- Providers that make network requests must NOT be enabled by default
- The
openaiprovider is fail-closed — requires--allow-external-api+OPENAI_API_KEY - Disabled providers must fail closed (no partial output)
- Attempting to use a disabled provider must produce a clear error message
Status: experimental — requires explicit opt-in.
Safety rules:
-
API key via environment variable only.
- Uses
OPENAI_API_KEYenv var — no hardcoded keys in source - Key is validated BEFORE any output directory is created (fail-closed ordering)
- Uses
-
No SDK dependency.
- Uses direct HTTP (
urllib.request) toapi.openai.com - No
import openai— avoids SDK code risk surface - No API key appears in payload, headers, or error messages
- Uses direct HTTP (
-
Mock-only testing.
- All 81 OpenAI tests (4 test files) use
unittest.mock— zero real API calls - CI fail-closed tests run without any
OPENAI_API_KEYset
- All 81 OpenAI tests (4 test files) use
-
Provider manifest disclosure.
uses_external_api: true,requires_api_key: truesafety.uploads_documents: true(documents are sent to OpenAI API)- Manifest is regenerated on every run
Every analysis run produces a provider_manifest.json file that discloses:
uses_external_api: whether network requests were maderequires_api_key: whether an API key is neededsafety.uploads_documents: whether documents are sent to external serverssafety.reads_api_key: whether the provider reads API keys from environmentsafety.writes_secrets: whether the provider writes secrets to output files
- Must NOT produce partial output files
- Must NOT create output directories (or must clean them up on failure)
- Must raise
RuntimeErrorwith a clear message - Must list available providers in the error message
- Every card must have non-empty
source_chunk_ids uses_external_apimust be set correctly- No fabrication of data, claims, or conclusions
- Fully offline: no network calls, no subprocess spawning, no environment variable reads
- Safe for development and testing without any external dependencies
- Serves as the reference implementation for local provider safety
Status: experimental — requires explicit opt-in.
Safety rules:
-
Only loopback endpoints allowed
- Allowed:
http://localhost:...,http://127.0.0.1:...,http://[::1]:... - Rejected: any HTTPS, any remote HTTP, any LAN address (
192.168.x.x,10.x.x.x,172.16.x.x)
- Allowed:
-
No Authorization headers
- The provider does NOT send
Authorizationheaders - No API keys are read or attached
- The provider does NOT send
-
No remote HTTP
- All requests must go to loopback addresses only
- DNS rebinding protection:
localhostis resolved to verify it maps to127.0.0.1
-
--allow-local-httprequired- Default behavior: fail closed
- Must pass
--allow-local-httpto CLI for any HTTP call to proceed - Fixture protocol (
--local-http-protocol fixture) does NOT require this flag
-
Prompt content should not be logged
- The provider does NOT print full prompts to stdout/stderr
- This avoids accidental exposure of document content in logs
Provider manifest disclosure:
The provider_manifest.json for local-http includes a network block:
{
"network": {
"uses_local_http": false,
"allows_remote_http": false,
"endpoint": null,
"protocol": "fixture",
"timeout_seconds": 30
}
}When uses_local_http is true, the endpoint field will show the actual endpoint.
The local-fixture provider is explicitly designed to be fully offline:
- It does not make any network requests, including to localhost
- It does not spawn subprocesses
- It does not read environment variables
- All responses are generated locally from static data
Any future local provider that makes network calls (including to localhost) must:
- Explicitly disclose this behavior in its documentation
- Set
uses_external_api: truein the provider manifest - Be disabled by default if it makes external network requests
When debugging or logging provider prompts:
- Never include API keys, tokens, or other secrets in prompt dumps
- Sanitize all output to remove sensitive information
- Use placeholder values (e.g.,
sk-...) when demonstrating prompt structures
The doctor command is completely offline:
- No network requests
- No DNS resolution
- No subprocess spawning
- No file modifications
- Exit code 0 on success
python -m explainlens.cli doctorThe validate-endpoint command performs static validation only:
- No HTTP requests
- No DNS resolution
- No network traffic of any kind
- Parses URL structure and validates against loopback-only policy
python -m explainlens.cli validate-endpoint http://localhost:11434/api/chat
# Static check only — no network call
python -m explainlens.cli validate-endpoint https://api.openai.com/v1/chat/completions
# Static check only — rejects immediately, no network callThe files in examples/configs/ are reference templates:
- They are NOT automatically loaded by ExplainLens
- They serve as documentation for users to understand the expected JSON structure
- Users must manually configure their own setup
- No secrets are included in these example files
The local-http provider never sends Authorization headers:
- No API keys are read from environment variables
- No Bearer tokens are attached to requests
- No authentication credentials are sent
- This is by design — local model servers (Ollama, LM Studio, llama.cpp) typically don't require auth
Verification:
# Check provider manifest for safety disclosures
cat outputs/ci_local_http_fixture/provider_manifest.json | grep -A10 '"safety"'Output should include:
"safety": {
"uploads_documents": false,
"reads_api_key": false,
"writes_secrets": false
}- Keep dependencies updated
- We use only well-maintained, widely-used packages
- Current dependencies:
jinja2,pydantic,pytest(dev)