mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 15:03:16 +00:00
Applies the plan's C5 step: git mv every test per the domain-reorg
move-map (movemap-m50.txt) into tests/{agent,analyzer,auth,dashboard,
github,middleware,models,reviewer,sandbox,slack,tools,webhooks}/, plus
the 13 fork-only placements from the scoping report §2c (Atlassian
webhook tests -> tests/webhooks/, test_atlassian_connect.py and
test_auth_error_leak.py -> tests/auth/, jira/confluence util tests ->
tests/tools/, test_repo_binding_isolation.py -> tests/sandbox/,
bot-identity/autofix tests -> tests/github/).
Path-only move: the only content edits are parents[1] -> parents[2]
fixes in test_e2b_integration.py and test_daytona_integration.py,
required because their __file__-relative ROOT path gained one more
directory level in the move.
Monkeypatch retargets for these files were already completed in C4;
none remained outstanding here.
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
"""Replay-window enforcement for Linear webhook signature verification (AUTHZ-001)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
from datetime import UTC, datetime
|
|
|
|
from agent.webhooks import common as webhook_common
|
|
|
|
_SECRET = "linear-signing-secret"
|
|
|
|
|
|
def _sign(body: bytes) -> str:
|
|
return hmac.new(_SECRET.encode("utf-8"), body, hashlib.sha256).hexdigest()
|
|
|
|
|
|
def _now_ms() -> int:
|
|
return int(datetime.now(UTC).timestamp() * 1000)
|
|
|
|
|
|
def test_fresh_timestamp_accepted() -> None:
|
|
body = json.dumps({"type": "Comment", "webhookTimestamp": _now_ms()}).encode()
|
|
assert webhook_common.verify_linear_signature(body, _sign(body), _SECRET) is True
|
|
|
|
|
|
def test_stale_timestamp_rejected() -> None:
|
|
stale = _now_ms() - 10 * 60 * 1000 # 10 minutes old
|
|
body = json.dumps({"type": "Comment", "webhookTimestamp": stale}).encode()
|
|
# Signature is valid, but the timestamp is outside the freshness window.
|
|
assert webhook_common.verify_linear_signature(body, _sign(body), _SECRET) is False
|
|
|
|
|
|
def test_future_timestamp_rejected() -> None:
|
|
future = _now_ms() + 10 * 60 * 1000
|
|
body = json.dumps({"type": "Comment", "webhookTimestamp": future}).encode()
|
|
assert webhook_common.verify_linear_signature(body, _sign(body), _SECRET) is False
|
|
|
|
|
|
def test_missing_timestamp_rejected() -> None:
|
|
body = json.dumps({"type": "Comment"}).encode()
|
|
assert webhook_common.verify_linear_signature(body, _sign(body), _SECRET) is False
|
|
|
|
|
|
def test_non_numeric_timestamp_rejected() -> None:
|
|
body = json.dumps({"type": "Comment", "webhookTimestamp": "not-a-number"}).encode()
|
|
assert webhook_common.verify_linear_signature(body, _sign(body), _SECRET) is False
|
|
|
|
|
|
def test_bad_signature_rejected_even_when_fresh() -> None:
|
|
body = json.dumps({"type": "Comment", "webhookTimestamp": _now_ms()}).encode()
|
|
assert webhook_common.verify_linear_signature(body, "deadbeef", _SECRET) is False
|