Skip to content

Conversation

@Arukuen
Copy link
Contributor

@Arukuen Arukuen commented Dec 6, 2025

fixes #7

Summary by CodeRabbit

  • New Features

    • Editor now shows permission-based restriction indicators and contextual notices for restricted interaction properties.
  • Bug Fixes

    • Enhanced validation and sanitization for interaction data to prevent invalid or unsafe values; safer saving and cache invalidation after updates.
  • Documentation

    • README expanded with clearer branding, links, examples, installation and upgrade guidance.
  • Chores

    • Plugin/package version bumped to 1.3.2; improved license activation/deactivation UI and build behavior for premium packages.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 6, 2025

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several changes appear out of scope: VSCode settings modification, plugin version bumps, readme updates, package.json version change, package.js build behavior, and freemius.php license modal refactoring are not directly related to extending security for unfiltered_html to premium actions. Remove or separate unrelated changes: version bumps, documentation updates, VSCode settings, and freemius modal refactoring should be in separate PRs focused on their specific objectives.
Docstring Coverage ⚠️ Warning Docstring coverage is 26.19% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'fix: add guard before sanitizing' is vague and generic, using non-descriptive terms that don't convey the meaningful change of extending security controls to premium actions. Consider revising the title to something more specific like 'fix: extend security for unfiltered html to premium actions' to better reflect the primary objective.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements comprehensive security controls extending unfiltered_html capability restrictions to premium action types through sanitization methods and capability checks [#7].
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/7-extend-security

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Arukuen Arukuen changed the title Extend security fix: add guard before sanitizing Dec 6, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/freemius.php (1)

604-676: License modal JS refactor looks correct; consider dropping debug logs.

The new IIFE-based dialog handling, busy-state toggling, and AJAX flows for activate/deactivate are well-structured and safely use textContent for server messages. Only minor nit: the console.log('result', result); calls in both handlers can be removed once debugging is done to avoid noisy consoles in production.

src/action-types/class-action-type-update-attribute.php (2)

28-35: Align restricted notice wording with actual capability check

The notice mentions “administrator with unfiltered_html capability”, but the enforcement uses current_user_can( 'unfiltered_html' ), which may apply to non‑admin roles (custom roles, multisite super admins, etc.). I’d suggest wording this around the capability itself to avoid confusing users and translators.

Example tweak:

- 'Some attribute names and values are disallowed unless you are an administrator with unfiltered_html capability for security reasons.'
+ 'Some attribute names and values are disallowed unless you have the unfiltered_html capability (typically administrator-level users) for security reasons.'

62-87: Dangerous-attribute detection looks good; consider minor refinements

The helper covers the key high‑risk cases (event handlers and URL/FORM‑related attributes) and normalizes casing, which is good. Two small refinements you may want to consider:

  • Anchor the regex with $ to avoid over‑matching attributes that merely start with on but aren’t event handlers (e.g., once): '/^on[a-z0-9]+$/'.
  • Re‑evaluate whether style should be included in $dangerous_attributes given the separate CSS sanitization; if you keep it commented out, a short comment clarifying that sanitize_style_value() is expected to mitigate style‑based payloads would help future maintainers.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f199ce and f3ea079.

📒 Files selected for processing (25)
  • .vscode/settings.json (1 hunks)
  • interactions.php (3 hunks)
  • package.json (1 hunks)
  • readme.txt (4 hunks)
  • scripts/package.js (1 hunks)
  • src/action-types/abstract-action-type.php (1 hunks)
  • src/action-types/class-action-type-background-color.php (1 hunks)
  • src/action-types/class-action-type-background-image.php (1 hunks)
  • src/action-types/class-action-type-css-rule.php (1 hunks)
  • src/action-types/class-action-type-display.php (1 hunks)
  • src/action-types/class-action-type-move.php (1 hunks)
  • src/action-types/class-action-type-opacity.php (1 hunks)
  • src/action-types/class-action-type-redirect-to-url.php (1 hunks)
  • src/action-types/class-action-type-rotate.php (1 hunks)
  • src/action-types/class-action-type-scale.php (1 hunks)
  • src/action-types/class-action-type-skew.php (1 hunks)
  • src/action-types/class-action-type-text-color.php (1 hunks)
  • src/action-types/class-action-type-toggle-class.php (1 hunks)
  • src/action-types/class-action-type-toggle-video.php (1 hunks)
  • src/action-types/class-action-type-update-attribute.php (2 hunks)
  • src/action-types/class-action-type-visibility.php (1 hunks)
  • src/class-interaction.php (3 hunks)
  • src/editor/components/timeline/property-control.js (2 hunks)
  • src/editor/editor.php (1 hunks)
  • src/freemius.php (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (15)
src/action-types/class-action-type-toggle-class.php (15)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-move.php (15)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-rotate.php (12)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (2)
  • sanitize_data_for_saving (268-271)
  • sanitize_style_value (279-290)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-background-color.php (15)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (2)
  • sanitize_data_for_saving (268-271)
  • sanitize_style_value (279-290)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-visibility.php (3)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-opacity.php (8)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-skew.php (2)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-update-attribute.php (8)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (2)
  • sanitize_data_for_saving (268-271)
  • sanitize_style_value (279-290)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-css-rule.php (6)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/abstract-action-type.php (2)
  • sanitize_data_for_saving (268-271)
  • sanitize_style_value (279-290)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-display.php (3)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-redirect-to-url.php (2)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/abstract-action-type.php (15)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-text-color.php (2)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/abstract-action-type.php (2)
  • sanitize_data_for_saving (268-271)
  • sanitize_style_value (279-290)
src/class-interaction.php (16)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-toggle-video.php (15)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/abstract-action-type.php (1)
  • sanitize_data_for_saving (268-271)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-96)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (89-133)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
🔇 Additional comments (20)
package.json (1)

3-3: Version bump to 1.3.2 looks consistent.

Matches interactions.php and readme.txt; no other behavioral changes here.

readme.txt (1)

1-8: Readme versioning and 1.3.2 notes align with code changes.

The updated stable tag (1.3.2), WP version fields, and the 1.3.2 changelog entries about unfiltered_html restrictions and extra sanitization correctly reflect the implementation changes elsewhere in the plugin.

Also applies to: 152-156

interactions.php (1)

10-11: Version bump and activation version-tracking logic look correct.

Updating INTERACT_VERSION to 1.3.2 and calling update_option( 'interact_plugin_version', INTERACT_VERSION ) unconditionally on activation ensures the stored version reliably matches the running plugin, while migrations still only run when upgrading ($saved_version < INTERACT_VERSION). This is a clean, predictable activation path.

Also applies to: 21-22, 31-36

src/editor/editor.php (1)

65-80: Exposing currentUserCanUnfilteredHtml to the editor is appropriate.

Using current_user_can( 'unfiltered_html' ) here and passing it through the existing localized config cleanly enables the UI to reflect capability-based restrictions without changing server-side checks. The flag is properly consumed in the editor to gate restricted notices in src/editor/components/timeline/property-control.js.

src/action-types/class-action-type-css-rule.php (1)

44-49: CSS Rule value sanitization is good; the current approach via sanitize_style_value() is appropriate.

The new sanitize_data_for_saving() correctly runs the value through sanitize_style_value(), which removes expression(...) and javascript: patterns, bringing this action in line with other style-related actions and preventing common XSS vectors.

The suggestion to additionally apply safecss_filter_attr() is not recommended. That function is designed specifically for inline style="" attributes and uses a property allowlist that may be overly restrictive for CSS rule values in custom contexts. The current sanitization strategy is sufficient and appropriate for this use case.

scripts/package.js (1)

315-323: Rename logic is safe but assumption about premium expectations is unverified.

The conditional rename from interactions.php to plugin.php for premium builds is guarded properly with fs.existsSync() and won't cause runtime errors. However, the justification that "the premium main plugin file and Freemius config expect plugin.php as the entry point" cannot be verified since the pro__premium_only directory and premium code are not present in this repository. Confirm that the premium distribution actually expects plugin.php as the main entry point.

src/action-types/class-action-type-skew.php (1)

50-66: Numeric sanitization for skew x/y looks correct

The method safely coerces x/y to numeric values and nulls out non‑numeric input, matching the scale pattern and preventing unsafe transform values from slipping through.

src/action-types/class-action-type-background-image.php (1)

37-42: Background image value is correctly sanitized via shared helper

Using sanitize_style_value() on the image field centralizes stripping of expression()/javascript: patterns and keeps the implementation consistent with other style-related action types.

src/action-types/class-action-type-visibility.php (1)

44-51: Visibility sanitizer safely whitelists allowed values

Restricting visibility to toggle/hide/show and defaulting to toggle on invalid input is a good, simple guard that matches the configured options.

src/action-types/class-action-type-display.php (1)

59-80: Display value whitelist aligns with configured options

The sanitizer’s allowed values list matches the select options and safely normalizes unexpected input to 'block', which is a sensible default for this action type.

src/action-types/class-action-type-scale.php (1)

50-66: Scale x/y sanitization is consistent and safe

Coercing x/y to numeric values and nulling non‑numeric input is appropriate here and keeps scale data in a clean, predictable shape.

src/action-types/class-action-type-opacity.php (1)

40-49: Opacity sanitizer correctly normalizes numeric input

The method enforces that opacity is numeric (or null), which is enough to prevent injection via crafted strings while keeping existing range enforcement in the UI.

src/action-types/class-action-type-background-color.php (1)

56-61: Background color is sanitized using shared style helper

Reusing sanitize_style_value() for the color field keeps background color in line with other style sanitization and helps strip dangerous patterns from any unexpected input.

src/action-types/class-action-type-text-color.php (1)

37-43: LGTM!

The sanitization method correctly follows the established pattern from other action types (e.g., background-color), properly checking for array structure and sanitizing the color field using the parent's sanitize_style_value() method.

src/action-types/class-action-type-redirect-to-url.php (1)

40-50: LGTM!

The URL sanitization using esc_url() is the appropriate WordPress function for this purpose. The type check and null fallback for non-string values is correctly implemented.

src/action-types/class-action-type-toggle-video.php (1)

57-73: LGTM!

The sanitization correctly validates the mode field against the allowed values and properly coerces startTime to a numeric value, consistent with the pattern used in other action types.

src/action-types/class-action-type-move.php (1)

61-75: LGTM!

The implementation efficiently handles all three coordinate fields using a loop, correctly preserving blank values (as noted in the property defaults) while sanitizing numeric inputs. Good use of the comment to clarify the intentional blank preservation.

src/class-interaction.php (2)

92-108: LGTM - Well-structured recursive sanitization.

The implementation correctly:

  • Short-circuits for users with unfiltered_html capability
  • Recursively processes nested arrays
  • Applies wp_kses_post() to strings for safe HTML filtering

The logic order ensures arrays are fully traversed before any string check, preventing unintended behavior.


118-147: Good integration of per-action-type sanitization with proper error propagation.

The flow correctly applies generic sanitization first via sanitize_interaction_value(), then delegates to action-type-specific sanitization via sanitize_data_for_saving(). The early return on WP_Error ensures validation failures (like from update-attribute with dangerous attributes) properly abort the save operation.

src/action-types/abstract-action-type.php (1)

279-290: Current implementation appropriately targets historical attack vectors; additional patterns are not recommended by modern CSS injection best practices.

The sanitize_style_value() method correctly removes expression() and javascript: patterns, which address the primary legacy IE-specific injection vectors. Modern CSS injection mitigation (2025) prioritizes Content-Security-Policy (CSP) with strict style-src controls and removal of style sinks over pattern-based blocking. The suggested additions (-moz-binding, behavior, @import) follow an outdated approach:

  • -moz-binding: Mozilla removed XBL support from Firefox; blocking it is unnecessary in modern browsers.
  • behavior: IE-only property, obsolete in modern contexts.
  • @import: Standard CSS at-rule controlled via CSP, not typically blocked by sanitizers; pattern blocking is not recommended practice.

The current implementation is solid for backward-compatibility scenarios. For modern security, rely on CSP enforcement and sanitizing user-controlled style sinks rather than expanding regex-based pattern exclusions.

Comment on lines +43 to 46
},
"search.exclude": {
"**/pro__premium_only": false
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

search.exclude value looks inverted for the described behavior.

In VS Code, "search.exclude": { "**/pro__premium_only": false } explicitly includes that folder in search. If your intention is to hide pro__premium_only from search results, this needs to be true instead:

-	"search.exclude": {
-		"**/pro__premium_only": false
-	}
+	"search.exclude": {
+		"**/pro__premium_only": true
+	}

If the goal is indeed to keep it searchable, the current setting is fine.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
},
"search.exclude": {
"**/pro__premium_only": false
}
},
"search.exclude": {
"**/pro__premium_only": true
}
🤖 Prompt for AI Agents
.vscode/settings.json lines 43-46: the search.exclude entry currently sets
"**/pro__premium_only": false which explicitly includes that folder in search;
if the intent is to hide/exclude pro__premium_only from searches change the
value to true so VS Code will exclude it; if the intent is to keep it
searchable, leave as-is.

Comment on lines +89 to +133
public function sanitize_data_for_saving( $value ) {
// Sanitize action value: ensure $value is an array and attribute/value are strings.
if ( ! is_array( $value ) ) {
return new WP_Error(
'invalid_structure',
__( 'Value must be an array containing attribute and value keys.', 'interactions' )
);
}

// Sanitize attribute name
if ( isset( $value['attribute'] ) && is_string( $value['attribute'] ) ) {
$value['attribute'] = sanitize_key( $value['attribute'] );
}

// Sanitize value if present, and convert to string.
if ( isset( $value['value'] ) ) {
$value['value'] = $this->sanitize_style_value( $value['value'] );
}

// Sanitize action field for select option.
if ( isset( $value['action'] ) ) {
$allowed_actions = [ 'update', 'remove', 'toggle' ];
if ( ! in_array( $value['action'], $allowed_actions, true ) ) {
$value['action'] = 'update';
}
}

if ( current_user_can( 'unfiltered_html' ) ) {
return $value;
}

if ( ! empty( $value['attribute'] ) && $this->is_dangerous_attribute( $value['attribute'] ) ) {
// Only allow dangerous attributes if user has unfiltered_html capability
return new WP_Error(
'invalid_attribute',
sprintf(
// Translators: %s is the attribute name.
__( 'The attribute "%s" requires administrator privileges with unfiltered_html capability to prevent security vulnerabilities.', 'interactions' ),
esc_html( $value['attribute'] )
)
);
}

return $value;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find all usages of sanitize_data_for_saving() method
rg -n "sanitize_data_for_saving" -C 3

Repository: gambitph/Interactions

Length of output: 10402


🏁 Script executed:

# Find other action-type files to compare implementation patterns
fd "class-action-type-" src/action-types/

Repository: gambitph/Interactions

Length of output: 905


🏁 Script executed:

# Check where the sanitize_data_for_saving result is used in the current file
cat -n src/action-types/class-action-type-update-attribute.php | head -150

Repository: gambitph/Interactions

Length of output: 5592


🏁 Script executed:

# Search more comprehensively for all calls to sanitize_data_for_saving
rg "sanitize_data_for_saving" -n --type php

Repository: gambitph/Interactions

Length of output: 1893


🏁 Script executed:

# Check what happens in the call site when WP_Error is returned
cat -n src/class-interaction.php | sed -n '120,145p'

Repository: gambitph/Interactions

Length of output: 1226


🏁 Script executed:

# Verify if required keys being missing causes actual issues by looking at how the value is used
rg "is_dangerous_attribute\|sanitize_data_for_saving" -A 5 -B 5 src/class-interaction.php

Repository: gambitph/Interactions

Length of output: 47


Structure validation message doesn't match actual checks, and 'remove' action should bypass dangerous attribute restrictions

The method handles WP_Error returns correctly (the call site in src/class-interaction.php:131 checks is_wp_error() before persisting), so that aspect is sound. However, two items merit refinement:

  1. Error message misleads about required keys.
    The 'invalid_structure' message claims the array must contain "attribute and value keys", but the code only checks is_array(). Missing keys don't cause errors; they're only validated with isset() afterward. Relax the message to say "Value must be an array" to match the actual validation logic.

  2. Allow 'remove' action on dangerous attributes for all users.
    Removing a dangerous attribute (e.g., onclick, href) poses no security risk and could even reduce it. The current logic blocks all operations on dangerous attributes unless the user has unfiltered_html, but remove should be allowed regardless:

     if ( ! empty( $value['attribute'] ) && $this->is_dangerous_attribute( $value['attribute'] ) ) {
  •   // Only allow dangerous attributes if user has unfiltered_html capability
    
  •   // Allow non-privileged users to remove dangerous attributes, but not add/update/toggle them.
    
  •   if ( isset( $value['action'] ) && 'remove' === $value['action'] ) {
    
  •       return $value;
    
  •   }
    
  •   // Only allow other operations on dangerous attributes if user has unfiltered_html capability.
      return new WP_Error(
          'invalid_attribute',
          sprintf(
              __( 'The attribute "%s" requires administrator privileges with unfiltered_html capability to prevent security vulnerabilities.', 'interactions' ),
              esc_html( $value['attribute'] )
          )
      );
    
    }
    
    
🤖 Prompt for AI Agents
In src/action-types/class-action-type-update-attribute.php around lines 89 to
133, update the structure validation message and allow the 'remove' action to
bypass dangerous-attribute restriction: change the WP_Error message for
'invalid_structure' to state only "Value must be an array." (remove the
"containing attribute and value keys" wording) and modify the
dangerous-attribute check so that it only returns WP_Error when the attribute is
dangerous AND the action is not 'remove' (i.e., if action === 'remove' do not
block even for users without unfiltered_html; continue to block for other
actions unless current_user_can('unfiltered_html')).

bfintal@gmail.com and others added 2 commits December 16, 2025 12:19
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3ea079 and f23d9f5.

📒 Files selected for processing (3)
  • src/action-types/class-action-type-rotate.php (1 hunks)
  • src/action-types/class-action-type-toggle-class.php (1 hunks)
  • src/editor/components/timeline/property-control.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/action-types/class-action-type-rotate.php
  • src/action-types/class-action-type-toggle-class.php
🔇 Additional comments (2)
src/editor/components/timeline/property-control.js (2)

13-13: LGTM! Imports support the new restriction notice feature.

The new imports are necessary for displaying the restriction notice tooltip and checking the user's unfiltered HTML capability.

Also applies to: 21-21


175-175: LGTM! Fix properly preserves computed help content.

The change from if ( property.help ) to if ( help ) correctly preserves the dynamically computed help content (including the restriction notice added above) when rendering the id-type help message. This addresses the concern raised in the previous review.

Comment on lines +53 to +74
// Add a restriction notice.
if ( property.restrictedNotice && ! currentUserCanUnfilteredHtml ) {
help = (
<>
{ help }{ ' ' }
<Tooltip
delay={ 0 }
text={ <span style={ { maxWidth: 300, display: 'block' } }>{ property.restrictedNotice.replace( /<[^>]+>/g, '' ) }</span> }
>
<span
style={ {
color: '#FFA500',
borderBottom: '1px dotted rgb(208, 135, 0)',
cursor: 'help',
} }
>
{ __( 'Has restrictions', 'interactions' ) }
</span>
</Tooltip>
</>
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add type guard before calling .replace() on restrictedNotice.

The code calls .replace() directly on property.restrictedNotice without verifying it's a string. If restrictedNotice is defined but not a string (e.g., an object, number, or array), this will throw a runtime error.

Apply this diff to add a type check:

-	if ( property.restrictedNotice && ! currentUserCanUnfilteredHtml ) {
+	if ( typeof property.restrictedNotice === 'string' && property.restrictedNotice && ! currentUserCanUnfilteredHtml ) {
 		help = (
 			<>
 				{ help }{ ' ' }
 				<Tooltip
 					delay={ 0 }
-					text={ <span style={ { maxWidth: 300, display: 'block' } }>{ property.restrictedNotice.replace( /<[^>]+>/g, '' ) }</span> }
+					text={ <span style={ { maxWidth: 300, display: 'block' } }>{ property.restrictedNotice.replace( /<[^>]+>/g, '' ) }</span> }
 				>

Additional observations:

  • The HTML tag stripping is appropriate for security, though HTML entities (like &lt; or &amp;) won't be decoded and will display as-is in the tooltip. This is acceptable for developer-defined restriction messages.
  • The hardcoded inline styles ('#FFA500', 'rgb(208, 135, 0)', 300) could be extracted to constants or CSS classes for better maintainability.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Add a restriction notice.
if ( property.restrictedNotice && ! currentUserCanUnfilteredHtml ) {
help = (
<>
{ help }{ ' ' }
<Tooltip
delay={ 0 }
text={ <span style={ { maxWidth: 300, display: 'block' } }>{ property.restrictedNotice.replace( /<[^>]+>/g, '' ) }</span> }
>
<span
style={ {
color: '#FFA500',
borderBottom: '1px dotted rgb(208, 135, 0)',
cursor: 'help',
} }
>
{ __( 'Has restrictions', 'interactions' ) }
</span>
</Tooltip>
</>
)
}
// Add a restriction notice.
if ( typeof property.restrictedNotice === 'string' && property.restrictedNotice && ! currentUserCanUnfilteredHtml ) {
help = (
<>
{ help }{ ' ' }
<Tooltip
delay={ 0 }
text={ <span style={ { maxWidth: 300, display: 'block' } }>{ property.restrictedNotice.replace( /<[^>]+>/g, '' ) }</span> }
>
<span
style={ {
color: '#FFA500',
borderBottom: '1px dotted rgb(208, 135, 0)',
cursor: 'help',
} }
>
{ __( 'Has restrictions', 'interactions' ) }
</span>
</Tooltip>
</>
)
}
🤖 Prompt for AI Agents
In src/editor/components/timeline/property-control.js around lines 53-74, the
code calls property.restrictedNotice.replace(...) without ensuring
restrictedNotice is a string; add a type guard (e.g., check typeof
property.restrictedNotice === 'string') before calling .replace and fall back to
an empty string (or String(property.restrictedNotice) sanitized) if it isn't,
then use that safe value in the Tooltip text; keep the existing regex for
stripping tags.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/action-types/class-action-type-update-attribute.php (1)

93-103: Allow 'remove' action on dangerous attributes for all users.

The past review comment still applies: removing a dangerous attribute (e.g., onclick, href) poses no security risk and could even improve it. The current logic blocks all operations on dangerous attributes, but remove should be allowed regardless of unfiltered_html capability.

Suggested fix:
 if ( ! empty( $value['attribute'] ) && $this->is_dangerous_attribute( $value['attribute'] ) ) {
-    // Only allow dangerous attributes if user has unfiltered_html capability
+    // Allow non-privileged users to remove dangerous attributes, but not add/update/toggle them.
+    if ( isset( $value['action'] ) && 'remove' === $value['action'] ) {
+        return $value;
+    }
+
+    // Only allow other operations on dangerous attributes if user has unfiltered_html capability.
     return new WP_Error(
🧹 Nitpick comments (2)
src/action-types/abstract-action-type.php (2)

343-354: Consider adding srcdoc and data to the dangerous attributes list.

The current list covers common vectors, but these additional attributes can execute scripts:

  • srcdoc - allows embedding full HTML documents in iframes
  • data - used by <object> elements to load external content
Suggested addition:
 $dangerous_attributes = [
     'href',
     'src',
+    'srcdoc',
+    'data',
     'action',
     'formaction',
     'form',
     'formmethod',
     'formtarget',
 ];

399-405: Broaden the data: protocol check.

The current check data:\s*text\/ is too narrow. Dangerous payloads can use variations like data:text/html, data:application/javascript, or base64-encoded variants. Consider blocking all data: URIs or at minimum expanding the pattern.

Suggested fix:
-if ( preg_match( '/javascript:\s*/i', $html ) || preg_match( '/data:\s*text\//i', $html ) ) {
+if ( preg_match( '/javascript:\s*/i', $html ) || preg_match( '/data:\s*/i', $html ) || preg_match( '/vbscript:\s*/i', $html ) ) {
     return new WP_Error(
         'invalid_protocol',
-        __( 'The HTML contains disallowed protocols (javascript: or data:).', 'interactions' )
+        __( 'The HTML contains disallowed protocols (javascript:, data:, or vbscript:).', 'interactions' )
     );
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f23d9f5 and 0a1aead.

📒 Files selected for processing (2)
  • src/action-types/abstract-action-type.php (1 hunks)
  • src/action-types/class-action-type-update-attribute.php (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/action-types/abstract-action-type.php (15)
src/action-types/class-action-type-update-attribute.php (1)
  • sanitize_data_for_saving (62-106)
src/action-types/class-action-type-rotate.php (1)
  • sanitize_data_for_saving (67-98)
src/action-types/class-action-type-background-color.php (1)
  • sanitize_data_for_saving (56-61)
src/action-types/class-action-type-toggle-class.php (1)
  • sanitize_data_for_saving (54-67)
src/action-types/class-action-type-background-image.php (1)
  • sanitize_data_for_saving (37-42)
src/action-types/class-action-type-css-rule.php (1)
  • sanitize_data_for_saving (44-49)
src/action-types/class-action-type-display.php (1)
  • sanitize_data_for_saving (59-80)
src/action-types/class-action-type-move.php (1)
  • sanitize_data_for_saving (62-75)
src/action-types/class-action-type-opacity.php (1)
  • sanitize_data_for_saving (40-49)
src/action-types/class-action-type-redirect-to-url.php (1)
  • sanitize_data_for_saving (41-50)
src/action-types/class-action-type-scale.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-text-color.php (1)
  • sanitize_data_for_saving (38-43)
src/action-types/class-action-type-skew.php (1)
  • sanitize_data_for_saving (50-66)
src/action-types/class-action-type-visibility.php (1)
  • sanitize_data_for_saving (44-52)
src/action-types/class-action-type-toggle-video.php (1)
  • sanitize_data_for_saving (58-73)
🔇 Additional comments (6)
src/action-types/class-action-type-update-attribute.php (2)

33-33: LGTM!

The restrictedNotice provides good UX by informing users about security restrictions upfront in the editor.


62-106: Approve the sanitization implementation with a minor note.

The sanitization flow is well-structured: array validation, field-level sanitization, allowlist for action types, and capability-gated dangerous attribute restrictions. The implementation correctly returns WP_Error for security violations, which is handled by the caller.

src/action-types/abstract-action-type.php (4)

268-271: LGTM!

Good template method pattern allowing child classes to override with specific sanitization logic while providing a safe default.


279-290: LGTM with optional enhancement.

The recursive regex for expression(...) handles nested parentheses well. Consider also stripping vbscript: for completeness, though it's primarily a legacy IE concern.


363-408: Note on regex-based HTML validation.

Regex-based HTML parsing can potentially be bypassed with malformed markup or encoding tricks. This is a known limitation, but given that:

  1. This serves as defense-in-depth behind the unfiltered_html capability check
  2. WordPress's wp_kses provides additional sanitization at output time

The approach is reasonable for this use case. For higher-risk scenarios, consider using DOMDocument for more robust parsing.


299-321: LGTM!

Comprehensive list of dangerous tags covering script execution, external content embedding, and page behavior modification vectors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend security for unfiltered html cap to premium actions

3 participants