Conversation
Greptile OverviewGreptile SummaryThis PR introduces a new PluginContext system that provides isolated security contexts and permission management for plugins. The implementation adds a Cordova plugin that generates unique tokens for each plugin and tracks their permissions through a native Android bridge. Key changes:
Issues found:
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant LP as loadPlugin.js
participant PC as PluginContext.js
participant Tee as Tee.java
participant Plugin as Plugin Instance
LP->>LP: Read plugin.json
LP->>PC: generate(pluginId, JSON.stringify(pluginJson))
PC->>Tee: requestToken(pluginId, pluginJson)
alt Plugin Already Disclosed
Tee-->>PC: Error: TOKEN_ALREADY_ISSUED
PC-->>LP: return null
else First Request
Tee->>Tee: Generate UUID token
Tee->>Tee: Parse permissions from pluginJson
Tee->>Tee: Store token & permissions
Tee->>Tee: Mark pluginId as disclosed
Tee-->>PC: Return token (UUID)
PC->>PC: Create _PluginContext(uuid)
PC-->>LP: Return frozen context object
end
LP->>Plugin: initPlugin(pluginId, baseUrl, $page, {ctx, ...})
Note over Plugin,Tee: Plugin can later check permissions
Plugin->>PC: ctx.grantedPermission("permission-name")
PC->>Tee: grantedPermission(token, permission)
Tee->>Tee: Check permissionStore[token]
Tee-->>PC: true/false
PC-->>Plugin: Permission result
Last reviewed commit: 82d3271 |
| generate: async function (pluginId, pluginJson) { | ||
| try { | ||
| function requestToken(pluginId) { | ||
| return new Promise((resolve, reject) => { | ||
| exec(resolve, reject, "Tee", "requestToken", [ | ||
| pluginId, | ||
| pluginJson, | ||
| ]); | ||
| }); |
There was a problem hiding this comment.
Native expects string JSON
On Android, Tee.java reads args.getString(1) and then new JSONObject(pluginJson), so the second argument must be a JSON string. Here pluginJson is passed directly (it’s an object in loadPlugin.js), which will arrive natively as a non-JSON value and trigger INVALID_PLUGIN_JSON, causing generate() to return null.
| generate: async function (pluginId, pluginJson) { | |
| try { | |
| function requestToken(pluginId) { | |
| return new Promise((resolve, reject) => { | |
| exec(resolve, reject, "Tee", "requestToken", [ | |
| pluginId, | |
| pluginJson, | |
| ]); | |
| }); | |
| exec(resolve, reject, "Tee", "requestToken", [ | |
| pluginId, | |
| JSON.stringify(pluginJson), | |
| ]); |
| <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="com.foxdebug.acode.rk.plugin.plugincontext" version="1.0.0"> | ||
| <name>Gatekeeper</name> | ||
|
|
||
|
|
||
| <js-module name="PluginContext" src="www/PluginContext.js"> | ||
| <clobbers target="window.PluginContext" /> | ||
| </js-module> |
There was a problem hiding this comment.
Plugin metadata mismatch
This plugin is plugincontext but the <name> is set to Gatekeeper, which will surface as the wrong plugin name in Cordova tooling/plugins list. This looks like a copy/paste error and should be renamed to match the actual plugin.
| <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="com.foxdebug.acode.rk.plugin.plugincontext" version="1.0.0"> | |
| <name>Gatekeeper</name> | |
| <js-module name="PluginContext" src="www/PluginContext.js"> | |
| <clobbers target="window.PluginContext" /> | |
| </js-module> | |
| <name>PluginContext</name> |
| "src/plugins/gatekeeper": { | ||
| "name": "com.foxdebug.acode.rk.plugin.gatekeeper", | ||
| "version": "1.0.0", | ||
| "extraneous": true, | ||
| "license": "MIT" | ||
| }, |
There was a problem hiding this comment.
Unrelated extraneous lockfile entry
This PR introduces an extraneous entry for src/plugins/gatekeeper, but there’s no corresponding dependency/plugin added in package.json. This will make installs non-reproducible across environments and should be removed from the lockfile (regenerate package-lock.json from a clean install) so only intentional dependencies are recorded.
Additional Comments (1)
|
| "com.foxdebug.acode.rk.exec.proot": {}, | ||
| "com.foxdebug.acode.rk.exec.terminal": {}, | ||
| "com.foxdebug.acode.rk.customtabs": {}, | ||
| "com.foxdebug.acode.rk.auth": {} | ||
| "com.foxdebug.acode.rk.plugin.plugincontext": {} |
There was a problem hiding this comment.
Plugin list inconsistency
cordova.plugins dropped the auth plugin entry while devDependencies still includes the local auth package. This makes the Cordova plugin list inconsistent with what gets installed, and can remove auth functionality unexpectedly. If auth is still required, add it back under cordova.plugins; otherwise remove the local auth dependency and update any consumers accordingly.
| private static final Map<String, String> tokenStore = new HashMap<>(); | ||
| private static final HashSet<String> disclosed = new HashSet<>(); | ||
| private static final Map<String, List<String>> permissionStore = new HashMap<>(); | ||
|
|
There was a problem hiding this comment.
Global static stores leak
tokenStore, disclosed, and permissionStore are static and never cleared. In a Cordova WebView, plugins can be installed/uninstalled or reloaded without a full process restart; these maps will retain old pluginIds/tokens/permissions and can cause TOKEN_ALREADY_ISSUED or stale permission results for later sessions. Consider scoping these stores to the plugin instance/lifecycle or providing a reset path tied to app/plugin reload.
| const uuid = await requestToken(pluginId); | ||
| return new _PluginContext(uuid); | ||
| } catch (err) { | ||
| console.warn(`PluginContext creation failed for pluginId ${pluginId}:`); |
There was a problem hiding this comment.
error object not logged in warning message
| console.warn(`PluginContext creation failed for pluginId ${pluginId}:`); | |
| console.warn(`PluginContext creation failed for pluginId ${pluginId}:`, err); |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| // pluginId : token | ||
| private /*static*/ final Map<String, String> tokenStore = new HashMap<>(); | ||
|
|
||
| //assined tokens |
There was a problem hiding this comment.
typo: 'assined' should be 'assigned'
| //assined tokens | |
| //assigned tokens |
| if (disclosed.contains(pluginId)) { | ||
| callback.error("TOKEN_ALREADY_ISSUED"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
disclosed set never cleared, blocks plugin reload
The disclosed set prevents the same pluginId from requesting a token twice in this plugin instance's lifetime. If loadPlugin() is called again for the same plugin (reload scenario), this check will fail with TOKEN_ALREADY_ISSUED even though it's a legitimate reload request.
No description provided.