Repository navigation
Replies: 2 comments 1 reply
This comment was marked as spam.
This comment was marked as spam.
|
Thank you. I agree on the first phase: transcript, summary, title, prompt and query text will be logged at |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Description
Speakr writes user-derived content and personal data to its application log at
INFOlevel.INFOis the default production level.Operators, administrators, and anyone with access to container or host logs can read uploaded filenames, transcription titles and summaries, raw LLM responses, chat queries and chat content, hotwords, speaker names, tag and folder names, emails, usernames, and absolute filesystem paths of uploaded audio.
This is a direct privacy exposure under GDPR/CCPA. It also contradicts the OWASP Logging Cheat Sheet, which requires logs to record what happened (events, IDs, counts, durations) rather than the data itself.
The defect was corroborated on a live instance. Uploading a short audio file produced an
INFO-level log of the full multi-line raw LLM response. It spanned roughly twelve physical lines.The goal is to keep
INFOand above limited to operational metadata. Full payloads move toDEBUGbehind an explicit opt-in toggle. Phase 1 is the desired production fix; phases 2-4 are optional hardening.Affected Version
v0.10.21-alpha
Requested Feature
Introduce a shared redaction helper. Phase 1 (CONTENT) is desired: remove user-derived content from
INFOand above. Phases 2-4 are optional follow-ups for PII, paths, and connector details. Full payloads are available only inDEBUGbehind an explicit opt-in toggle and a size cap. IP addresses stay in security events per OWASP as an explicit, documented exception.The helper is a new module
src/utils/log_redaction.py. It exposes pure functions:compact_json,redact_path,redact_user,sanitize_text, andtruncate. It also exposes thindebug_payloadandlog_payloadwrappers.Two environment toggles are added.
LOG_RAW_PAYLOADSdefaults tofalse.LOG_RAW_MAX_CHARSdefaults to2000.File references reduce to recording, session, and staging IDs at
INFO. Basename and absolute path appear only in gatedDEBUG.User identifiers reduce to numeric
user_idatINFO. Username and email appear only in gatedDEBUG.IP addresses stay in security events per OWASP. This is an explicit, documented exception.
Connector request URLs drop the query string at
INFO.Proposed Implementation
src/utils/log_redaction.pyas the single owner of the redaction policy. Path redaction reduces to internal IDs. User redaction reduces to numeric ID. Text sanitization collapses whitespace and newlines. Truncation followsLOG_RAW_MAX_CHARS. Payloads moved toDEBUGare serialized as compact single-line JSON. All remediated call sites use the helper instead of ad-hoc f-strings.LOG_RAW_PAYLOADS(defaultfalse) andLOG_RAW_MAX_CHARS(default2000) from the environment insrc/app.py:148-161, next toLOG_LEVEL. Document them indocs/getting-started/installation.md:273.INFOand above. Touchsrc/tasks/processing.py:856,src/api/inquire.py:195,src/api/recordings.py:3178,src/services/llm.py:453, and related modules. Move full payloads toDEBUGthroughdebug_payload, which checks both the toggle and the effective level.user_idinINFO. Touchsrc/api/inquire.py:195,src/api/auth.py:478,src/api/recordings.py:3178, and admin paths. Speaker names become IDs or counts.src/services/recording_ingestion.py:1018,src/utils/audio_conversion.py:167,src/utils/ffprobe.py:141, andsrc/file_exporter.py:285.src/utils/error_body.py:65(redact_secrets) andsrc/utils/error_body.py:79(summarize_error_body) redaction consistently to connector error bodies and miscellaneous logged bodies. Check connector logs for leaked tokens or secrets.src/services/transcription/connectors/asr_endpoint.py:195(optional, part of phase 4). The httpx-emitted request URL lines are third-party and are covered by the backstop filter. Keep host and path only.src/utils/error_body.py:65already redacts credentials and absolute directories.src/utils/error_formatting.py:349providestruncate_middle.Concrete pytest cases:
tests/test_log_redaction.py::test_compact_json_single_line: input{"a": "x\ny"}; assert output contains no\nor\rand parses as JSON.tests/test_log_redaction.py::test_truncate_caps_length: input a 5000-character string with cap 2000; assert result length <= 2000 plus marker and contains[truncated].tests/test_log_redaction.py::test_redact_path_keeps_id: input/data/uploads/foo.mp3with recording id 29; assert output isrecording 29and contains no slash orfoo.tests/test_log_redaction.py::test_redact_user_keeps_id: input usernamealiceand emailalice@example.comwith user id 7; assert output is7and contains neitheralicenorexample.com.tests/test_log_redaction.py::test_debug_payload_respects_toggle: withLOG_RAW_PAYLOADS=false, calldebug_payloadfor a payload; assert no record emitted even atDEBUG.tests/test_cov_processing.pycaplog case: run the summary path with caplog atINFO; assert"Raw LLM response"not in any record message and no record message contains a newline.tests/test_cov_inquire.pycaplog case: run an inquire request with caplog atINFO; assert the user message text and username are absent from record messages.src/forlogger\.(info|warning|debug|error)calls containing\nor\rliterals or f-string interpolation of filenames, titles, summaries, or emails; expect zero matches after remediation.Expected Behavior
INFOand above logs contain no transcription text, summaries, titles, prompts, raw LLM responses, hotwords, snippets, queries, chat content, or response bodies.DEBUGonly when bothLOG_RAW_PAYLOADS=trueandLOG_LEVEL=DEBUGare set. They are truncated toLOG_RAW_MAX_CHARScharacters.src/services/login_lock.py:96, authentication paths) as an explicit OWASP exception.FECHA - app - LEVEL - mensajetext format keep working unchanged.Root Cause Analysis
Logging is configured in
src/app.py:148-161with a singlelogging.StreamHandlerand a plain text formatter. There is no shared redaction policy.Call sites build messages with f-strings that interpolate user-derived values.
Concrete leak sites confirmed in the code and in the live log include
src/services/recording_ingestion.py:1018(File saved to {filepath}),src/services/recording_ingestion.py:413andsrc/utils/audio_conversion.py:167(Detected codec for {original_filename}),src/services/recording_ingestion.py:1150(Final MIME type: ... for file {filepath}),src/tasks/processing.py:633(AI title generated for recording ...: {title}),src/tasks/processing.py:445(Title set for recording ...: {final_title}),src/tasks/processing.py:856(Raw LLM response for recording ...: '{raw_response}'),src/tasks/processing.py:859(Processed summary length ...),src/api/inquire.py:195(Inquire chat - User: {user.username}, Query: '{user_message}'),src/api/inquire.py:393andsrc/api/inquire.py:404(Search query ... returned ... chunks),src/api/recordings.py:3178(Created ... auto-shares for recording ... via group tag '{tag.name}'),src/file_exporter.py:285andsrc/file_exporter.py:726(export paths), and many filename-interpolating warnings insrc/utils/ffprobe.py:141andsrc/utils/audio_conversion.py:167.The raw LLM response log at
src/tasks/processing.py:856is the site that produced the roughly twelve-lineINFOrecord observed in production. TheLOG_LEVELdefault isINFO(src/app.py:149), so all of these execute in a default deployment.There is no existing redaction utility for log messages.
src/utils/error_body.py:65is the closest prior art but is scoped to error bodies returned by external services.Additional Context
Affected modules and representative sensitive sites by phase, in citecheck form (
path:line).Phase 1 (CONTENT):
src/tasks/processing.py:445,src/tasks/processing.py:633,src/tasks/processing.py:856,src/tasks/processing.py:859,src/tasks/processing.py:2989,src/api/inquire.py:195,src/api/inquire.py:393,src/api/inquire.py:404,src/api/inquire.py:563,src/services/llm.py:254,src/services/llm.py:278,src/services/llm.py:305,src/services/llm.py:453,src/services/llm.py:484,src/services/llm.py:487,src/services/llm.py:489,src/services/llm.py:498,src/services/llm.py:599,src/services/llm.py:630.Phase 2 (PII):
src/api/inquire.py:195,src/api/recordings.py:3178,src/api/recordings.py:3249,src/api/auth.py:478,src/api/auth.py:519,src/api/auth.py:773,src/api/auth.py:794,src/api/auth.py:803,src/services/inquire_agent.py:1573.Phase 3 (PATH):
src/services/recording_ingestion.py:1018,src/services/recording_ingestion.py:413,src/services/recording_ingestion.py:828,src/services/recording_ingestion.py:1150,src/utils/audio_conversion.py:167,src/utils/audio_conversion.py:206,src/utils/audio_conversion.py:215,src/utils/audio_conversion.py:266,src/utils/audio_conversion.py:271,src/utils/audio_conversion.py:277,src/utils/audio_conversion.py:285,src/utils/audio_conversion.py:297,src/utils/ffprobe.py:141,src/utils/ffprobe.py:271,src/utils/ffprobe.py:338,src/file_exporter.py:285,src/file_exporter.py:726.Phase 4 (CREDENTIAL/OTHER):
src/services/transcription/connectors/asr_endpoint.py:195(request URL log), and any connector or service that logs raw error bodies. The httpx-emitted request URL lines are third-party and are covered by the backstop filter. Reusesrc/utils/error_body.py:79(summarize_error_body) andsrc/utils/error_body.py:65(redact_secrets).IP addresses are retained in security events per OWASP. Notably
src/services/login_lock.py:96(Password sign-in locked for user id ... from ...) andsrc/api/auth.py:342. This is an explicit, documented exception to PII redaction.Tests should be added as
tests/test_log_redaction.py. Cover the helper: no newlines in output, truncation, path and user redaction, JSON validity.Add per-phase caplog assertions of absence of sensitive substrings at
INFO. Add a static check that nologger.*call interpolates\n,\r, or sensitive values.Manual smoke should run against the test instance on port 8899. Use
LOG_LEVEL=INFO, thenLOG_LEVEL=DEBUGwithLOG_RAW_PAYLOADS=true.Phase 1 is the desired, reviewable unit. Phases 2-4 are optional and can be delivered separately. Rollback is a revert of the relevant phase commits.
No schema, API, UI, or formatter change is required beyond the two environment toggles.
All reactions