mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-05-07 07:03:31 +00:00
fix(security): escape user-controlled output in plugin action UI (#323)
* style: trim trailing whitespace and fix EOF in plugins_manager.js Autofix from pre-commit hooks (trailing-whitespace, end-of-file-fixer). No code logic changes — purely whitespace. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(security): escape user-controlled output in plugin action UI The plugin action result handlers in executePluginAction() injected data.message, data.output, and data.auth_url directly into innerHTML template literals without escaping. A plugin's action handler returning malicious content (e.g., from a third-party plugin or compromised upstream) could execute arbitrary JavaScript in the web UI context. Wrap user-controlled strings in escapeHtml() at all four sites: - Step-2 (continuation) error path (message + output) - OAuth flow auth_url (link href + display text, with http:// guard) - Step-1 simple-success output - Step-1 failure path (message + output) The escapeHtml() helper is already defined in this file and used elsewhere (validation errors, plugin store cards). Co-Authored-By: 5ymb01 <5ymb01@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: 5ymb01 <5ymb01@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4851,9 +4851,9 @@ window.executePluginAction = function(actionId, actionIndex, pluginIdParam = nul
|
|||||||
showNotification(data.message || 'Action completed successfully!', 'success');
|
showNotification(data.message || 'Action completed successfully!', 'success');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
statusDiv.innerHTML = `<div class="text-red-600"><i class="fas fa-exclamation-circle mr-2"></i>${data.message}</div>`;
|
statusDiv.innerHTML = `<div class="text-red-600"><i class="fas fa-exclamation-circle mr-2"></i>${escapeHtml(data.message || 'Error')}</div>`;
|
||||||
if (data.output) {
|
if (data.output) {
|
||||||
statusDiv.innerHTML += `<pre class="mt-2 text-xs bg-red-50 p-2 rounded overflow-auto max-h-32">${data.output}</pre>`;
|
statusDiv.innerHTML += `<pre class="mt-2 text-xs bg-red-50 p-2 rounded overflow-auto max-h-32">${escapeHtml(data.output)}</pre>`;
|
||||||
}
|
}
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
@@ -4897,8 +4897,8 @@ window.executePluginAction = function(actionId, actionIndex, pluginIdParam = nul
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<p class="text-sm text-blue-700 mb-2">1. Click the link below to authorize:</p>
|
<p class="text-sm text-blue-700 mb-2">1. Click the link below to authorize:</p>
|
||||||
<a href="${data.auth_url}" target="_blank" class="text-blue-600 hover:text-blue-800 underline break-all">
|
<a href="${data.auth_url && data.auth_url.startsWith('http') ? escapeHtml(data.auth_url) : '#'}" target="_blank" class="text-blue-600 hover:text-blue-800 underline break-all">
|
||||||
${data.auth_url}
|
${escapeHtml(data.auth_url || '')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
@@ -4920,7 +4920,7 @@ window.executePluginAction = function(actionId, actionIndex, pluginIdParam = nul
|
|||||||
<div class="text-green-900 font-medium mb-2">
|
<div class="text-green-900 font-medium mb-2">
|
||||||
<i class="fas fa-check-circle mr-2"></i>${data.message || 'Action completed successfully'}
|
<i class="fas fa-check-circle mr-2"></i>${data.message || 'Action completed successfully'}
|
||||||
</div>
|
</div>
|
||||||
${data.output ? `<pre class="mt-2 text-xs bg-green-50 p-2 rounded overflow-auto max-h-32">${data.output}</pre>` : ''}
|
${data.output ? `<pre class="mt-2 text-xs bg-green-50 p-2 rounded overflow-auto max-h-32">${escapeHtml(data.output)}</pre>` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@@ -4933,9 +4933,9 @@ window.executePluginAction = function(actionId, actionIndex, pluginIdParam = nul
|
|||||||
statusDiv.innerHTML = `
|
statusDiv.innerHTML = `
|
||||||
<div class="bg-red-50 border border-red-200 rounded p-3">
|
<div class="bg-red-50 border border-red-200 rounded p-3">
|
||||||
<div class="text-red-900 font-medium mb-2">
|
<div class="text-red-900 font-medium mb-2">
|
||||||
<i class="fas fa-exclamation-circle mr-2"></i>${data.message || 'Action failed'}
|
<i class="fas fa-exclamation-circle mr-2"></i>${escapeHtml(data.message || 'Action failed')}
|
||||||
</div>
|
</div>
|
||||||
${data.output ? `<pre class="mt-2 text-xs bg-red-50 p-2 rounded overflow-auto max-h-32">${data.output}</pre>` : ''}
|
${data.output ? `<pre class="mt-2 text-xs bg-red-50 p-2 rounded overflow-auto max-h-32">${escapeHtml(data.output)}</pre>` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@@ -8065,4 +8065,3 @@ setTimeout(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user