semgrep problems fix
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 5m57s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped

This commit is contained in:
Torsten Schulz (local)
2026-06-10 13:55:50 +02:00
parent 5da11d2e4d
commit b4e1c50ea3
3 changed files with 28 additions and 7 deletions

View File

@@ -20,8 +20,10 @@ const FILES = {
function getDataPath(filename) { function getDataPath(filename) {
const cwd = process.cwd() const cwd = process.cwd()
if (cwd.endsWith('.output')) { if (cwd.endsWith('.output')) {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename) return path.join(cwd, '../server/data', filename)
} }
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename) return path.join(cwd, 'server/data', filename)
} }

View File

@@ -34,6 +34,21 @@ function buildBackupName(date = new Date()) {
return `${date.toISOString().replace(/[:.]/g, '-')}-${randomSuffix}.bak` return `${date.toISOString().replace(/[:.]/g, '-')}-${randomSuffix}.bak`
} }
export function resolveDataFileBackupPath(backupDir, backupName) {
if (typeof backupName !== 'string' || !backupName.endsWith('.bak') || path.basename(backupName) !== backupName) {
throw new Error('Ungueltiger Backup-Dateiname')
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
const resolvedBackupDir = path.resolve(backupDir)
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
const resolvedBackupPath = path.resolve(resolvedBackupDir, backupName)
if (path.dirname(resolvedBackupPath) !== resolvedBackupDir) {
throw new Error('Backup-Datei liegt ausserhalb des Backup-Ordners')
}
return resolvedBackupPath
}
async function ensureDirectory(dirPath) { async function ensureDirectory(dirPath) {
await fs.mkdir(dirPath, { recursive: true }) await fs.mkdir(dirPath, { recursive: true })
} }
@@ -55,10 +70,11 @@ async function rotateOldBackups(backupDir, maxBackups) {
} }
const toDelete = backups.slice(0, overflowCount) const toDelete = backups.slice(0, overflowCount)
await Promise.all(toDelete.map((name) => fs.unlink(path.join(backupDir, name)).catch(() => {}))) await Promise.all(toDelete.map((name) => fs.unlink(resolveDataFileBackupPath(backupDir, name)).catch(() => {})))
} }
export function getBackupDirectoryForDataFile(filePath) { export function getBackupDirectoryForDataFile(filePath) {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
const resolvedPath = path.resolve(filePath) const resolvedPath = path.resolve(filePath)
return path.join(getBackupRoot(), sanitizeFileKey(resolvedPath)) return path.join(getBackupRoot(), sanitizeFileKey(resolvedPath))
} }
@@ -77,6 +93,7 @@ export async function writeDataFileWithRotation(filePath, content, {
encoding = 'utf-8', encoding = 'utf-8',
maxBackups = DEFAULT_MAX_BACKUPS maxBackups = DEFAULT_MAX_BACKUPS
} = {}) { } = {}) {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
const resolvedPath = path.resolve(filePath) const resolvedPath = path.resolve(filePath)
await ensureDirectory(path.dirname(resolvedPath)) await ensureDirectory(path.dirname(resolvedPath))
@@ -100,7 +117,7 @@ export async function writeDataFileWithRotation(filePath, content, {
if (existingContent !== null) { if (existingContent !== null) {
const backupDir = getBackupDirectoryForDataFile(resolvedPath) const backupDir = getBackupDirectoryForDataFile(resolvedPath)
await ensureDirectory(backupDir) await ensureDirectory(backupDir)
backupPath = path.join(backupDir, buildBackupName()) backupPath = resolveDataFileBackupPath(backupDir, buildBackupName())
await fs.copyFile(resolvedPath, backupPath) await fs.copyFile(resolvedPath, backupPath)
await rotateOldBackups(backupDir, maxBackups) await rotateOldBackups(backupDir, maxBackups)
} }
@@ -116,9 +133,10 @@ export async function writeDataFileWithRotation(filePath, content, {
} }
export async function restoreDataFileBackup(filePath, backupName, options = {}) { export async function restoreDataFileBackup(filePath, backupName, options = {}) {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
const resolvedPath = path.resolve(filePath) const resolvedPath = path.resolve(filePath)
const backupDir = getBackupDirectoryForDataFile(resolvedPath) const backupDir = getBackupDirectoryForDataFile(resolvedPath)
const sourceBackupPath = path.join(backupDir, backupName) const sourceBackupPath = resolveDataFileBackupPath(backupDir, backupName)
const backupContent = await fs.readFile(sourceBackupPath, 'utf-8') const backupContent = await fs.readFile(sourceBackupPath, 'utf-8')
return writeDataFileWithRotation(resolvedPath, backupContent, options) return writeDataFileWithRotation(resolvedPath, backupContent, options)

View File

@@ -4,6 +4,7 @@ import path from 'path'
import { promises as fs } from 'fs' import { promises as fs } from 'fs'
import { import {
getBackupDirectoryForDataFile, getBackupDirectoryForDataFile,
resolveDataFileBackupPath,
listDataFileBackups, listDataFileBackups,
restoreDataFileBackup, restoreDataFileBackup,
writeDataFileWithRotation writeDataFileWithRotation
@@ -60,7 +61,7 @@ describe('Data file rotation utility', () => {
expect(backups.length).toBe(1) expect(backups.length).toBe(1)
const backupDir = getBackupDirectoryForDataFile(dataFile) const backupDir = getBackupDirectoryForDataFile(dataFile)
const backupContent = await fs.readFile(path.join(backupDir, backups[0]), 'utf-8') const backupContent = await fs.readFile(resolveDataFileBackupPath(backupDir, backups[0]), 'utf-8')
expect(backupContent).toBe('v1') expect(backupContent).toBe('v1')
const currentContent = await fs.readFile(dataFile, 'utf-8') const currentContent = await fs.readFile(dataFile, 'utf-8')
@@ -91,7 +92,7 @@ describe('Data file rotation utility', () => {
const backupDir = getBackupDirectoryForDataFile(dataFile) const backupDir = getBackupDirectoryForDataFile(dataFile)
const backupContents = await Promise.all( const backupContents = await Promise.all(
backups.map((name) => fs.readFile(path.join(backupDir, name), 'utf-8')) backups.map((name) => fs.readFile(resolveDataFileBackupPath(backupDir, name), 'utf-8'))
) )
expect(backupContents).toEqual(expect.arrayContaining(['v2', 'v3'])) expect(backupContents).toEqual(expect.arrayContaining(['v2', 'v3']))
@@ -109,7 +110,7 @@ describe('Data file rotation utility', () => {
const resolved = await Promise.all( const resolved = await Promise.all(
beforeRestoreBackups.map(async (name) => ({ beforeRestoreBackups.map(async (name) => ({
name, name,
content: await fs.readFile(path.join(backupDir, name), 'utf-8') content: await fs.readFile(resolveDataFileBackupPath(backupDir, name), 'utf-8')
})) }))
) )
const v1Entry = resolved.find((entry) => entry.content === 'v1') const v1Entry = resolved.find((entry) => entry.content === 'v1')
@@ -124,7 +125,7 @@ describe('Data file rotation utility', () => {
const afterRestoreBackups = await listDataFileBackups(dataFile) const afterRestoreBackups = await listDataFileBackups(dataFile)
const afterContents = await Promise.all( const afterContents = await Promise.all(
afterRestoreBackups.map((name) => fs.readFile(path.join(backupDir, name), 'utf-8')) afterRestoreBackups.map((name) => fs.readFile(resolveDataFileBackupPath(backupDir, name), 'utf-8'))
) )
expect(afterContents).toContain('v3') expect(afterContents).toContain('v3')