You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix: audit-log failed confirmed-destructive calls; warn on wrapper collisions
- Saddle_Approval::gate now logs a confirmed destructive execution even when
the executor returns WP_Error — a partial mutation with no audit trail is
worse than a noisy one. The entry carries a "FAILED after confirmation"
marker plus the error message.
- Saddle_Integrations::wrap surfaces genuine wrapper-name collisions via
_doing_it_wrong instead of silently dropping the partner tool; its own
wrappers are tracked so idempotent re-runs stay silent.
Part of Phase 1 (correctness) of the 2026-07 architecture review
(plugpressco/saddle-pro#39).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133CZhoFPY6BBChDDGEa22Q
* feat: security hardening — token user-binding, credential marker, split log caps, domain enforcement
- Approval tokens are bound to the previewing user: with several agents on
one site, agent A's preview can no longer be confirmed by agent B
(saddle_token_user_mismatch).
- Saddle-issued app passwords are recorded by UUID in user meta
(saddle_issued_credentials); credential scoping, the XML-RPC block, and
client revoke/rotate/list key on that immutable marker instead of the
user-editable display name. Legacy prefix-named keys migrate on sight.
- Saddle_Log GC caps denials (300) and executed mutations (1000) as
separate filterable buckets, batch raised to 500, so denial noise can
never evict change history.
- Opt-in domain-drift enforcement (saddle_enforce_tier_domain, default
off): write/admin abilities refuse on a migrated/cloned domain until the
owner re-confirms the tier; exposed via GET/POST /settings.
Phase 2 of the 2026-07 architecture review (#63).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133CZhoFPY6BBChDDGEa22Q
* style: Yoda condition on the token user-binding check
phpcs flagged it after the merge; WPCS wants the call on the left.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GkZr73cqaSesHRDG89Yy8S
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@@ -1051,17 +1062,20 @@ public static function revoke_client( WP_REST_Request $request ) {
1051
1062
returnnewWP_Error( 'saddle_app_passwords_unavailable', __( 'Application Passwords are not available on this site.', 'saddle' ), array( 'status' => 500 ) );
1052
1063
}
1053
1064
1054
-
// Only allow revoking a Saddle-prefixed password, so this endpoint can't
1055
-
// be used to delete unrelated credentials.
1065
+
// Only allow revoking a Saddle-issued password, so this endpoint can't
1066
+
// be used to delete unrelated credentials. Checked via the immutable
1067
+
// marker (with legacy name-prefix fallback), so a renamed key can
'message' => __( 'This site\'s domain changed since write access was granted, and the owner has domain enforcement on — write tools are refused until they re-confirm the access level (Saddle → Permissions). Do not retry; tell the user.', 'saddle' ),
$summary = sprintf( __( 'Blocked: the tool "%s" is turned off.', 'saddle' ), $tool );
517
540
break;
541
+
case'domain':
542
+
/* translators: %s: tool name. */
543
+
$summary = sprintf( __( 'Blocked: the site domain changed since write access was granted — "%s" is refused until the access level is re-confirmed.', 'saddle' ), $tool );
544
+
break;
518
545
case'tier':
519
546
default:
520
547
/* translators: %s: tool name. */
@@ -552,6 +579,25 @@ public static function set_paused( $paused ) {
0 commit comments