From be9827ce13863dc04b20db8bafcf5c2c0141ac95 Mon Sep 17 00:00:00 2001 From: peptiderepo <273788462+peptiderepo@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:51:22 +0700 Subject: [PATCH] fix(security): remove key-format prefix from API-key placeholder (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR Vision settings API-key input had placeholder="sk-or-…" which rendered the provider key-format prefix in admin HTML (tripping the not.toContain("sk-or-") smoke assertion). The real key value was never rendered (value="" write-only was correct). Placeholder is now neutral. Full-codebase audit: no other key-output path. v0.3.3. QA: APPROVE (2026-06-19-e4c51ee). Agent-Session: prv-key-leak-fix-20260619 --- CHANGELOG.md | 38 +++++++++++++++++++ .../core/class-prv-key-manager-renderer.php | 10 ++++- pr-vision.php | 4 +- tests/bootstrap.php | 2 +- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e87e48..4d46e4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,44 @@ Versioning: [Semantic Versioning](https://semver.org/) --- +## [0.3.3] — 2026-06-19 + +P1 security fix: write-only key invariant — remove key-format prefix from placeholder. + +### Fixed +- **[SECURITY] P1 — API-key input placeholder leaked provider prefix in rendered HTML.** + `class-prv-key-manager-renderer.php` rendered `placeholder="sk-or-…"` on the + password input. The placeholder contained the literal string `sk-or-` which (a) + exposes provider-format information to anyone who can view page source, and (b) + caused the `not.toContain('sk-or-')` smoke-suite assertion to fail (confirmed in + CI run on 2026-06-17, job 81824705096). The placeholder is now `Paste provider API + key` — neutral, no key-format prefix, no provider information. The `value=""` write- + only invariant was already correct; this fixes the remaining output surface. +- Added inline comment and docblock note: placeholder text must never contain a + key-format prefix — even a format hint in a placeholder violates the write-only + contract and trips smoke-suite security guards. + +### Audit +Full-codebase audit performed (2026-06-19): +- `class-prv-settings-renderer.php` — no key emission; clean. +- `class-prv-key-manager-renderer.php` — placeholder was the sole leak; now fixed. +- `class-prv-model-manager-table.php` — no key fields; clean. +- `class-prv-settings-controller.php` — processes POST input; never reflects back. +- `class-prv-key-store.php` — `get_key()` used server-side only; never echoed. +- `class-prv-crypto-helper.php` — encrypt/decrypt helper; no HTML output. +- `class-prv-key-test-ajax.php` — AJAX; returns valid/invalid message, never the key. +- `class-prv-model-test-ajax.php` — AJAX; returns model health, never the key. +- `class-prv-gateway-client.php` — HTTP client; key only in Authorization header. +- `class-prv-probe-run-executor.php` — `error_log()` only logs HTTP status, not the key. +- `class-prv-capture-writer.php` — allowlisted fields; no key fields. +- `class-prv-call-drawer-renderer.php` / `class-prv-call-log-*` — no key fields. +- All providers — `get_key()` only flows to HTTP Authorization header. +- No `wp_localize_script` calls anywhere in the plugin. +- No REST endpoints registered; no REST response surfaces. +- No data attributes containing key fields. + +--- + ## [0.3.2] — 2026-06-16 CI/CD adoption: PHPUnit test suite (WP stubs), estate reusable pipeline, diff --git a/includes/core/class-prv-key-manager-renderer.php b/includes/core/class-prv-key-manager-renderer.php index d95fc22..e7f6cd8 100644 --- a/includes/core/class-prv-key-manager-renderer.php +++ b/includes/core/class-prv-key-manager-renderer.php @@ -12,7 +12,10 @@ declare(strict_types=1); * * Outputs only the key SOURCE (constant / admin / not set) and the * last-run API status. The stored key value is NEVER included in any - * output: not in HTML, not in JS, not in data attributes. + * output: not in HTML, not in JS, not in data attributes. The password + * input placeholder must also never contain a key format prefix (e.g. + * 'sk-or-') — even a format hint in the placeholder leaks provider + * information and trips write-only security guards. * * Three states: * - SOURCE_CONSTANT: constant defined → lock the input, show note. @@ -120,10 +123,13 @@ class PRV_Key_Manager_Renderer { echo ''; // Input always renders empty — never outputs stored key. + // Placeholder must not include a key-format prefix (e.g. 'sk-or-'); + // even a format hint leaks provider information and will trip + // write-only security assertions in the smoke suite. echo ''; if ( $is_const ) { diff --git a/pr-vision.php b/pr-vision.php index 44a71d3..e17bf16 100644 --- a/pr-vision.php +++ b/pr-vision.php @@ -3,7 +3,7 @@ * Plugin Name: PR Vision * Plugin URI: https://peptiderepo.com * Description: Weekly server-side LLM probes for AI-visibility (GEO) tracking. Records whether peptiderepo.com is cited by LLMs across core peptides, stores time-series, and renders an admin dashboard + settings UI. - * Version: 0.3.2 + * Version: 0.3.3 * Author: peptiderepo * Author URI: https://peptiderepo.com * License: GPL-2.0-or-later @@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) { /* ── Constants ────────────────────────────────────────────────────────── */ -define( 'PRV_VERSION', '0.3.2' ); +define( 'PRV_VERSION', '0.3.3' ); define( 'PRV_PLUGIN_FILE', __FILE__ ); define( 'PRV_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'PRV_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 92987bc..f81aaba 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -32,7 +32,7 @@ if ( ! defined( 'ARRAY_N' ) ) { define( 'ARRAY_N', 'ARRAY_N' ); } -define( 'PRV_VERSION', '0.3.2' ); +define( 'PRV_VERSION', '0.3.3' ); define( 'PRV_PLUGIN_FILE', __DIR__ . '/../pr-vision.php' ); define( 'PRV_PLUGIN_DIR', realpath( __DIR__ . '/..' ) . '/' ); define( 'PRV_PLUGIN_URL', 'http://example.test/wp-content/plugins/pr-vision/' );