Resolve wallet security review issues - #3
Conversation
Reviewer's GuideThis PR decouples wallet and security services through shared primitives and an injected response interface, enforces timezone-aware recovery locks, replaces device-string trust heuristics with explicit connection metadata, adds security-event review support, centralizes configuration with compatibility aliases, and fixes Flask application formatting. Sequence diagram for recovery-lock enforcementsequenceDiagram
participant Client
participant PiWalletManager
participant Wallet
participant PiSecurityEngine
Client->>PiWalletManager: authenticate(username, entered_passphrase)
PiWalletManager->>Wallet: read recovery_locked_until
alt recovery lock is active
PiWalletManager-->>Client: false
else lock expired or absent
PiWalletManager->>PiWalletManager: SecurityUtils.hash_passphrase(entered_passphrase)
alt revoked passphrase detected
PiWalletManager->>PiSecurityEngine: trigger_response(wallet)
PiSecurityEngine->>Wallet: set recovery_locked_until
PiWalletManager-->>Client: false
else active passphrase
PiWalletManager-->>Client: true
end
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="pishield/backend/app.py" line_range="19" />
<code_context>
-
+CORS(app)
+
security_engine = SecurityEngine()
FRONTEND_DIR = Path(__file__).resolve().parents[1] / "frontend"
</code_context>
<issue_to_address>
**issue (broader_impact):** The Flask application instantiates `SecurityEngine`, not `PiSecurityEngine`, and never wires a `PiWalletManager` with a recovery response handler. Consequently, revoked-passphrase authentication through the new wallet manager uses the default `None` handler and returns `False` without setting `recovery_locked_until`, so the new recovery-lock enforcement is not active in the application path.
**Triggers:** When authentication is performed through the application’s configured security components.
**Suggested fix:** Instantiate `PiSecurityEngine` and inject it into the wallet manager, or make the application’s existing `SecurityEngine` implement the response-handler contract and connect it to wallet authentication.
</issue_to_address>
### Comment 2
<location path="pishield/backend/wallet_manager.py" line_range="42" />
<code_context>
+
+ # A recovery lock applies to every authentication attempt, including a
+ # revoked passphrase, until its timezone-aware UTC expiry has passed.
+ if wallet.recovery_locked_until and wallet.recovery_locked_until > utc_now():
+ return False
+
</code_context>
<issue_to_address>
**issue (bug_risk):** The comparison raises `TypeError` when a wallet contains a naive `recovery_locked_until`, because `utc_now()` is timezone-aware. The `Wallet` dataclass accepts naive datetimes without validation, so callers constructing or restoring wallets with legacy naive timestamps cannot authenticate at all.
**Triggers:** When a wallet is created or restored with a naive recovery-lock datetime.
**Suggested fix:** Normalize incoming datetimes to UTC or reject naive values at the `Wallet` boundary before comparing them.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 2 findings to address first, and the change adds wallet authentication and recovery-lock behavior, including passphrase hashing and a 24-hour lock triggered by revoked credentials. A defect could deny legitimate wallet access or create an authentication weakness; the state is currently in memory and a restart or rollback clears it, but any incorrect access decision can affect users while deployed.
Blocking findings: pishield/backend/app.py:19, pishield/backend/wallet_manager.py:42
Motivation
PiOSConfigsurface while preserving legacy module-level aliases for backwards compatibility.Description
pishield/backend/security_shared.pyprovidingWallet,SecurityUtils.hash_passphrase,utc_now(), andConnectionMetadataso wallet and security services share types without importing each other.pishield/backend/wallet_manager.pyto use the sharedWallettype, callSecurityUtils.hash_passphrase, enforcerecovery_locked_until > utc_now()(short-circuiting authentication when locked), and accept an injectedRecoveryResponseHandlerinstead of importing the engine.pishield/backend/security_engine.pyto introducePiTrustAnalyzerthat consumesConnectionMetadata(explicituses_vpn/uses_torflags andip_address), useipaddress.ip_address(...).is_privatefor private-network heuristics, addSecurityEventandsecurity_events_db, and implementPiSecurityEngine.trigger_responseto set a timezone-aware UTCrecovery_locked_untilusingPiOSConfig.RECOVERY_LOCK_HOURS.pishield/backend/config.pyby addingPiOSConfigand providing clear legacy aliases (PI_APP_URL,PI_APP_NAME, etc.) for Flask compatibility.pishield/backend/app.pyindentation and formatting so imports and endpoints import/format correctly and the Flask test client runs.Testing
python -m compileall -q pishield(succeeded).created_at, lock enforcement behavior, revoked-passphrase handling, andPiTrustAnalyzer.calculate_risk_scorebehavior (all assertions passed)./health,/api/config, and invalidrotate-passphraseinput) and checks passed.pytestwhich completed but reported "no tests ran" (no collected tests).git diff --check(no whitespace/merge errors reported).Codex Task
Summary by Sourcery
Strengthen the sandbox wallet security architecture with shared security primitives, enforced UTC recovery locks, explicit connection metadata, centralized configuration, and expanded validation.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests:
Chores: