Skip to content

Api, Cli, Desktop: Resolves #16054: Add fail-closed guardrails for locked notes - #16098

Open
keshav0479 wants to merge 3 commits into
laurent22:devfrom
keshav0479:gsoc-local-note-encryption-guardrails
Open

Api, Cli, Desktop: Resolves #16054: Add fail-closed guardrails for locked notes#16098
keshav0479 wants to merge 3 commits into
laurent22:devfrom
keshav0479:gsoc-local-note-encryption-guardrails

Conversation

@keshav0479

@keshav0479 keshav0479 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Resolves #16054
Adds fail-closed checks for the paths that reach a note without going through the editor, behind the existing feature flag.

Data API:

  • Reading or writing a locked note's body, listing its resources, or changing is_locked is refused. Metadata is unaffected, so renaming still works, and the response no longer echoes the body back.
  • Collection responses exclude locked notes entirely: GET /notes, GET /folders/:id/notes and GET /tags/:id/notes skip them, and GET /revisions skips the revisions of locked notes. The API documentation mentions the exclusions.
  • Search needed no change, locked notes never enter the search index.
  • Revision load and save are blocked, deletion is left as is.
  • Plugins and the web clipper go through the same routes, so the same checks cover them.

CLI:

  • cat, edit and set refuse locked notes, along with the other commands that already refuse encrypted items, and edit fails before writing the temp file.
  • set also refuses to change is_locked itself, with a plain error instead of the deep failure it would otherwise hit.

Convert to Markdown:

  • Works on a locked note once the session is unlocked, through a full gated load and save, so the conversion reads the decrypted body and the converted copy stays encrypted.
  • While the session is locked it shows an error for external callers, and the menu entry is greyed out, same as printing.

With the flag off, nothing changes, everything runs through the old code paths.

Testing

  • yarn tsc
  • yarn workspace @joplin/lib test (routes/notes, routes/revisions, convertNoteToMarkdown, Note, NoteLockService, NoteLockSession, RevisionService)
  • yarn workspace joplin test (command-cat, command-set, command-edit)
  • Manually tested:
    • Data API against a running dev app: GET /notes, GET /folders/:id/notes and GET /tags/:id/notes no longer list a locked note, GET /revisions contains no revisions of a locked note, single note metadata still works and a body request returns 403.
    • CLI: with the flag on, set is_locked on a plain note stops with the plain error; with the flag off it saves like before.
    • Convert to Markdown on a locked HTML note: with the session unlocked it converted, the copy kept its padlock and opened readable, and the original landed in the trash. With the session locked the menu entry was greyed out.
    • The Secure Notes plugin toggle on a locked note with the session unlocked: it did nothing, and the note stayed locked and intact (video below).

Screenshots and video

Menu while session locked Converted while unlocked
Convert to Markdown greyed out Converted note still encrypted

Secure Notes plugin on a locked note, session unlocked:

video_20260802_134207.mp4

AI Assistance Disclosure

I used AI tools while working on this PR for code suggestions and review, checking scope and tests, and drafting parts of this description, including the disclosure. I reviewed the final changes and reran the tests listed above myself.

@coderabbitai coderabbitai Bot added api Joplin API cli CLI app specific issue enhancement Feature requests and code enhancements security labels Jul 30, 2026

@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.

.

Comment thread packages/lib/commands/convertNoteToMarkdown.ts Outdated
Comment thread packages/lib/services/rest/routes/notes.ts
Comment thread packages/lib/services/rest/routes/revisions.ts Outdated
@joplin-coderabbit-cleanup joplin-coderabbit-cleanup Bot deleted a comment from coderabbitai Bot Jul 30, 2026
@mrjo118
mrjo118 marked this pull request as draft July 31, 2026 01:23
Comment thread packages/lib/services/rest/routes/notes.ts
@coderabbitai coderabbitai Bot added the documentation Documentation, web site, README label Aug 2, 2026

@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.

.

Comment thread readme/api/references/rest_api.md
@joplin-coderabbit-cleanup joplin-coderabbit-cleanup Bot deleted a comment from coderabbitai Bot Aug 2, 2026
@keshav0479
keshav0479 marked this pull request as ready for review August 2, 2026 09:06
@keshav0479

Copy link
Copy Markdown
Contributor Author

All feedback addressed and description now covers the new behaviour with ss and plugin video.

@laurent22

Copy link
Copy Markdown
Owner

That looks good to me. @mrjo118, please confirm it's ok with you as well, and feel free to merge if it is

Comment thread packages/lib/services/rest/routes/revisions.ts
Comment thread packages/lib/commands/convertNoteToMarkdown.ts Outdated
}
},
enabledCondition: 'selectionIncludesHtmlNotes && (multipleNotesSelected || !noteIsReadOnly)',
enabledCondition: 'selectionIncludesHtmlNotes && (multipleNotesSelected || !noteIsReadOnly) && !noteLockContentUnavailable',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

When the enabledCondition resolves false, does the banner not show (please check both desktop and mobile routes), or does the availability of the banner still need addressing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Checked both. The unlock and cannot-decrypt panels return before renderConvertHtmlToMarkdown is reached, so the banner can't show when content is unavailable, and mobile doesn't have the convert banner or route at all. One edge, a locked note with unsaved changes keeps the editor mounted when the session locks, so the banner stays and Convert it hits the command's error dialog. Can hide it there too if you want.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes hide it there too please

@joplin-coderabbit-cleanup joplin-coderabbit-cleanup Bot deleted a comment from coderabbitai Bot Aug 7, 2026
throw new Error(_('Cannot convert locked note: "%s"', note.title));
}
// Captured once so a session lock mid-run cannot fail the remaining conversions.
noteLockKey = NoteLockSession.instance().decryptedKey();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It would be better to first check if any of the notes are locked before the loop, then grab the decrypted key before iterating the loop, so that the key remains available for bulk conversion.

Also, the validation above if the session is locked should apply when the noteLockKey is populated, not just when it is not

}
// A locked note's revisions are not all flagged themselves, so the note state is checked too.
const whereQuery = isNoteLockEnabled() ? { sql: 'is_locked = 0 AND item_id NOT IN (SELECT id FROM notes WHERE is_locked = 1)' } : null;
const whereQuery = isNoteLockEnabled() ? { sql: 'is_locked = 0' } : null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does the whereQuery apply to DELETE? If it does, we don't locked notes to be excluded for that

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

Labels

api Joplin API cli CLI app specific issue documentation Documentation, web site, README enhancement Feature requests and code enhancements security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add API, CLI and Markdown conversion guardrails for locked notes

3 participants