Skip to content

Added curated private site access codes#28029

Merged
ErisDS merged 2 commits into
mainfrom
add-private-site-word-access-code
May 21, 2026
Merged

Added curated private site access codes#28029
ErisDS merged 2 commits into
mainfrom
add-private-site-word-access-code

Conversation

@ErisDS
Copy link
Copy Markdown
Member

@ErisDS ErisDS commented May 21, 2026

Summary

  • Replaces the temporary fake-### private-site access-code placeholder with the Product-approved curated word### format.
  • Generates codes server-side with crypto.randomInt, choosing from the 48-word curated list plus a zero-padded three-digit suffix.
  • Keeps the regenerate settings flow read-only from the API caller; this PR only changes the generated format and matching tests.

Testing

  • source ~/.nvm/nvm.sh && nvm use 22 && pnpm test:single test/unit/server/services/settings/private-site-access-code.test.js
  • source ~/.nvm/nvm.sh && nvm use 22 && pnpm test:single test/unit/server/services/settings/settings-service.test.js
  • source ~/.nvm/nvm.sh && nvm use 22 && pnpm --filter @tryghost/parse-email-address install --offline && pnpm --filter @tryghost/parse-email-address build && pnpm test:single test/e2e-api/admin/settings.test.js
  • source ~/.nvm/nvm.sh && nvm use 22 && pnpm exec eslint core/server/services/settings/private-site-access-code.js test/unit/server/services/settings/private-site-access-code.test.js test/unit/server/services/settings/settings-service.test.js test/e2e-api/admin/settings.test.js
  • git diff --check

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Review Change Stack

Walkthrough

This PR updates the private-site access code generator to replace a placeholder format with a curated word-based format. The generator now selects a random word from a fixed 48-word list and appends a zero-padded 3-digit number, returning codes in word### format. The implementation exports both the generator function and the word list constant. All unit tests, integration tests, and e2e tests are updated to validate and expect the new format throughout the codebase.

Possibly related PRs

  • TryGhost/Ghost#27920: Introduces the original fake-### placeholder format in the access code generator and related test assertions.
  • TryGhost/Ghost#27988: Adds a password regeneration endpoint that depends on the access code generation flow and format.
  • TryGhost/Ghost#27917: Implements publicSiteAccess enforcement logic that relies on password generation and regeneration behavior.

Suggested labels

preview

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: replacing temporary placeholder codes with curated word-based access codes in the expected word### format.
Description check ✅ Passed The description clearly explains the changes: replacing fake-### format with word### format, generation method, API behavior preservation, and comprehensive testing verification.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ 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 add-private-site-word-access-code

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

Copy link
Copy Markdown
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: 1

🤖 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
`@ghost/core/test/unit/server/services/settings/private-site-access-code.test.js`:
- Around line 15-18: Restore a strict content assertion for the curated word
list in the test "uses a 48-word lowercase list with no duplicate entries":
after the existing shape checks for ACCESS_CODE_WORDS, add a strict
equality/deep-equality assertion (e.g. assert.deepStrictEqual or snapshot)
comparing ACCESS_CODE_WORDS to the exact 48-word array so any accidental
substitution is detected; reference the ACCESS_CODE_WORDS constant and the test
case name when making the change.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6a289c00-9911-4f24-b4f6-457d694e6683

📥 Commits

Reviewing files that changed from the base of the PR and between 130658e and e75abd0.

📒 Files selected for processing (1)
  • ghost/core/test/unit/server/services/settings/private-site-access-code.test.js

Comment on lines +15 to +18
it('uses a 48-word lowercase list with no duplicate entries', function () {
assert.equal(ACCESS_CODE_WORDS.length, 48);
assert.equal(new Set(ACCESS_CODE_WORDS).size, ACCESS_CODE_WORDS.length);
assert.ok(ACCESS_CODE_WORDS.every(word => /^[a-z]+$/.test(word)));
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 21, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reinstate an exact-content assertion for the curated word list.

At Line 15–18, the test now validates only shape (count/uniqueness/lowercase), which weakens protection for the Product-approved curated set. This will miss accidental word substitutions as long as they still match the regex and count. Please add back a strict equality/snapshot check for the exact 48 words.

🤖 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/test/unit/server/services/settings/private-site-access-code.test.js`
around lines 15 - 18, Restore a strict content assertion for the curated word
list in the test "uses a 48-word lowercase list with no duplicate entries":
after the existing shape checks for ACCESS_CODE_WORDS, add a strict
equality/deep-equality assertion (e.g. assert.deepStrictEqual or snapshot)
comparing ACCESS_CODE_WORDS to the exact 48-word array so any accidental
substitution is detected; reference the ACCESS_CODE_WORDS constant and the test
case name when making the change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

What does this protect against? Duplicating the list in tests seems overkill

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.

@ErisDS — That's a fair point, and I think you're right. The only scenario my suggestion would catch is someone swapping one curated word for another while keeping the count at 48 and all words lowercase — but that's an intentional edit to the source file, not an accidental regression. Duplicating the full array in the test is effectively copying a production constant, which adds maintenance burden without meaningfully improving safety. The existing shape-and-membership checks (format regex, word-is-in-list, length, uniqueness, lowercase) give good behavioral coverage. Happy to dismiss this one.

ᓚᘏᗢ


🧠 Learnings used
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25791
File: ghost/core/core/server/api/endpoints/member-comment-ban.js:64-68
Timestamp: 2026-01-08T10:26:38.700Z
Learning: In the Ghost API, endpoints rely on the serialization layer to prepare frame.data[docName] as a non-empty array before query() executes. Endpoints access frame.data[docName][0] directly (e.g., frame.data.comment_bans[0], frame.data.members[0], frame.data.posts[0]) without per-endpoint validation. This pattern is common across API endpoints. When maintaining or creating endpoints, avoid duplicating validation for frame.data[docName] and ensure the serializer guarantees the shape and non-emptiness. If you add a new endpoint that uses this frame.data[docName], follow the same assumption and avoid redundant checks unless there's a documented exception.

Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25791
File: ghost/core/test/e2e-api/admin/member-commenting.test.js:268-321
Timestamp: 2026-01-26T13:53:03.597Z
Learning: In Ghost's e2e API tests, audit events should separate the generic operation from the specific action. Assert that event equals the CRUD operation type (e.g., 'edited', 'added', 'deleted') and, if needed, also assert on context.action_name for the exact action (e.g., 'commenting_disabled', 'commenting_enabled'). This makes tests robust to both high-level and specific audit details.

Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 26219
File: ghost/core/test/e2e-api/members-comments/comments.test.js:939-983
Timestamp: 2026-02-04T15:58:09.124Z
Learning: In Ghost core tests and code that interact with the Ghost comments API, count.replies is a backward-compatible alias for count.total_replies (all descendants via parent_id) and does not represent direct replies. The new field count.direct_replies returns tree-native direct reply counts. Reviewers should verify any code paths, tests, or API surface areas that rely on count.replies are preserved for compatibility, and consider updating or adding tests to cover count.direct_replies for direct counts. When updating or adding tests, ensure behavior is documented and that any assertions reflect the distinction between total (including descendants) and direct reply counts to avoid regressions in API consumer expectations.

Learnt from: vershwal
Repo: TryGhost/Ghost PR: 26791
File: ghost/core/test/unit/server/services/media-inliner/test/external-media-inliner.test.js:1033-1039
Timestamp: 2026-03-12T10:43:01.366Z
Learning: In tests for the Ghost media-inliner, when reviews reference getMediaStorage withArgs(extension), verify that the extension corresponds to the actual binary content of the nock/mocked response (detected via magic bytes) rather than the URL filename. This applies to all tests under ghost/core/test/unit/server/services/media-inliner (and similar media-inliner tests). Use binary content to determine expected extension (e.g., a GIF blob should yield .gif even if the URL ends with .jpg).

Learnt from: vershwal
Repo: TryGhost/Ghost PR: 27290
File: ghost/core/package.json:76-77
Timestamp: 2026-04-09T09:44:26.783Z
Learning: In the TryGhost/Ghost monorepo, treat `tryghost/admin-api-schema` as the single abstraction layer over AJV version differences. Do not raise code review findings for AJV-internal error field changes (e.g., `dataPath` → `instancePath` between AJV v6 and v8) when evaluating Ghost consumer code. The consumer-facing error contract for this package (`ValidationError` with `message`, `property`, `errorDetails`) is expected to remain stable, and Ghost wrapper code should not inspect raw AJV error objects—so review should focus on the stable `ValidationError` shape rather than AJV internals.

@ErisDS ErisDS merged commit 4bf863a into main May 21, 2026
42 checks passed
@ErisDS ErisDS deleted the add-private-site-word-access-code branch May 21, 2026 14:04
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.

1 participant