[grade=B] DC-081: Input validation for 20 highest-risk mutating routes
Secures 20 mutating routes across 7 files against path traversal, shell injection, and ReDoS vectors: - containers.js: container ID validation + resource limit bounds (6 routes) - recipes/manage.js: recipe ID slug validation (4 routes) - tailscale.js: subdomain regex before interpolation + shell char blocking (2) - workflows.js: workflow ID slug validation (3 routes) - dependencies.js: service ID + dependsOn array validation (3 routes) - logs.js: YYYY-MM-DD date format validation (1 route) - sites.js: additional domain validation (1 route) Uses existing REGEX patterns from constants.js. No new dependencies. Codex: B (no blocking issues, 4 Low follow-ups for tests + strict bools). 1552/1552 tests pass, 0 regressions.
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
const express = require('express');
|
||||
const { ok } = require('../src/utils/responses');
|
||||
const { ValidationError } = require('../src/utilities/errors');
|
||||
|
||||
/**
|
||||
* Validate a workflow ID.
|
||||
* @param {string} workflowId - Workflow ID from route param
|
||||
* @throws {ValidationError} if the ID contains unsafe characters
|
||||
*/
|
||||
function validateWorkflowId(workflowId) {
|
||||
if (!workflowId || typeof workflowId !== 'string') {
|
||||
throw new ValidationError('Workflow ID is required');
|
||||
}
|
||||
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(workflowId)) {
|
||||
throw new ValidationError('Invalid workflow ID format');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Workflows routes factory
|
||||
@@ -27,6 +42,7 @@ module.exports = function({ workflowEngine, licenseManager, asyncHandler, ok })
|
||||
// Enable a workflow
|
||||
router.post('/workflows/:workflowId/enable', asyncHandler(async (req, res) => {
|
||||
const { workflowId } = req.params;
|
||||
validateWorkflowId(workflowId);
|
||||
const result = workflowEngine.setWorkflowEnabled(workflowId, true);
|
||||
ok(res, result);
|
||||
}, 'workflows-enable'));
|
||||
@@ -34,6 +50,7 @@ module.exports = function({ workflowEngine, licenseManager, asyncHandler, ok })
|
||||
// Disable a workflow
|
||||
router.post('/workflows/:workflowId/disable', asyncHandler(async (req, res) => {
|
||||
const { workflowId } = req.params;
|
||||
validateWorkflowId(workflowId);
|
||||
const result = workflowEngine.setWorkflowEnabled(workflowId, false);
|
||||
ok(res, result);
|
||||
}, 'workflows-disable'));
|
||||
@@ -41,6 +58,7 @@ module.exports = function({ workflowEngine, licenseManager, asyncHandler, ok })
|
||||
// Manually trigger a workflow
|
||||
router.post('/workflows/:workflowId/run', asyncHandler(async (req, res) => {
|
||||
const { workflowId } = req.params;
|
||||
validateWorkflowId(workflowId);
|
||||
const triggerData = req.body || {};
|
||||
triggerData.trigger = 'manual';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user