-
Notifications
You must be signed in to change notification settings - Fork 1
fix(adhoc-plugins-openframe-js): 3 review findings in openframe.js #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,8 +8,13 @@ const MESH_DEVICE_GROUP = process.env.MESH_DEVICE_GROUP || ''; | |
|
|
||
| // --- Helpers --- | ||
|
|
||
| function corsHeaders(res) { | ||
| res.set('Access-Control-Allow-Origin', '*'); | ||
| const ALLOWED_ORIGINS = (process.env.CORS_ALLOWED_ORIGINS || '').split(',').filter(Boolean); | ||
| function corsHeaders(req, res) { | ||
| var origin = req.headers.origin; | ||
| if (origin && ALLOWED_ORIGINS.includes(origin)) { | ||
| res.set('Access-Control-Allow-Origin', origin); | ||
| res.set('Vary', 'Origin'); | ||
| } | ||
| res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); | ||
| res.set('Access-Control-Allow-Headers', 'Content-Type, X-MeshAuth'); | ||
| } | ||
|
|
@@ -32,6 +37,22 @@ function deriveTenantDomain(domains) { | |
| return ''; | ||
| } | ||
|
|
||
| // Validate the X-MeshAuth shared secret. Returns true if the request is authenticated. | ||
| var MESH_AUTH_SECRET = process.env.MESH_AUTH_SECRET || ''; | ||
| function checkAuth(req, res) { | ||
| if (!MESH_AUTH_SECRET) { | ||
| // No secret configured β deny all to avoid accidentally open endpoints | ||
| sendError(res, 403, 'Authentication not configured'); | ||
| return false; | ||
| } | ||
| var provided = req.headers['x-meshauth'] || ''; | ||
| if (provided !== MESH_AUTH_SECRET) { | ||
| sendError(res, 401, 'Unauthorized'); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| // --- Plugin --- | ||
|
|
||
| module.exports.openframe = function (pluginHandler) { | ||
|
|
@@ -51,13 +72,15 @@ module.exports.openframe = function (pluginHandler) { | |
|
|
||
| // CORS preflight | ||
| app.options(['/generate-msh', '/api/*'], function (req, res) { | ||
| corsHeaders(res); | ||
| corsHeaders(req, res); | ||
| res.sendStatus(204); | ||
| }); | ||
|
|
||
| // Route 1: GET /generate-msh?host=X - Generate custom MSH agent config | ||
| app.get('/generate-msh', function (req, res) { | ||
| corsHeaders(res); | ||
| corsHeaders(req, res); | ||
|
|
||
| if (!checkAuth(req, res)) return; | ||
|
|
||
| var host = req.query.host; | ||
| if (!host) return sendError(res, 400, 'Missing required parameter: host'); | ||
|
Comment on lines
72
to
86
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 𦩠π΄ No authentication check on /generate-msh β any caller can obtain mesh credentials Added a π€ Prompt for AI agentsfix confidence: π‘ 75 medium β react π/π to teach the reviewer |
||
|
|
@@ -95,7 +118,9 @@ module.exports.openframe = function (pluginHandler) { | |
| // Route 2: GET /api/deviceStatus?id=node/<domain>/<hash> - Get device status | ||
| // Uses MeshCentral core: GetConnectivityState() (in-memory) + db 'lc' record | ||
| app.get('/api/deviceStatus', function (req, res) { | ||
| corsHeaders(res); | ||
| corsHeaders(req, res); | ||
|
|
||
| if (!checkAuth(req, res)) return; | ||
|
|
||
| var nodeId = req.query.id; | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 𦩠π΄ No authentication on /api/deviceStatus β unauthenticated callers can probe device existence and connectivity Added the same π€ Prompt for AI agentsfix confidence: π‘ 75 medium β react π/π to teach the reviewer |
||
| if (!nodeId) return sendError(res, 400, 'Missing required parameter: id'); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
𦩠π΄ CORS wildcard origin on all plugin API routes enables cross-origin data exfiltration
Changed
corsHeaders(res)tocorsHeaders(req, res)everywhere (definition and all three call sites: OPTIONS preflight,/generate-msh,/api/deviceStatus). The new implementation readsreq.headers.originand only echoes it back inAccess-Control-Allow-Originwhen it appears in theALLOWED_ORIGINSallowlist (populated fromprocess.env.CORS_ALLOWED_ORIGINS, a comma-separated list). AVary: Originheader is added whenever a specific origin is reflected. IfCORS_ALLOWED_ORIGINSis empty or the request origin is not listed, noAccess-Control-Allow-Originheader is emitted, so browsers will block cross-origin reads. Risk: operators must setCORS_ALLOWED_ORIGINSin their environment or all browser-initiated cross-origin requests will be blocked (which is the safe default). Server-to-server callers (noOriginheader) are unaffected.π€ Prompt for AI agents
fix confidence: π‘ 85 medium β react π/π to teach the reviewer