Skip to content

fix(security): redact sensitive logging + refurb cleanups (cluster A)#1814

Merged
chernistry merged 3 commits into
mainfrom
fix/code-scanning-cluster-a
May 21, 2026
Merged

fix(security): redact sensitive logging + refurb cleanups (cluster A)#1814
chernistry merged 3 commits into
mainfrom
fix/code-scanning-cluster-a

Conversation

@chernistry

@chernistry chernistry commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #2446 #2447 (CodeQL py/clear-text-logging-sensitive-data, HIGH). Also fixes three refurb findings in the same cluster.

Alert File:Line Fix
#2446 (CodeQL HIGH) src/bernstein/core/security/always_allow.py:294 logger.error(reason) embedded env-var-derived paths (rules_path, manifest_path). Switched to a static safety message; full path-bearing reason still goes to .sdd/metrics/guardrails.jsonl via _record_tamper_event for operator forensic review.
#2447 (CodeQL HIGH) src/bernstein/core/security/always_allow.py:302 Same site, same strategy: static log line + pointer to the metrics file. Identical treatment to #2446.
FURB123 src/bernstein/core/security/audit_chain.py:122 dict(details) -> details.copy() for an already-typed dict[str, Any] input.
FURB143 src/bernstein/core/tasks/task_lifecycle.py:568 Dropped the redundant or "" since original_error is typed str.
FURB184 src/bernstein/core/security/jwt_tokens.py:159 Chained the cast() and .get() call; the intermediate header_dict had no other users.

Redaction strategy (both HIGH sites)

CodeQL traced the BERNSTEIN_ALWAYS_ALLOW_PATH env value through rules_path -> reason -> logger.error(reason) and classified the resolved path as sensitive. The fix:

  • Application log line is now a static safety message naming the failure category (missing manifest / unreadable manifest) plus a pointer to .sdd/metrics/guardrails.jsonl.
  • The full path-bearing reason is still forwarded to _record_tamper_event, which writes it (with sha256 digests pre-redacted) to .sdd/metrics/guardrails.jsonl for forensic review.
  • The raised AlwaysAllowTamperError still carries the full reason for callers that catch and re-emit it through orchestrator-side channels.

Net effect: operators keep full debuggability via the metrics file; the application-log stream no longer carries the env-derived path.

Verification

  • uv run pytest tests/unit/ -q --no-cov --timeout=120 -k "always_allow or audit_chain or task_lifecycle or jwt_tokens or agent_identity_jwt or jwt_refresh" -> 145 passed
  • uv run ruff check . -> All checks passed
  • uv run ruff format . -> 1 file reformatted (always_allow.py whitespace only)
  • uv run refurb on the four files -> the three targeted findings cleared (FURB186 at task_lifecycle:1328 was out of scope and untouched)
  • uv run pyright -> no new errors near the edited lines (255 pre-existing baseline errors unchanged)

Test plan

  • Targeted unit tests pass (145/145)
  • Ruff check + format clean
  • Refurb findings cleared at the three cited lines
  • CodeQL re-run confirms #2446 and #2447 resolved

Summary by CodeRabbit

  • New Features

    • Added schedule CLI command for managing scheduled operations.
  • Security & Stability

    • Enhanced error logging in security operations to emit generic, operator-friendly messages.
    • Improved error handling in task lifecycle telemetry for better diagnostics.

Review Change Stack

always_allow.py:294,302 - CodeQL py/clear-text-logging-sensitive-data
flagged both logger.error(reason) sites because the reason string
embeds rules_path/manifest_path, both transitively derived from the
BERNSTEIN_ALWAYS_ALLOW_PATH env value (a tainted source). The full
reason still goes to .sdd/metrics/guardrails.jsonl via
_record_tamper_event (already digest-sanitized) for operator forensic
review; the application-log line is now a static safety message that
names the failure category and points operators at the metrics file.
This preserves debuggability without leaking the env-derived path to
the log stream.

audit_chain.py:122 - FURB123: dict(details) -> details.copy() for an
already-typed dict[str, Any] input.

task_lifecycle.py:568 - FURB143: drop the redundant `or ""` since
original_error is typed str.

jwt_tokens.py:158-159 - FURB184: chain the cast and .get() call;
header_dict had no other users.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @chernistry, you have reached your weekly rate limit of 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@chernistry chernistry enabled auto-merge (squash) May 21, 2026 21:04
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c4bc3726-af6e-4bca-9d7b-b1c158efdd80

📥 Commits

Reviewing files that changed from the base of the PR and between 139ffa3 and 3d8c453.

📒 Files selected for processing (5)
  • src/bernstein/core/security/always_allow.py
  • src/bernstein/core/security/audit_chain.py
  • src/bernstein/core/security/jwt_tokens.py
  • src/bernstein/core/tasks/task_lifecycle.py
  • tests/unit/test_readme_api_coverage.py

📝 Walkthrough

Walkthrough

This PR applies five small maintenance refinements across security modules, task lifecycle telemetry, and test documentation. Security error logging favors operator-friendly generic messages over detailed paths, audit event copying simplifies to .copy(), JWT header extraction removes a temporary variable, dead letter telemetry removes None-safe fallback logic, and CLI test documentation is updated to include the new schedule command.

Changes

Security and Telemetry Refinements

Layer / File(s) Summary
Security Module Updates
src/bernstein/core/security/always_allow.py, src/bernstein/core/security/audit_chain.py, src/bernstein/core/security/jwt_tokens.py
Lines 294–312 in always_allow.py redirect tamper-failure logging to generic error messages with exception type only, while full reason text remains in SAFETY events. Line 122 in audit_chain.py simplifies shallow-copy from dict(details) to details.copy(). Line 158 in jwt_tokens.py refactors JWT header alg extraction to direct cast-and-get, removing intermediate header_dict variable.
Task Lifecycle Telemetry
src/bernstein/core/tasks/task_lifecycle.py
Line 568 in _capture_dead_letter removes the None-safe fallback (original_error or "") and now directly slices original_error[:800] when building DLQ telemetry payload.
CLI Documentation Allowlist Update
tests/unit/test_readme_api_coverage.py
Lines 242–243 add "schedule" command to the DOCUMENTED_COMMANDS allowlist, ensuring test coverage validation passes when this new top-level CLI command is registered.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

tests

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes the primary changes: security logging redaction for CodeQL alerts and refactoring cleanups.
Description check ✅ Passed Description is comprehensive and detailed; includes what/why/how, a structured table of fixes, verification results, and test plan. Covers all critical aspects of the changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/code-scanning-cluster-a

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the core label May 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Sonar insights (advisory, no merge-block)

Snapshot of bernstein on the configured Sonar instance:

Metric Value
Coverage 13.5
Code smells 151
Bugs 11
Vulnerabilities 2
Security hotspots 91

Run bernstein doctor sonar locally for the full surface.

This comment is a soft signal. The Sonar scan runs on push to main; the PR check itself never fails on smells.

@github-actions

Copy link
Copy Markdown
Contributor

Review-bot acknowledgement summary

  • Must-address findings: 0 (0 acknowledged, 0 open)
  • Informational findings: 0

All must-address findings are resolved or acknowledged.

@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

bernstein doctor observe for PR #1814 (fix/code-scanning-cluster-a): ok=0, warn=2, fail=0, error=0, skipped=2

sonar -- WARN (project bernstein)

metric value delta threshold status
coverage_pct 13.5% new 80.0% fail
code_smells 151 new 50 warn
bugs 11 new 0 fail
vulnerabilities 2 new 0 warn
security_hotspots 91 new 0 fail

code-scanning -- WARN (33 open alert(s))

metric value delta threshold status
open_alerts 33 new 0 fail
critical_alerts 0 new 0 ok
high_alerts 2 new 0 warn
medium_alerts 0 new - ok
low_alerts 0 new - ok
Skipped backends (credentials not configured)
  • glitchtip: BERNSTEIN_GLITCHTIP_TOKEN not set
  • dt: DTRACK_URL/TOKEN/PROJECT not set

See docs/observability/unified-doctor.md for backend setup notes.

github-actions Bot and others added 2 commits May 21, 2026 21:05
Auto-applied by contract-drift-autofix.yml on PR #1814.
Regenerated via scripts/regen_contract_drift.py. Refs #1273.

Source CI run: https://github.com/sipyourdrink-ltd/bernstein/actions/runs/26253016689
The prior commit (contract-drift allow-list regen) was authored by
github-actions[bot], so GitHub suppressed the pull_request workflow
triggers and the required `CI gate` context never published, leaving
the PR BLOCKED with no failing check. This empty commit re-runs the
pull_request workflows so the gate can report.
@github-actions github-actions Bot added the tests label May 21, 2026
@chernistry chernistry merged commit 7d29d1a into main May 21, 2026
66 of 67 checks passed
@chernistry chernistry deleted the fix/code-scanning-cluster-a branch May 21, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant