Skip to content

Commit 1ce4aeb

Browse files
rolandpgclaude
andauthored
fix(audit): batch 1 — M-1 bare-except + M-3 timezone_offset + C-2 spec drift + typedb doc (#100)
* fix: audit batch 1 — M-1 bare-except + M-3 timezone_offset + C-2 spec drift + typedb doc Closes 4 findings from tasks/compliance-audit-2026-04-25.md, all small / mechanical / independent. M-1 — memory_store.py:351 bare `except:` → `except Exception:` Direct GOV-003 §"Error Handling" violation: bare except catches KeyboardInterrupt/SystemExit. The re-raise on line 354 partially compensated, but the pattern itself is what GOV-003 forbids and what Ruff `E722` (already enabled) would flag the moment H-1 lands. Comment added explaining the GOV-003 reference for future readers. M-3 — OCSF events now emit `metadata.timezone_offset: 0` GOV-012 §"Required OCSF Base Fields" lists timezone_offset as a mandatory base attribute. Our events were correct on `time` (always ISO8601 UTC) but missed the explicit offset, forcing OCSF-strict downstream pipelines (Sentinel custom-log tables) to insert an ingest-time transform. Constant `0` since we always emit UTC. C-2 — `no_hardcoded_secrets` fabricated control claim removed governance/controls.yaml:45-47 declared GovernanceValidator .validate_operation as the runtime enforcement point for no_hardcoded_secrets. Reading governance_validator.py:31-49 — the method has zero secret-scanning logic. This is the highest-severity audit-risk pattern: a control listed as 'enforced via runtime method X' where X is a no-op. Removed the fabricated claim with an inline comment documenting honest state (GitGuardian + Snyk in CI today; ruff S rules pending H-1; runtime detector tracked as follow-up). test_governance_spec_drift.py still passes — its "every runtime rule references a method or test" check is satisfied by the remaining input_validation rule. typedb doc — `export TYPEDB_USERNAME=admin` → `<your-typedb-username>` Carry-over from PR #82 (TypeDB auth hardening) that missed the merge by 64s. The example block in configure-typedb.md was the last place the old hardcoded `admin` default still appeared in user-facing docs. All four touch independent files. 18/18 governance + logging tests pass locally. ruff check + ruff format --check clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: enforce --cov-fail-under=67 (audit H-2) GOV-007 §'Coverage Requirements' mandates ≥80% line / ≥70% branch 'enforced in CI'. Today's CI runs pytest --cov but does NOT pass --cov-fail-under, so any coverage including 0% is silently accepted. controls.yaml:35 already claims --cov-fail-under=67 as the enforced value; this PR makes the claim true. 67% chosen as the starting ratchet because it matches the existing governance/controls.yaml declaration and is verified to pass on master today. Issue #51 tracks raising it toward 80% across v2.5.x. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f36409e commit 1ce4aeb

5 files changed

Lines changed: 24 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ jobs:
5959
ZETTELFORGE_BACKEND: sqlite
6060
ZETTELFORGE_EMBEDDING_PROVIDER: fastembed
6161
run: |
62-
pytest tests/ -v --cov=zettelforge --cov-report=xml --cov-report=term-missing
62+
# GOV-007 §"Coverage Requirements" mandates ≥80% line / ≥70% branch.
63+
# We start the ratchet at 67 (matches governance/controls.yaml's
64+
# current declaration) so today's pipeline does not break, and #51
65+
# tracks raising it toward 80% across v2.5.x. Audit finding H-2.
66+
pytest tests/ -v --cov=zettelforge --cov-report=xml --cov-report=term-missing --cov-fail-under=67
6367
6468
- name: Upload coverage
6569
if: matrix.python-version == '3.12'

docs/how-to/configure-typedb.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ backend: typedb
118118
export TYPEDB_HOST=localhost
119119
export TYPEDB_PORT=1729
120120
export TYPEDB_DATABASE=zettelforge
121-
export TYPEDB_USERNAME=admin
121+
export TYPEDB_USERNAME=<your-typedb-username>
122122
export TYPEDB_PASSWORD=<your-password>
123123
export ZETTELFORGE_BACKEND=typedb
124124
```

governance/controls.yaml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,15 @@ controls:
4242
- id: input_validation
4343
description: "Content must be str or have .content attribute"
4444
runtime_method: "GovernanceValidator.validate_operation"
45-
- id: no_hardcoded_secrets
46-
description: "Content must not contain API keys, tokens, or credentials"
47-
runtime_method: "GovernanceValidator.validate_operation"
45+
# The 2026-04-25 compliance audit (C-2) found that a previously-declared
46+
# `no_hardcoded_secrets` rule pointed at GovernanceValidator.validate_operation
47+
# as its runtime_method, but that method contains no secret-detection
48+
# logic. Honest state: NOT IMPLEMENTED at runtime today. Static
49+
# enforcement is provided by GitGuardian (CI) and (once GOV-003-mandated
50+
# `S` rules are restored to ruff config — audit H-1) Bandit S105/S106/S108.
51+
# Runtime detector (regex + entropy + detect-secrets) is tracked as
52+
# follow-up work; the rule will be re-declared here when implemented.
53+
# Removed rather than left fabricated — see tasks/compliance-audit-2026-04-25.md.
4854

4955
GOV-012:
5056
name: Audit Logging

src/zettelforge/memory_store.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,10 @@ def _rewrite_note(self, note: MemoryNote) -> None:
348348
# Update cache
349349
if self._note_cache is not None:
350350
self._note_cache[note.id] = note
351-
except:
351+
except Exception:
352+
# GOV-003: never bare-except. Catches all real write
353+
# failures while letting KeyboardInterrupt / SystemExit
354+
# propagate normally.
352355
if os.path.exists(tmp_path):
353356
os.unlink(tmp_path)
354357
raise

src/zettelforge/ocsf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ def _base_fields(
101101
"status_id": status_id,
102102
"status": "Success" if status_id == STATUS_SUCCESS else "Failure",
103103
"time": datetime.now(timezone.utc).isoformat(),
104+
# GOV-012 §"Required OCSF Base Fields" lists timezone_offset alongside
105+
# `time`. We always emit UTC, so this is constant; surfacing it lets
106+
# OCSF-strict downstream validators (Sentinel custom-log tables) accept
107+
# the events without an ingest-time transform.
108+
"timezone_offset": 0,
104109
"metadata": {
105110
"version": "1.3.0",
106111
"product": {

0 commit comments

Comments
 (0)