Skip to content

fix(security): harden AI summary, docs listing, QuickSearch & feedback endpoints#330

Merged
iftakharul-islam merged 2 commits into
weDevsOfficial:developfrom
arifulhoque7:fix/security-hardening
Jul 16, 2026
Merged

fix(security): harden AI summary, docs listing, QuickSearch & feedback endpoints#330
iftakharul-islam merged 2 commits into
weDevsOfficial:developfrom
arifulhoque7:fix/security-hardening

Conversation

@arifulhoque7

@arifulhoque7 arifulhoque7 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Audit tracker: weDevsOfficial/wedocs-pro#349
Fixes (audit issues, tracked on the pro repo): weDevsOfficial/wedocs-pro#344, #345, #346, #347, #348


Summary

Security hardening for weDocs Free (2.3.1). One bundled PR covering the five findings from the manual audit (tracked in weDevsOfficial/wedocs-pro#349). All fixes are backward-compatible — the public "Generate AI Summary" button and QuickSearch keep working; only anonymous access to unpublished content, cost abuse, and unescaped output are closed.

Fixes

Finding Change File
Refs weDevsOfficial/wedocs-pro#344 — unauth AI-summary generate (cost abuse + draft leak) __return_truegenerate_summary_permissions_check (published-only for anon, edit_docs bypass) + per-IP rate limit + wp_kses_post on save includes/API/API.php
Refs weDevsOfficial/wedocs-pro#345 — unauth draft/pending disclosure wedocs_get_docs returns unpublished statuses only to edit_docs users includes/Ajax.php
Refs weDevsOfficial/wedocs-pro#346 — stored XSS via doc title in QuickSearch escape title before <mark> highlight src/blocks/QuickSearch/templates/search-results.php
Refs weDevsOfficial/wedocs-pro#347 — feedback meta on arbitrary post reject non-docs post IDs before update_post_meta includes/Ajax.php
Refs weDevsOfficial/wedocs-pro#348 — contributor user_login enumeration return display_name includes/API/API.php

Adds wedocs_rate_limit_ok() (per-IP transient limiter) in includes/functions.php.

Compatibility

Callers traced in src//assets/build/ first: the AI-summary button and QuickSearch are public-by-design and stay public (hardened with validation + rate-limit + escaping); get_docs/feedback are unchanged for legitimate use. No frontend build change required — assets/build/** is CI-generated from src/**.

Testing

php -l clean on all changed PHP. Manual: anon generate on a published doc still returns a summary; anon generate on a draft → 403; QuickSearch highlights an HTML-bearing title escaped.

Summary by CodeRabbit

  • Security & Access

    • Added permission checks and rate limits for AI summary generation.
    • Restricted documentation visibility based on editing permissions.
    • Sanitized generated summaries before saving.
    • Validated feedback submissions against documentation content.
  • Improvements

    • Contributor listings now show display names.
    • Quick Search highlights results safely without exposing unescaped content.

…k endpoints

- REST /docs/{id}/ai-summary/generate: replace __return_true with a
  permission callback (published-only for anonymous, edit_docs bypass) and
  rate-limit anonymous requests to curb AI cost abuse; wp_kses_post the
  stored summary to prevent stored XSS.
- AJAX wedocs_get_docs: return draft/pending docs only to users who can
  edit docs (was leaking unpublished content to anonymous callers).
- AJAX wedocs_ajax_feedback: reject non-docs post IDs before writing meta.
- REST /docs/contributors: return display_name instead of user_login
  (user enumeration).
- QuickSearch results template: escape the doc title before highlighting
  (stored XSS via title in nopriv AJAX HTML response).
- Add wedocs_rate_limit_ok() per-IP transient rate limiter.
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a9332233-c349-4851-8269-63b7dd598cd2

📥 Commits

Reviewing files that changed from the base of the PR and between 75a1525 and 9217888.

📒 Files selected for processing (3)
  • includes/API/API.php
  • includes/functions.php
  • src/blocks/QuickSearch/templates/search-results.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • includes/functions.php
  • includes/API/API.php

Walkthrough

The changes restrict AI summary and AJAX document access, add per-IP rate limiting, validate feedback targets, sanitize generated summaries, display contributor names, and safely highlight quick-search title matches.

Changes

Documentation security hardening

Layer / File(s) Summary
Per-IP rate-limit helper
includes/functions.php
Adds transient-based request limiting keyed by bucket and client IP.
AI summary authorization and storage
includes/API/API.php
Validates documents, capabilities, publication status, and request limits before summary generation; contributor names use display_name, and generated HTML is sanitized.
AJAX document and feedback validation
includes/Ajax.php
Restricts document statuses for non-editors and rejects feedback for non-doc posts.
Quick-search output escaping
src/blocks/QuickSearch/templates/search-results.php
Escapes title fragments before wrapping matched text in <mark> elements.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: QA Approved

Suggested reviewers: iftakharul-islam

Poem

I’m a rabbit guarding docs in the queue,
With rate limits hopping neatly through.
Drafts stay tucked, unsafe tags fade,
Search marks sparkle where matches are made.
Thump, thump—secure changes bloom!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main security hardening across AI summary, docs listing, QuickSearch, and feedback paths.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.2.2)

PHP Fatal error: Uncaught Error: Undefined constant "ABSPATH" in /includes/functions.php:423
Stack trace:
#0 /includes/functions.php(397): wedocs_is_plugin_active()
#1 /vendor/composer/autoload_real.php(39): require('...')
#2 /vendor/composer/autoload_real.php(43): {closure}()
#3 /vendor/autoload.php(25): ComposerAutoloaderInit4c924043604fa3fd576f5366632e9201::getLoader()
#4 phar:///usr/bin/phpstan/bin/phpstan(46): require_once('...')
#5 phar:///usr/bin/phpstan/bin/phpstan(107): _PHPStan_2874a496b{closure}()
#6 /usr/bin/phpstan(7): require('...')
#7 {main}
thrown in /includes/functions.php on line 423
Fatal error: Uncaught Error: Undefined constant "ABSPATH" in /includes/functions.php:423
Stack trace:
#0 /includes/functions.php(397): wedocs_is_plugin_active()
#1 /vendor/composer/autoload_real.php(39): require('...')
#2 /vendor/composer/autoload_real.php(43): {closure}()
#3 /vendor/autoload.php(25): ComposerAutoloaderInit4c924043604fa3fd576f5366632e9201::getLoader()
#4 phar:///usr/bin/phpstan/bin/phpstan(46): require_once('...')
#5 phar:///usr/bin/phpstan/bin/phpstan(107): _PHPStan_2874a496b{closure}()
#6 /usr/bin/phpstan(7): require('...')
#7 {main}
thrown in /includes/functions.php on line 423


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@includes/API/API.php`:
- Around line 808-813: Update the contributor loop around get_userdata to check
whether it returned a valid user before accessing display_name or calling
get_avatar_url. Skip deleted or otherwise missing contributor accounts, while
preserving the existing data mapping for valid users.

In `@includes/functions.php`:
- Around line 1201-1213: Update wedocs_rate_limit_ok to implement a fixed window
by storing the window start time alongside the request count, resetting both
only when the existing window has expired, and preserving the original expiry
rather than refreshing it on every allowed request. Also make the counter update
atomic or otherwise prevent concurrent requests from passing the same limit
check, while retaining the current key isolation and boolean return behavior.

In `@src/blocks/QuickSearch/templates/search-results.php`:
- Around line 44-55: Update the title handling around $title and
$highlighted_title to retain the raw document title for matching instead of
applying esc_html() before preg_replace(). Reconstruct the highlighted output
from raw matched and unmatched fragments, escaping each fragment before
inserting the trusted <mark> wrapper, and use context-specific escaping at
output boundaries such as the esc_attr() call. Preserve normal unhighlighted
output and support both array and scalar title values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 834a36b1-075e-4357-85af-489913b489df

📥 Commits

Reviewing files that changed from the base of the PR and between dd786d1 and 75a1525.

📒 Files selected for processing (4)
  • includes/API/API.php
  • includes/Ajax.php
  • includes/functions.php
  • src/blocks/QuickSearch/templates/search-results.php

Comment thread includes/API/API.php
Comment thread includes/functions.php
Comment thread src/blocks/QuickSearch/templates/search-results.php Outdated
- Guard get_userdata() against deleted contributor accounts (avoid fatal on ->display_name)
- Make wedocs_rate_limit_ok() a true fixed window; preserve original TTL instead of sliding it
- Highlight QuickSearch results against the raw title and escape each fragment (no entity-name matches, no double escaping)

@iftakharul-islam iftakharul-islam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Look fine

@iftakharul-islam
iftakharul-islam merged commit 63ccdfb into weDevsOfficial:develop Jul 16, 2026
1 check passed
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.

2 participants