Add PRV_Key_Store (resolver), PRV_Crypto_Helper (libsodium/OpenSSL), PRV_Key_Manager_Renderer (write-only Settings card), PRV_Key_Test_Ajax. Key resolver: PRV_OPENROUTER_API_KEY constant > prv_provider_key_enc option > none. All probe + test paths read key through PRV_Key_Store::get_key(). Security: - Ciphertext only in prv_provider_key_enc (hex nonce:hex ciphertext). - Enc key from SHA-256(AUTH_KEY+SECURE_AUTH_KEY) — never stored. - Plaintext never in options, logs, exceptions, page source, REST, or JS. - manage_options + nonce on set/remove/test. wp-config precedence enforced. - write-only UI: password input always renders empty; status text only. - uninstall.php prv_ wildcard DELETE covers prv_provider_key_enc. Files: 4 new (all ≤300 lines), 14 modified. Tests: test-key-store.php (encrypt/decrypt round-trip, wrong-key fails silently, resolver precedence, write-only render, Remove, constant wins). ARCHITECTURE/CONVENTIONS/CONTEXT/ CHANGELOG all updated in this commit. Agent-Session: 2026-06-15-pr-vision-v0.2.3-api-key-ui
6.1 KiB
6.1 KiB
Changelog — PR Vision
All notable changes to this project will be documented in this file.
Format: Keep a Changelog Versioning: Semantic Versioning
[0.2.3] — 2026-06-15
UI-editable provider API key: encrypted at rest, write-only, wp-config precedence.
Added
PRV_Key_Store(includes/core/class-prv-key-store.php): single key resolver with precedence chain —PRV_OPENROUTER_API_KEYconstant → encrypted admin optionprv_provider_key_enc→ none.get_key()is the sole key-access point for all probe and test paths.PRV_Crypto_Helper(includes/core/class-prv-crypto-helper.php): low-level symmetric encryption/decryption. Prefers libsodiumsodium_crypto_secretbox(XSalsa20-Poly1305); falls back to OpenSSL AES-256-GCM. Encryption key is derived from WP salts (AUTH_KEY+SECURE_AUTH_KEY) via SHA-256 — environment-specific, never stored. Storage format:hex(nonce):hex(ciphertext).PRV_Key_Manager_Renderer(includes/core/class-prv-key-manager-renderer.php): renders the write-only "Provider API Key" card in Settings. Shows source status only ("Set via wp-config (takes precedence)" / "Set via admin" / "Not set") plus last-run health. Password input always renders empty. Constant defined → input disabled with note.PRV_Key_Test_Ajax(includes/core/class-prv-key-test-ajax.php): AJAX handler for the "Test key" button. Resolves key viaPRV_Key_Store::get_key(), runs one cheap probe, reports valid/invalid witharia-liveresult; key never in response.- Key set/remove POST actions in
PRV_Settings_Controller:handle_key_set()(encrypts + stores; empty input = no-op) andhandle_key_remove(). prv_key_set,prv_key_remove,prv_key_errornotices inPRV_Settings_Renderer.wp_ajax_prv_test_keyhook registered inPRV_Settings_Page→ dispatches toPRV_Key_Test_Ajax.- Tests (
tests/unit/test-key-store.php): encrypt↔decrypt round-trip, wrong-key fails silently, resolver precedence (constant > option > none), write-only render emits no key, Remove clears option, constant takes precedence.
Changed
PRV_OpenRouter_Provider: removed directPRV_OPENROUTER_API_KEYconstant reads;probe()now callsPRV_Key_Store::get_key(). Newprobe_with_key(string $q, string $key)method for the test path (PRV_Key_Test_Ajax injects the resolved key).is_configured()now checksPRV_Key_Store::get_key() !== ''.PRV_Perplexity_Provider: same key-resolver migration —probe()callsPRV_Key_Store::get_key().is_configured()checks the store. Direct constant reads removed.PRV_Model_Test_Ajax: resolves key viaPRV_Key_Store::get_key()instead of checking the constant directly. Returns a specific "key not configured — set via Settings" message when no key is available.PRV_Settings_Renderer: replaced read-onlyrender_api_key_status()with a call toPRV_Key_Manager_Renderer::render(). Added key-action notices. Provider API Key card is now separate from the Probe Configuration form.PRV_Settings_Page: addedNONCE_KEY+NONCE_KEY_TESTconstants; registeredadmin_post_prv_key_set,admin_post_prv_key_remove,wp_ajax_prv_test_key.uninstall.php: updated comment to explicitly listprv_provider_key_encas a v0.2.3 addition purged by theprv_wildcard DELETE.tests/unit/test-uninstall.php: seeds + assertsprv_provider_key_encpurge.
Security
- Plaintext API key never stored in any option, transient, log, or echoed to the browser.
- The encrypted option (
prv_provider_key_enc) is ciphertext only; decryption is server-side at probe time only. The encryption key is derived from WP salts — not stored. manage_options+ nonce required for all key actions (set, remove, test).wp-configconstant always takes precedence; admin path is only active when absent.
[0.2.2] — 2026-06-14
Kill white WP admin gutters around the dark "Assay" UI — CSS only, no logic change.
Fixed
- Admin chrome (dashboard + settings):
#wpcontent,#wpbody, and#wpbody-contentnow receive the dark page background (#14181C) on both PR Vision screens. WP's default20pxleft padding on#wpcontentis removed so no light gutter appears between the left menu and the dark content area. - WP admin bar / first-element gap:
#wpbodydark fill removes the white strip between the admin bar and the first card. - Page
<h1>header:.wrap > h1colour set to the light text token (#EEF2F5) so the dashboard heading renders on dark, not white. - Proxy-note banner (dashboard):
.notice.prv-proxy-noteoverridden to dark surface (#1C2228) with teal left border — banner now sits on dark bg matching the surrounding cards. - Scope: entire CSS block delivered via
wp_add_inline_style('wp-admin', …)insidePRV_Admin_Page::enqueue_assets(), which is already guarded bystrpos($hook, 'pr-vision'). Styles are absent on every other WP admin page. WP admin bar and left menu are not touched.
[0.1.1] — 2026-06-13
Rename to PR Vision; fix www-strip in competitor display; PHPCS gating; remove dead cache.
Changed
- Rename: plugin, repo, slug, class prefix, option/hook/table prefix all updated from
pgm_/PGM_toprv_/PRV_and frompeptide-geo-monitor/GEO Monitortopr-vision/PR Vision. No data migration required (nopgm_*data existed).
Fixed
- P2-A:
ltrim($host, 'www.')character-mask bug inPRV_Citation_Detector::parse_domains()replaced withstr_starts_with($host, 'www.') ? substr($host, 4) : $host. Domains starting withw(e.g.wikipedia.org,webmd.com) are no longer corrupted in competitor standings. - P2-C: Removed dead in-run cache from
PRV_Probe_Runner::run(). Thepeptides × intents × modelsloop visits each(slug, intent, model)triplet exactly once, so the cache could never produce a hit on a single traversal.
CI
- P2-B: Dropped
continue-on-error: truefrom the PHPCS step — WordPress Coding Standards now gates the build. PHPCS was already green on v0.1.0; this enforces the DoD for future commits.