Skip to content

Conversation

@mike182uk
Copy link
Member

@mike182uk mike182uk commented Jan 8, 2026

Validated postId as an ObjectID before using it in database queries. This ensures we are not passing arbitrary SQL strings and limits the postId variable to a string format we expect.

  • Extracted postId parsing to separate getPostIdFromFilter method
  • Used ObjectID ValueObject for type safety and validation
  • Added unit tests for the extraction method

Credit:

Sho Odagiri
GMO Cybersecurity by Ierae, Inc.


Note

Strengthens handling of data.post_id and secures the aggregated click events query.

  • Extracts getPostIdFromFilter to parse/validate data.post_id with bson-objectid, returning an ObjectID or null
  • Updates getAggregatedClickEvents to use the validated ObjectID (toHexString()) in raw SQL and removes data.post_id from NQL filters to avoid unsafe interpolation
  • Adds unit tests covering valid/invalid IDs, $and extraction, injection attempts, and missing filters

Written by Cursor Bugbot for commit 0518876. This will update automatically on new commits. Configure here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

A new helper method getPostIdFromFilter(filter) was added to the EventRepository class to extract and validate post_id values from filter objects, converting them to ObjectID instances. The existing getAggregatedClickEvents method was refactored to use this helper instead of handling post_id extraction directly. The postClicksQuery construction was updated to call .toHexString() on the ObjectID for proper hex formatting. Corresponding unit tests were added covering valid inputs, nested filter structures, invalid ObjectIDs, SQL injection attempts, and null/undefined cases.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly references fixing a SQL injection vulnerability in click event queries, which matches the main security-focused objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description accurately describes the changeset, explaining the SQL injection vulnerability fix, the new getPostIdFromFilter method, ObjectID validation, and added unit tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link
Contributor

@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

🧹 Nitpick comments (1)
ghost/core/core/server/services/members/members-api/repositories/EventRepository.js (1)

489-505: Excellent security fix! Consider adding JSDoc documentation.

The validation logic correctly prevents SQL injection by ensuring only valid ObjectIDs are used in queries. The implementation handles both direct and $and filter structures appropriately.

📝 Suggested improvements
  1. Add JSDoc documentation since this is now part of the public API:
+   /**
+    * Extracts and validates a post_id from a filter object.
+    * @param {Object} filter - Filter object that may contain data.post_id
+    * @returns {ObjectID|null} Valid ObjectID instance or null if invalid/missing
+    */
    getPostIdFromFilter(filter) {
  1. Minor: The empty string initialization on line 490 is unnecessary:
-       let postIdString = '';
+       let postIdString;
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c500b62 and ecb2d8f.

📒 Files selected for processing (2)
  • ghost/core/core/server/services/members/members-api/repositories/EventRepository.js
  • ghost/core/test/unit/server/services/members/members-api/repositories/event-repository.test.js
🧰 Additional context used
🧬 Code graph analysis (1)
ghost/core/test/unit/server/services/members/members-api/repositories/event-repository.test.js (1)
ghost/core/core/server/services/members/members-api/repositories/EventRepository.js (1)
  • require (4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Ghost-CLI tests
  • GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
  • GitHub Check: Lint
  • GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
  • GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
  • GitHub Check: Unit tests (Node 22.18.0)
  • GitHub Check: Legacy tests (Node 22.18.0, mysql8)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build & Push Docker Image
🔇 Additional comments (6)
ghost/core/core/server/services/members/members-api/repositories/EventRepository.js (3)

510-528: LGTM! SQL injection vulnerability successfully mitigated.

The refactoring correctly validates postId before using it in the SQL query. Using toHexString() on the validated ObjectID ensures only safe hex strings are interpolated into the query, effectively preventing SQL injection.

While parameterized queries would be ideal, the current approach with validated ObjectID hex strings is secure and doesn't require extensive refactoring of the query construction logic.


5-5: All API methods are compatible with bson-objectid v2.0.4.

The import statement and the three methods used (ObjectID.isValid(), ObjectID.createFromHexString(), and .toHexString()) are all available and correctly implemented in the bson-objectid v2.0.4 release. No changes needed.


489-607: SQL injection vulnerability at line 528 is isolated; no other similar patterns found in the codebase.

Verification confirms that the postId.toHexString() interpolation at line 528 is the only instance where user-controlled input is directly embedded in raw SQL within the members-api repositories. No other methods construct raw SQL queries with similar interpolation patterns.

ghost/core/test/unit/server/services/members/members-api/repositories/event-repository.test.js (3)

1-1: LGTM! Good use of strict assertions.

The addition of node:assert/strict provides stricter equality checks, which is appropriate for testing security-sensitive validation logic. The test suite setup follows the existing pattern in the file.

Also applies to: 119-132


134-151: Excellent test coverage for valid inputs.

These tests verify both direct and nested filter structures, ensuring the method correctly extracts and validates ObjectIDs in common use cases. The assertions confirm the ObjectID is properly constructed and can be safely serialized.


153-169: Outstanding security test coverage! 🔒

These tests directly validate the SQL injection fix by attempting a classic SQL injection payload ('; DROP TABLE posts; --) and confirming it's rejected. The comprehensive coverage of invalid inputs (malformed ObjectIDs, SQL injection, null/undefined/empty) ensures the validation logic is robust and secure.

The test on lines 159-163 is especially critical as it demonstrates the vulnerability is properly mitigated.

Validated postId as an ObjectID before using it in database queries.
This ensures we are not passing arbitrary SQL strings and limits the
postId variable to a string format we expect.

- Extracted postId parsing to separate getPostIdFromFilter method
- Used ObjectID ValueObject for type safety and validation
- Added unit tests for the extraction method

Credit:

Sho Odagiri
GMO Cybersecurity by Ierae, Inc.
@mike182uk mike182uk force-pushed the fix-sql-injection-in-click-query branch from ecb2d8f to 0518876 Compare January 8, 2026 10:30
@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

React E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 20813768094 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

1 similar comment
@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

React E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 20813768094 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

betschki added a commit to magicpages/Ghost that referenced this pull request Jan 8, 2026
- Added indexnow_api_key to dynamicDefault in settings model
- Removed getOrCreateApiKey logic from indexnow service
- Added 'coming-soon' to default post slugs to skip
- Separated database migration to PR TryGhost#25804
@kevinansfield kevinansfield merged commit f2165f9 into main Jan 8, 2026
73 of 74 checks passed
@kevinansfield kevinansfield deleted the fix-sql-injection-in-click-query branch January 8, 2026 11:29
kevinansfield pushed a commit that referenced this pull request Jan 8, 2026
Validated postId as an ObjectID before using it in database queries. This ensures we are not passing arbitrary SQL strings and limits the postId variable to a string format we expect.

- Extracted postId parsing to separate getPostIdFromFilter method
- Used ObjectID ValueObject for type safety and validation
- Added unit tests for the extraction method

Credit:

Sho Odagiri
GMO Cybersecurity by Ierae, Inc.
betschki added a commit to magicpages/Ghost that referenced this pull request Jan 8, 2026
- Added indexnow_api_key to dynamicDefault in settings model
- Removed getOrCreateApiKey logic from indexnow service
- Added 'coming-soon' to default post slugs to skip
- Separated database migration to PR TryGhost#25804
kevinansfield pushed a commit that referenced this pull request Jan 8, 2026
Validated postId as an ObjectID before using it in database queries. This ensures we are not passing arbitrary SQL strings and limits the postId variable to a string format we expect.

- Extracted postId parsing to separate getPostIdFromFilter method
- Used ObjectID ValueObject for type safety and validation
- Added unit tests for the extraction method

Credit:

Sho Odagiri
GMO Cybersecurity by Ierae, Inc.
betschki added a commit to magicpages/Ghost that referenced this pull request Jan 8, 2026
- Added indexnow_api_key to dynamicDefault in settings model
- Removed getOrCreateApiKey logic from indexnow service
- Added 'coming-soon' to default post slugs to skip
- Separated database migration to PR TryGhost#25804
betschki added a commit to magicpages/Ghost that referenced this pull request Jan 8, 2026
- Added indexnow_api_key to dynamicDefault in settings model
- Removed getOrCreateApiKey logic from indexnow service
- Added 'coming-soon' to default post slugs to skip
- Separated database migration to PR TryGhost#25804
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.

4 participants