Fix Archer Jira ticket description auto-population and security audit fixes

Auto-populate description field when creating Jira tickets from the Archer
page with ticket metadata (EXC number, CVE, vendor, status, Archer URL).
Previously the description was always empty, requiring manual entry.

Includes security audit fixes for SQL injection prevention and input
validation in compliance, VCL multi-vertical, and CCP metrics routes.

Updates security audit tracker documentation.
This commit is contained in:
Jordan Ramos
2026-06-05 09:53:53 -06:00
parent e8aa7038ad
commit af5fa11421
6 changed files with 133 additions and 114 deletions

View File

@@ -352,7 +352,7 @@ function createComplianceRouter(upload) {
res.json({
drift, drift_error, schema: xlsxSchema,
diff: { new_count: diff.newCount, recurring_count: diff.recurringCount, resolved_count: diff.resolvedCount },
tempFile: tempFilePath, filename: req.file.originalname,
tempFile: tempFilename, filename: req.file.originalname,
report_date: parsed.report_date, total_items: parsed.total,
});
} catch (err) {
@@ -405,11 +405,13 @@ function createComplianceRouter(upload) {
router.post('/commit', requireGroup('Admin', 'Standard_User'), async (req, res) => {
const { tempFile, filename, report_date } = req.body;
if (!tempFile || typeof tempFile !== 'string') return res.status(400).json({ error: 'tempFile is required' });
if (!isSafeTempPath(tempFile)) return res.status(400).json({ error: 'Invalid tempFile path' });
if (!fs.existsSync(tempFile)) return res.status(400).json({ error: 'Preview session expired — please upload again' });
// Reconstruct full path from basename only — never trust a client-supplied absolute path
const resolvedTempFile = path.join(TEMP_DIR, path.basename(tempFile));
if (!isSafeTempPath(resolvedTempFile)) return res.status(400).json({ error: 'Invalid tempFile path' });
if (!fs.existsSync(resolvedTempFile)) return res.status(400).json({ error: 'Preview session expired — please upload again' });
let parsed;
try { parsed = JSON.parse(fs.readFileSync(tempFile, 'utf8')); }
try { parsed = JSON.parse(fs.readFileSync(resolvedTempFile, 'utf8')); }
catch { return res.status(400).json({ error: 'Could not read preview data — please upload again' }); }
try {
@@ -419,7 +421,7 @@ function createComplianceRouter(upload) {
filename: filename || parsed.filename,
userId: req.user?.id || null,
});
fs.unlink(tempFile, () => {});
fs.unlink(resolvedTempFile, () => {});
const { rows } = await pool.query(
`SELECT id, filename, report_date, uploaded_at, new_count, resolved_count, recurring_count