Skip to content

🐛 Fixed security action history entries#29012

Open
9larsons wants to merge 1 commit into
mainfrom
fix-28222-security-action-history
Open

🐛 Fixed security action history entries#29012
9larsons wants to merge 1 commit into
mainfrom
fix-28222-security-action-history

Conversation

@9larsons

@9larsons 9larsons commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

ref #28222

Summary

  • Allows the actions API to eager-load include=resource for security_action audit rows without throwing Bookshelf's unknown polymorphic type error
  • Keeps the existing security_action audit category intact so existing installs are fixed without a data migration
  • Formats the History title as "Security action reset authentication" and renders reset counts instead of (unknown)
  • Tightens History grouping so null-resource actions only group when resource type and edited action name also match
  • Adds e2e/API/frontend regression coverage for the failing include=actor,resource path and the History display/grouping helpers

Review follow-up

Incorporated review-agent findings for the History : (unknown) fallback and null-resource grouping edge case before internal review.

Testing

  • pnpm --dir ghost/core test:single test/e2e-api/admin/actions.test.js
  • pnpm exec eslint core/server/models/action.js test/e2e-api/admin/actions.test.js from ghost/core
  • pnpm --filter @tryghost/admin-x-framework test:unit -- test/unit/api/actions.test.ts
  • pnpm --filter @tryghost/admin-x-framework lint:code
  • pnpm --filter @tryghost/admin-x-framework lint:test
  • pnpm --filter @tryghost/admin-x-framework test:types
  • pnpm --filter @tryghost/admin-x-settings lint
  • pnpm nx run @tryghost/admin-x-framework:build
  • pnpm --filter @tryghost/admin-x-settings test:acceptance -- test/acceptance/advanced/history.test.ts

@nx-cloud

nx-cloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit e42dcda

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 9m 56s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗
nx run ghost:test:ci:integration ✅ Succeeded 2m 28s View ↗
nx run-many -t test:unit -p @tryghost/admin-x-f... ✅ Succeeded 7m 10s View ↗
nx run ghost:test:integration ✅ Succeeded 2m 47s View ↗
nx run @tryghost/admin:build ✅ Succeeded 3m 52s View ↗
nx run ghost:test:legacy ✅ Succeeded 2m 58s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 26s View ↗
Additional runs (5) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-01 14:29:55 UTC

@9larsons 9larsons requested a review from rob-ghost July 1, 2026 14:07
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.38%. Comparing base (dc7bc33) to head (e42dcda).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #29012   +/-   ##
=======================================
  Coverage   74.37%   74.38%           
=======================================
  Files        1565     1565           
  Lines      135730   135749   +19     
  Branches    16501    16509    +8     
=======================================
+ Hits       100949   100972   +23     
+ Misses      33785    33749   -36     
- Partials      996     1028   +32     
Flag Coverage Δ
admin-tests 55.25% <ø> (-0.03%) ⬇️
e2e-tests 76.52% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ref #28222

Reset all authentication records audit entries with resource_type=security_action. Those entries are not backed by a Bookshelf model, so including resource in the actions API caused History to fail while eager-loading the polymorphic relation.

This keeps the audit resource type intact, teaches the resource relation to accept it, and formats the History label without snake_case.
@9larsons 9larsons force-pushed the fix-28222-security-action-history branch from f52548c to e42dcda Compare July 1, 2026 14:17
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds support for a new security_action resource type across backend and frontend. The Action model gains a resourceCandidates() method resolving security_action to User. The admin-x-framework Action type allows resource_id to be null, and a new actionsAreGroupable helper centralizes grouping logic used by useBrowseActions. getActionTitle now labels security_action/reset_authentication. The history modal renders a custom summary for reset-authentication security actions. Unit, acceptance, and e2e tests were added/updated accordingly.

Possibly related PRs

  • TryGhost/Ghost#28888: Both PRs modify getActionTitle normalization logic in the same file, adding new label handling for different resource types.

Suggested reviewers: jonatansberg, kevinansfield

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main fix for security action history entries.
Description check ✅ Passed The description clearly matches the changes, covering the API fix, history formatting, grouping logic, and tests.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-28222-security-action-history

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.

🧹 Nitpick comments (2)
ghost/core/core/server/models/action.js (1)

13-22: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Document/enforce the resource_id === null invariant for security_action.

Mapping security_action to User only avoids the "unknown polymorphic type" error safely because resource_id is always null for this type — morphTo groups by stored type and queries whereIn id [...ids], which yields no match on null, so resource correctly resolves as absent. But if a future code path ever writes a non-null resource_id for security_action, this mapping would silently fetch and expose an unrelated User record as resource.

Consider adding a comment explaining this constraint, since nothing in the model enforces it.

📝 Suggested comment
     resourceCandidates() {
         const candidates = this.candidates();
 
         const User = ghostBookshelf.registry.models.User;
         if (User) {
+            // NOTE: 'security_action' rows always have a null resource_id.
+            // This candidate exists only so morphTo doesn't throw an
+            // "unknown polymorphic type" error; it must never resolve to
+            // an actual User record.
             candidates.push([User, 'security_action']);
         }
 
         return candidates;
     },
🤖 Prompt for 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.

In `@ghost/core/core/server/models/action.js` around lines 13 - 22, Add an inline
comment in action.js around resourceCandidates() explaining that mapping
security_action to User is only safe because resource_id must always remain null
for this type, and that morphTo would otherwise resolve an unrelated User record
if a non-null resource_id were ever persisted. Reference resourceCandidates(),
candidates(), and the security_action/User mapping so the invariant is clear for
future maintainers.
apps/admin-x-framework/test/unit/api/actions.test.ts (1)

24-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test doesn't isolate null resource_id equality.

This case returns false purely due to the resource_type mismatch (security_action vs setting); it doesn't actually exercise the null === null resource_id comparison. Consider adding a positive case where two actions share resource_id: null, same resource_type, event, and action_name to confirm they do group.

🤖 Prompt for 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.

In `@apps/admin-x-framework/test/unit/api/actions.test.ts` around lines 24 - 29,
The current test in actionsAreGroupable only proves different resource_type
values do not group, so it never validates null resource_id handling. Update the
unit coverage in actions.test.ts by adding a positive case for actions with
resource_id: null, matching resource_type, event, and context.action_name so the
actionsAreGroupable behavior for null === null is explicitly exercised.
🤖 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.

Nitpick comments:
In `@apps/admin-x-framework/test/unit/api/actions.test.ts`:
- Around line 24-29: The current test in actionsAreGroupable only proves
different resource_type values do not group, so it never validates null
resource_id handling. Update the unit coverage in actions.test.ts by adding a
positive case for actions with resource_id: null, matching resource_type, event,
and context.action_name so the actionsAreGroupable behavior for null === null is
explicitly exercised.

In `@ghost/core/core/server/models/action.js`:
- Around line 13-22: Add an inline comment in action.js around
resourceCandidates() explaining that mapping security_action to User is only
safe because resource_id must always remain null for this type, and that morphTo
would otherwise resolve an unrelated User record if a non-null resource_id were
ever persisted. Reference resourceCandidates(), candidates(), and the
security_action/User mapping so the invariant is clear for future maintainers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2da01201-66f0-4e6a-8c00-059d77ebd6c7

📥 Commits

Reviewing files that changed from the base of the PR and between f7a92b1 and e42dcda.

📒 Files selected for processing (6)
  • apps/admin-x-framework/src/api/actions.ts
  • apps/admin-x-framework/test/unit/api/actions.test.ts
  • apps/admin-x-settings/src/components/settings/advanced/history-modal.tsx
  • apps/admin-x-settings/test/acceptance/advanced/history.test.ts
  • ghost/core/core/server/models/action.js
  • ghost/core/test/e2e-api/admin/actions.test.js

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