Title: GSoC 2026 Proposal Draft - Idea 6: Strengthen the Security of the Plugin Ecosystem - Sriram Varun Kumar
Links
-
Project idea: idea-6
-
GitHub: varunkumar-22
-
Forum introduction: intro
-
PoC Demo Video: Video
Merged Pull Requests:
#14504 :- Mobile:Rich Text Editor: Fix extra blank line above nested lists
#14477:- Desktop: Fix UI freeze when closing plugin dialog with Escape key
#14421:-All: Fixes #14335: Support include_deleted parameter for GET /folders endpoint
#14461:-CLI: Fixes #13158: Fix null crash in e2ee decrypt command
#14541:-Desktop: Fixes #14196: Fix file:// links with backslashes for Windows UNC paths
#14580:-Mobile: Fix tapping rendered image scrolling to cursor position
#14559:-CLI: Fix trailing spaces in ls -l output
#14432:-Desktop: Fix "Copy dev mode command" producing an unquotable path on Windows
1. Introduction
My name is Sriram Varun Kumar, a B.Tech Computer Science student at Medhavi Skills University. I've been contributing to Joplin for the past few weeks and spent time reading through the plugin infrastructure specifically the plugin-repo-cli pipeline, the manifest validation logic, and how RepositoryApi.ts serves plugin data to the desktop app. Outside of Joplin I work with TypeScript, Node.js, and React, and I'm comfortable navigating large monorepos.
2. Project Summary
The problem:
The Problem:
Joplin has over 300 plugins today and every single one of them entered the plugin store the same way. an automated script detected it and it became available in the plugin store with no human ever reviewing it. No one looked at the code. No one checked the dependencies. The .jpl file that users download could contain anything and there is no way to verify it actually matches what is in the author's repository.
There are some safety nets in place. Plugins run in sandboxed processes, the manifest validation blocks ID hijacking, and a recommended flag lets maintainers highlight plugins they trust. But none of this touches the real problem. The binary users install is built privately on the author's own machine before Joplin's pipeline even sees it. A malicious author could show clean code on GitHub and publish something completely different to npm and nothing in the current system would catch it.
This problem has been discussed multiple times in the Joplin community:
-
LINK : Users have directly asked whether plugins in the official repository go through any security vetting
-
LINK : Some community members shared workarounds like manually reading source code before installing plugins or running a separate Joplin profile without plugins for sensitive notes
-
LINK : There was a proposal to sandbox plugins and restrict access to Node.js modules like fs, but it was considered too broad in scope at the time
-
LINK : There were concerns about what happens to a plugin's recommended status after updates, since no re-review takes place
Why It Matters to Users
Most Joplin users assume the official plugin store means some level of vetting has happened. That assumption is wrong. Every plugin installed today is a binary built on the author's own machine with no oversight from the Joplin team. For users who store sensitive notes or personal data in Joplin, this is a real risk.
What Will Be Implemented
Right now any plugin author can publish to npm and their plugin reaches every Joplin user with no one ever looking at the code. This project fixes that by building a review pipeline from the ground up.
The submission side is kept as simple as possible for plugin authors. They open a PR with their repository URL and commit hash, and a GitHub Action takes care of formatting the registry entry automatically. When the author is ready for review they comment "Review Plugin" and the CI does the heavy lifting, cloning the source, scanning dependencies, running a security analysis, and posting a full summary for the maintainer in one PR comment.
On the build side, once a maintainer approves and merges the PR, a trusted pipeline clones the plugin at the exact reviewed commit and builds the .jpl directly from source on Joplin's own CI runner, not the author's machine. The pipeline is split into two jobs with different permission levels so that even a malicious build script cannot write anything to the repository.
Every reviewed plugin will carry a permanent record of what it does, network access, filesystem usage, eval usage, and more, stamped directly into the manifest. Users will see a simple Reviewed or Unreviewed badge in the plugin manager.
The 300+ plugins already in the store are not left behind. A migration script tags them all as unreviewed while the existing flow keeps running in parallel so nothing breaks for current users. A security hold mechanism lets maintainers pull a dangerous plugin from distribution with a single field change, no deployment needed.
Expected Outcome
Every new plugin entering the store will be built from reviewed source code on a trusted server by Joplin's own CI, not the author's machine. Users will see a clear Reviewed or Unreviewed label in the plugin manager. Existing plugins remain installable but are honestly labelled. The ecosystem stays open to new plugins while becoming meaningfully more secure for the people who use it.
3. Technical Approach
Understanding Current System:
TheRecommendation System and Why It Falls Short
The _recommended field in PluginManifest is how Joplin surfaces trusted plugins to users. Recommended plugins appear more prominently in the plugin browser, so users naturally treat them as safer choices. But this flag only reflects a maintainer's choice to highlight a plugin. It does not mean the binary was ever built from reviewed source. A recommended plugin goes through the exact same unverified npm pipeline as any other, so the badge carries a level of trust the current system cannot actually back up.
The New System:
Architecture
The new system has two files with distinct roles.
approved-plugins.json - Pipeline Input
-
Stores the repository URL, reviewed commit hash, reviewer name, build command, and status for every plugin that has gone through review.
-
A plugin only gets built if its entry exists here and its status is approved.
manifests.json - Pipeline Output
-
The generated output that the Joplin app reads to list plugins.
-
It never gets modified directly. The build pipeline writes to it after a successful build.
Keeping them separate avoids a circular dependency where the pipeline reads from the file it also writes to. There was some discussion about consolidating them into manifests.json following the existing pattern of _recommended and _publish_hash. I think the separation is cleaner but I will discuss this with mentors during community bonding and go with whatever the team prefers.
How an Entry Is Created
When a plugin author opens a PR they provide their repository URL and the commit hash they want reviewed. The GitHub Action reads those two values and formats the full approved-plugins.json entry automatically, filling in the standard build command and publish directory. The reviewer name, review date, and approved status are then stamped onto the entry when the maintainer merges the PR. The pipeline reads the completed entry and only builds plugins where status is approved.
Forge Support
The system works with any public git forge that supports cloning at a commit hash. While the initial implementation targets GitHub, GitLab and Codeberg work with the same approach and need no architectural changes.
Figure 2: Sample approved-plugins.json Entry [POC]
Architecture
Changes to plugin-repo-cli
The core change is in packages/plugin-repo-cli/index.ts. The current commandBuild function searches npm and calls processNpmPackage for each result, downloading a pre-built .jpl that was built on the author's own machine. The new version reads approved-plugins.json and calls buildPluginFromSource for each approved entry.
buildPluginFromSource - Steps in Order
-
Clones the repository at the exact reviewed commit hash
-
Runs npm install with the --ignore-scripts flag to prevent malicious postinstall scripts
-
Deletes the plugin's package-lock.json and regenerates it fresh from the real registry to prevent lockfile poisoning
-
Runs the build command specified in approved-plugins.json
-
Runs the ESLint security scan on the cloned source
-
Stamps _security_signals and review metadata into the manifest
-
Extracts the .jpl from the publish directory
If any step fails, the function exits immediately, cleans up the temporary directory, and reports the failure. No partial output is written.
Edge Cases
-
If a plugin's reviewed commit has not changed since the last build, the rebuild is skipped entirely to avoid unnecessary CI usage.
-
If a plugin specifies a custom build command in approved-plugins.json that differs from the standard npm run dist, that command is used instead, allowing plugins with non-standard build setups to work without being rejected.
What Stays the Same
The rest of the pipeline, writing manifests, updating the README, and creating GitHub releases, stays exactly the same. This minimises the change surface and avoids breaking anything that already works.
Submission Workflow
Plugin authors submit or update their plugin by opening a PR against joplin/plugins. A GitHub Action reads the repository URL and commit hash from the PR description and formats the approved-plugins.json entry automatically. The author never touches JSON directly.
What the PR Description Needs
-
Repository URL
-
Commit hash they want reviewed
-
Changelog or description of what changed (for updates)
Review Plugin Trigger
The CI does not start automatically on PR open. Instead a bot comments on every new PR asking the author to comment "Review Plugin" when they are ready. This keeps the review queue clean and avoids wasted CI runs on commits that are still being finalized.
The trigger has the following rules:
-
Only accepted from the PR author or a maintainer with write access to the repository
-
Comments from other users are ignored by the bot, preventing spam and unauthorised CI triggers
What CI Does After the Trigger
Once the author comments "Review Plugin" the CI runs the following and posts everything as a single summary comment for the maintainer:
-
Clones the repo at the submitted commit hash
-
Runs the dependency audit
-
Runs the ESLint security scan
-
Generates a diff from the last reviewed commit
The system is designed around public git repositories. While the initial implementation targets GitHub, any public git forge that supports cloning at a commit hash such as GitLab or Codeberg is compatible with the same approach with no architectural changes needed.
Updates
For updates the author updates the commit hash in the PR description and comments "Review Plugin" again. The CI generates a diff between the old and new commits keeping the review focused and manageable.
Security Updates
For security updates, plugin authors report the vulnerability using GitHub Private Security Advisories following the instructions in SECURITY.md. This keeps the vulnerability details and the fix completely private until it is ready to ship. The maintainer collaborates with the author on the fix through the private advisory, reviews the patched commit manually, and only publishes once the fix is merged and distributed. Automated CI scanning does not trigger on private forks the same way it does on public PRs, so the maintainer reviews these manually. The tradeoff is acceptable since security fixes are typically small and targeted. This will be discussed further with mentors during community bonding to confirm the best approach.
Dependency Auditing and Change Detection
Dependencies are the biggest attack surface in any plugin. A plugin's own code might be 200 lines but its node_modules can contain thousands of packages. The CI handles this in three layers.
Layer 1: Template Diff on First Submission
On first submission the CI compares the plugin's api/, package.json, and webpack.config.js against the upstream Joplin plugin generator template and posts the diff in the PR comment. This surfaces anything unusual in files that might otherwise look like standard boilerplate and helps reviewers spot malicious changes without reading every line manually.
Layer 2: Lockfile Poisoning Prevention
Before running npm install the pipeline deletes the plugin's package-lock.json so npm regenerates it fresh from the real registry. This prevents lockfile poisoning where a manipulated lockfile could silently redirect npm to fetch trusted package names from an attacker controlled source. Both npm audit and ESLint would miss this attack since they only see package names not where they were fetched from.
Layer 3: Dependency Audit and Change Detection
The CI runs npm audit on the full dependency tree to catch known vulnerabilities. For updates it also compares the old and new package.json and posts a clear summary as a PR comment showing any added, removed, or version-changed packages. Reviewers immediately know if the dependency surface changed without having to dig through the diff themselves.
Edge Case
If a plugin has no package-lock.json in its repository the pipeline skips the deletion step and proceeds directly to npm install, generating a fresh lockfile from the real registry. If a plugin's repository contains a custom .npmrc pointing to a private registry, the pipeline flags this during the CI phase and blocks the submission until the author removes it, since the build environment cannot access private registries and the dependency source cannot be verified.
Out of Scope
Dependency allowlisting is out of scope for this project but is the natural next step once the review pipeline is running and patterns emerge about which packages are universally safe. That data can then be formalized into an allowlist, reducing reviewer burden on future submissions.
GitHub Actions CI Pipeline:
There are two workflows, both triggered by changes to approved-plugins.json.
Workflow 1: PR Check Workflow
Runs when the author comments "Review Plugin" on a submission PR.
-
Validates JSON format of the approved-plugins.json entry
-
Clones the plugin repo at the submitted commit hash
-
Deletes package-lock.json and regenerates it fresh from the real registry
-
Compares api/, package.json, and webpack.config.js against the upstream Joplin plugin generator template and includes the diff in the PR comment
-
Runs dependency audit and ESLint scan
-
Generates a diff from the last reviewed commit
-
Posts the full summary as a single PR comment for the maintainer
Workflow 2: Build-on-Merge Workflow
Runs when the PR is merged and the review is approved. This workflow is split into two separate jobs with different permission scopes.
Build Job - runs with contents: read permission only, never writes to the repository:
-
Clones the repo at the reviewed commit
-
Deletes package-lock.json and regenerates it fresh from the real registry
-
Runs npm install --ignore-scripts and the build command
-
Runs ESLint scan and stamps _security_signals into the manifest
-
Verifies the manifest ID matches the plugin ID in approved-plugins.json
-
Uploads the .jpl and manifest as a GitHub Actions artifact
Commit Job - runs after the build job with contents: write permission, never executes any plugin code:
-
Downloads the artifact from the build job
-
Commits the .jpl and manifest to the joplin/plugins repository
-
Creates the GitHub release
The needs: build line ensures the commit job only runs after the build job completes successfully. The build job has contents: read only and cannot write to the repository under any circumstance. The commit job has contents: write but never runs any plugin code. It only handles the artifact produced by the build job.
Figure 4: GitHub Actions Workflow - Separated Build and Commit Jobs
Why Job Separation is Necessary
Joplin plugins are built with Webpack and Webpack's own threat model states that it trusts project sources and assets. This means a malicious Webpack config could cause damage during the build step. By giving the build job no write permissions, a compromised build script cannot access the commit token or push anything to the repository. Only the commit job, which never runs plugin code, holds write access.
Edge Cases
If the build job fails after merge, the commit job does not run and no .jpl is published. The failure is visible in the GitHub Actions log and the maintainer can re-trigger the workflow manually once the issue is resolved.
Security Signals Scanning
The security scan runs as part of buildPluginFromSource before the .jpl is built. Since Joplin already uses ESLint this adds zero new infrastructure. No new service, no account, no Docker image.
ESLint Plugins Used
-
eslint-plugin-security for detecting eval, dynamic require, and unsafe patterns
-
@microsoftmicrosoftmicrosoftmicrosoft/eslint-plugin-sdl for detecting new Function and other dynamic execution vectors
-
A custom rule for detecting network calls like fetch, axios, and http.request
Sample PR Comment Output
The CI posts the following as part of the PR comment for the maintainer:
How Signals Are Used
-
The results are stamped into the manifest as _security_signals, a permanent field that every reviewed plugin carries
-
This data is available to the plugin manager and can be used by maintainers and future tooling
-
The user facing UI keeps it simple, showing just a Reviewed or Unreviewed badge
-
A plugin with network_calls: true and fs_access: true is not automatically rejected but a reviewer seeing both together will look more carefully at what the plugin is doing with both capabilities
Protection Against Self-Setting
validateUntrustedManifest.ts is updated to block plugin authors from self-setting _security_signals, following the same pattern already used for _recommended and _review_status. Only the build pipeline can stamp these fields onto a manifest.
Why Not Socket.dev
Socket.dev could serve as an enrichment layer for plugins still on npm during migration, but ESLint running on cloned source provides stronger guarantees with no external service dependency."
Optional Enhancement: Semgrep CE
If core deliverables land ahead of schedule, Semgrep CE can be added as a second layer on top of ESLint for deeper semantic analysis.
-
Fully open source under LGPL-2.1
-
Requires no account or API key
-
Runs directly on cloned source
-
This is explicitly a future enhancement as ESLint alone covers all the core signal
Manifest Extensions
PluginManifest in packages/lib/services/plugins/utils/types.ts is extended with the following new fields. These fields are pipeline-only, meaning only the build pipeline can write them. Plugin authors cannot set them manually.
New Fields and What They Mean
-
_review_status: either "reviewed" or "unreviewed". Set to "unreviewed" for all existing plugins during migration and set to "reviewed" only after a successful source build and human approval.
-
_reviewed_commit: the exact commit hash that was reviewed and built. This makes the review auditable. Anyone can check out this commit and verify what was reviewed.
-
_review_date: the date the review was completed. Stamped automatically on merge.
-
_security_signals: a permanent record of what the ESLint scan found in the plugin source. Contains eval_usage, child_process, dynamic_require, network_calls, fs_access, and scanned_at. This travels with the plugin forever and is available to the plugin manager and future tooling.
Figure 6: Extended PluginManifest Interface [POC]
Protection Against Self-Setting
validateUntrustedManifest.ts is updated to block plugin authors from self-setting any of these fields, following the same pattern already used for _recommended. Only the build pipeline can stamp these fields onto a manifest.
Figure 7: validateUntrustedManifest.ts Validation Check
Backward Compatibility
All new fields are optional, and the plugin manager treats a missing _review_status as unreviewed, so no existing plugin breaks.
Review Status in the Plugin Manager UI
The desktop app already shows _recommended badges in the plugin manager using an existing pattern in RepositoryApi.ts. The same pattern will be reused for review status, keeping the implementation minimal and consistent with what is already in the codebase.
Two States Shown to Users
-
Reviewed: source code reviewed by Joplin maintainers, built from source on a trusted CI runner, security signals scanned. The user can trust that what they are installing matches what was reviewed.
-
Unreviewed: entered through the npm-based flow, source not verified. The binary could have been built on the author's own machine. The user is informed and can decide.
What the Badge Shows
For reviewed plugins the badge will show the review date and a link to the reviewed commit so users can verify exactly what was audited. For unreviewed plugins the badge shows a clear warning that the source has not been verified.
Restricted Install Mode
In restricted install mode, only reviewed and recommended plugins would be installable. This gives organisations and privacy-conscious users a way to enforce a stricter plugin policy without modifying the app beyond a settings toggle.
Implementation Approach
-
The _review_status field in the manifest drives the badge display
-
RepositoryApi.ts is updated to read _review_status and pass it to the plugin info panel
-
The existing _recommended badge rendering is used as the reference implementation
-
No new UI framework or component library is needed
Vulnerability Reporting
A SECURITY.md will be added to joplin/plugins with clear instructions for reporting plugin vulnerabilities. The response mechanism reuses the existing manifestOverrides.json pattern keeping it consistent with what is already in the codebase.
Reporting Process
Plugin vulnerabilities are reported using GitHub Private Security Advisories following the instructions in SECURITY.md. This keeps the vulnerability details and the fix completely private until it is ready to ship. The process works as follows:
-
The reporter opens a private security advisory in the joplin/plugins repository
-
The maintainer is notified privately and coordinates with the plugin author on a fix
-
The author prepares a patched commit and shares it through the private advisory
-
The maintainer reviews the patched commit manually since automated CI does not trigger on private forks
-
Once the fix is ready the maintainer merges it and immediately applies a security hold if needed
-
The advisory is published only after the fix is distributed to users
This ensures the vulnerability is never publicized before users have access to the fix. The tradeoff of manual review for security fixes is acceptable since these are typically small and targeted changes. This will be discussed further with mentors during community bonding to confirm the best approach.
Immediate Response: Security Hold Mechanism
For a quick response, a _security_hold: true field in approved-plugins.json immediately pulls a dangerous plugin from distribution while a fix is in progress.
This follows the existing manifestOverrides.json pattern of using _obsolete: true with an _obsolete_reason, keeping the response mechanism consistent with what is already in the codebase.
-
A maintainer can pull a dangerous plugin from distribution with a single field change and one merged PR
-
No deployment required
-
The plugin remains visible in the plugin manager but with a clear security warning so users know not to install it
-
Once the fix is reviewed and a new build is published the security hold is lifted by removing the field
Full Response Timeline
-
Vulnerability reported privately via GitHub Security Advisory
-
Maintainer applies _security_hold immediately if the risk is active
-
Author prepares fix privately through the advisory
-
Maintainer reviews patched commit manually
-
Fix merged and new .jpl built and published through the normal pipeline
-
Security hold lifted and advisory published publicly
Migration
There are 300+ existing plugins that entered through the npm flow and cannot all be reviewed overnight. The three phase migration plan is shown in the diagram below.
One edge case worth noting - I checked the current manifests.json and found 8 plugins with no linked public repository. These cannot go through the source review flow since there is no code to review. They will remain tagged as unreviewed until the author links a repository and submits it for review.
Libraries and Technologies
-
TypeScript: all changes follow the existing codebase conventions
-
ESLint: eslint-plugin-s@microsoftmicrosoftcurity, @microsoft/eslint-plugin-sdl, and one custom rule for network call detection. Chosen because Joplin already uses ESLint, adding zero new infrastructure.
-
GitHub Actions: two workflow files, job separation using artifacts and permission scopes
-
GitHub Private Security Advisories: used for coordinating private vulnerability reports and fixes before public disclosure
-
zod: JSON schema validation for approved-plugins.json entries. Chosen over AJV because it gives TypeScript type inference directly from the schema with no separate type definitions needed.
-
Node.js 20: standardised across the build pipeline for consistency
Potential Challenges
Build environment consistency: Plugins may use different Node.js versions or custom build commands. The approved-plugins.json schema allows a build command per plugin and CI uses a standardised Node.js version to handle this.
Migration scale: Tagging 300+ plugins as unreviewed requires a migration script that runs without breaking existing installs. It will be tested against a full copy of manifests.json before production.
Review bandwidth: 300+ plugins is a large queue. The ESLint scan and dependency audit are designed to reduce manual effort per plugin so reviewers can focus on intent rather than pattern matching.
Plugins with native dependencies: Some plugins use native Node.js modules that require platform-specific compilation. These may fail in CI environments. Custom build commands can be specified in approved-plugins.json and the issue will be raised with mentors during community bonding.
Webpack configuration complexity: Joplin plugins use Webpack for bundling and some may have complex configs. As confirmed by Webpack's own threat model, Webpack trusts project sources, meaning a malicious config could behave unexpectedly. Job separation limits the blast radius since the build job has no write access, but complex configs may still cause build failures that need graceful handling.
Plugin authors not migrating: Some authors may not maintain their plugins or respond to review requests. These will remain permanently unreviewed and the UI will show a clear warning so users can decide.
Backward compatibility: The npm-based flow must keep running in parallel throughout GSoC without disruption. Any changes to plugin-repo-cli will be carefully tested to ensure the existing flow is unbroken while the new flow is built alongside it.
Lockfile poisoning edge cases: Deleting package-lock.json before every build solves the poisoning vector but introduces a new risk where npm resolves a slightly different version of a dependency than what the author tested. If a dependency releases a breaking update between the author's test and the CI build, the build fails unexpectedly. This will be handled by pinning dependency versions in package.json where possible and surfacing clear error messages when version resolution fails so the maintainer can investigate.
Private registry detection: Some plugins may have a .npmrc file pointing to a private or custom registry. When the pipeline regenerates the lockfile fresh, npm will fail to resolve packages from a registry it cannot access. The CI phase will check for the presence of a custom .npmrc and block the submission with a clear message asking the author to remove it before the pipeline can proceed.
Review turnaround under load: The automated CI posts everything a maintainer needs in one PR comment, keeping reviews fast even under load. If the queue grows, the ESLint scan, dependency audit, and template diff reduce time per review so no single plugin blocks the pipeline.
4.IMPLEMENTATION PLAN
Community Bonding: May 1 to May 24
The core PoC is already built - buildPluginFromSource, approved-plugins.json schema, JSON validation, and 7 passing tests. The bonding period will be used to align with mentors on the schema design and the approved-plugins.json vs manifests.json decision, discuss the job separation design for the build pipeline, confirm the GitHub Action auto-formatting approach for PR submissions, identify the top 10 most popular plugins to use as migration test cases, and refine the PoC based on mentor feedback.
Month 1: May 25 to June 20
Week 1-2:
-
Finalise approved-plugins.json schema based on mentor feedback
-
Build the GitHub Action that reads repo URL and commit hash from PR description and auto-formats the approved-plugins.json entry
-
Create PR submission template with "Review Plugin" comment trigger
-
Implement bot comment flow on PR open with trigger rules limiting it to PR author and maintainers only
-
Set up initial test data with 5-10 existing plugins
Week 3-4:
-
Integrate buildPluginFromSource into plugin-repo-cli/index.ts
-
Modify commandBuild to read from approved-plugins.json
-
Add lockfile deletion and regeneration step to buildPluginFromSource to prevent lockfile poisoning
-
Keep backward compatibility with existing npm flow during transition
-
Write tests for the new build path
Month 2: June 21 to July 18
Week 1-2:
-
Integrate npm audit into review pipeline
-
Build template diff between plugin's api/, package.json, and webpack.config.js against the upstream Joplin plugin generator template
-
Build dependency change detection between old and new package.json for updates
-
Write the PR check GitHub Actions workflow triggered by "Review Plugin" comment
-
Test with real plugin updates
Week 3-4:
-
Implement plugin-build.yml with separated build and commit jobs
-
Build job with contents: read only, outputs .jpl as artifact
-
Commit job picks up artifact, handles write step, never runs plugin code
-
Add manifest extensions including _security_signals, _review_status, _reviewed_commit, and _review_date
-
Integrate ESLint security scanning into buildPluginFromSource using eslint@microsoftmicrosoftplugin-security, @microsoft/eslint-plugin-sdl, and custom network call rule
-
Update validateUntrustedManifest.ts to block self-setting of all pipeline fields
-
End to end testing of full submission-to-build path
Midterm Evaluation: July 18 to July 25
Full pipeline working end to end. Plugin submitted via PR, GitHub Action auto-formats the entry, CI triggered by "Review Plugin" comment, dependency audit and ESLint scan posted as a single PR comment, maintainer approves, build job runs with contents: read only, commit job picks up artifact and publishes .jpl with _security_signals stamped into manifest. The midterm proof will be walking joplin-plugin-backup through the entire new flow.
Month 3: July 25 to August 16
Week 1-2:
-
Add review status display to plugin manager UI showing Reviewed and Unreviewed states
-
Update RepositoryApi.ts to read _review_status and pass it to the plugin info panel
-
Build migration script to generate approved-plugins.json from existing manifests.json
-
Mark all existing 300+ plugins as _review_status: "unreviewed"
Week 3-4:
-
Create SECURITY.md for joplin/plugins with vulnerability reporting instructions
-
Implement _security_hold mechanism in approved-plugins.json for rapid response
-
Write documentation for plugin authors on the new PR-based submission process
-
Write documentation for reviewers on reading the automated PR comment and handling security holds
-
If core deliverables are complete: begin Semgrep CE integration as optional second scanning layer
Final Assessment: August 17 to August 24
-
Comprehensive testing of the full workflow
-
Final bug fixes
-
Migration of at least 10 popular plugins to the new source-reviewed flow as proof of concept
-
Clean up PRs and ensure all code is well documented
5.Deliverables
Code
-
buildPluginFromSource function in plugin-repo-cli replacing extractPluginFilesFromPackage
-
Lockfile deletion and regeneration step inside buildPluginFromSource to prevent lockfile poisoning
-
GitHub Action that auto-formats approved-plugins.json entry from repo URL and commit hash in PR description
-
approved-plugins.json registry format with JSON schema validation via zod
-
PR submission template with "Review Plugin" comment trigger, restricted to PR author and maintainers only
-
Two GitHub Actions workflows: PR check workflow and build-on-merge workflow
-
Build-on-merge workflow split into two jobs: build job with contents: read only outputting an artifact, and commit job that picks up the artifact and handles the write step without ever running plugin code
-
Template diff of api/, package.json, and webpack.config.js against the upstream Joplin plugin generator on first submission
-
ESLint security scanning using eslint-plugin-security, @microsoft/eslint-plugin-sdl, and a custom network call rule, with _security_signals stamped permanently into manifests
-
validateUntrustedManifest.ts updated to block self-setting of all pipeline fields: _review_status, _reviewed_commit, _review_date, and _security_signals
-
Migration script to tag all 300+ existing plugins as _review_status: "unreviewed"
-
_security_hold mechanism in approved-plugins.json for rapid vulnerability response
-
SECURITY.md for joplin/plugins with clear vulnerability reporting instructions
-
Review status badges in plugin manager UI showing Reviewed and Unreviewed states
Tests
-
Unit tests for buildPluginFromSource
-
Integration tests for the full submission-to-build path
-
Regression tests confirming the existing npm-based flow is unbroken during transition
Documentation
-
Plugin author guide for the new PR-based submission process including the "Review Plugin" trigger
-
Reviewer guide for reading the automated PR comment, understanding ESLint signals, and handling security holds
-
Security response guide for the _security_hold mechanism
-
Updated README for joplin/plugins
6.Availability
-
40 hours per week throughout the GSoC period
-
Timezone: IST (GMT+5:30), available 10am to 12 am IST
-
No exams, internships, or other commitments during the program
-
Will post weekly progress updates on the Joplin forum and remain active on Discord
-
Comfortable working asynchronously with mentors in different timezones
AI Disclosure
AI was used to correct grammatical mistakes, improve clarity and wording









