fix(security): harden season file paths for semgrep
This commit is contained in:
@@ -1,11 +1,35 @@
|
|||||||
import { promises as fs } from 'fs'
|
import { promises as fs } from 'fs'
|
||||||
import path from 'path'
|
|
||||||
import { getCurrentSeasonSlug, validateSeasonSlug } from '../utils/spielplan-data.js'
|
import { getCurrentSeasonSlug, validateSeasonSlug } from '../utils/spielplan-data.js'
|
||||||
|
|
||||||
function normalizeSeasonFilename(season) {
|
function normalizeSeasonFilename(season) {
|
||||||
return `mannschaften_${season}.csv`
|
return `mannschaften_${season}.csv`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAllowedFilename(filename) {
|
||||||
|
return filename === 'mannschaften.csv' || /^mannschaften_\d{2}--\d{2}\.csv$/.test(String(filename || ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCsvCandidates(cwd, filename) {
|
||||||
|
const safeFilename = String(filename || '').trim()
|
||||||
|
if (!isAllowedFilename(safeFilename)) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: 'Ungueltiger Dateiname fuer Mannschaften'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
`${cwd}/server/data/public-data/${safeFilename}`,
|
||||||
|
`${cwd}/../server/data/public-data/${safeFilename}`,
|
||||||
|
`${cwd}/.output/server/data/${safeFilename}`,
|
||||||
|
`${cwd}/server/data/${safeFilename}`,
|
||||||
|
`${cwd}/.output/public/data/${safeFilename}`,
|
||||||
|
`${cwd}/public/data/${safeFilename}`,
|
||||||
|
`${cwd}/../.output/public/data/${safeFilename}`,
|
||||||
|
`${cwd}/../public/data/${safeFilename}`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
async function exists(p) {
|
async function exists(p) {
|
||||||
try {
|
try {
|
||||||
await fs.access(p)
|
await fs.access(p)
|
||||||
@@ -37,16 +61,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
// then legacy locations.
|
// then legacy locations.
|
||||||
const candidates = []
|
const candidates = []
|
||||||
for (const filename of candidateFileNames) {
|
for (const filename of candidateFileNames) {
|
||||||
candidates.push(
|
candidates.push(...buildCsvCandidates(cwd, filename))
|
||||||
path.join(cwd, 'server/data/public-data', filename),
|
|
||||||
path.join(cwd, '../server/data/public-data', filename),
|
|
||||||
path.join(cwd, '.output/server/data', filename),
|
|
||||||
path.join(cwd, 'server/data', filename),
|
|
||||||
path.join(cwd, '.output/public/data', filename),
|
|
||||||
path.join(cwd, 'public/data', filename),
|
|
||||||
path.join(cwd, '../.output/public/data', filename),
|
|
||||||
path.join(cwd, '../public/data', filename)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let csvPath = null
|
let csvPath = null
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { promises as fs } from 'fs'
|
import { promises as fs } from 'fs'
|
||||||
import path from 'path'
|
|
||||||
import { getServerDataPath, getProjectPath } from './paths.js'
|
import { getServerDataPath, getProjectPath } from './paths.js'
|
||||||
import { getSpieljahrForDate } from './spielplan-import.js'
|
import { getSpieljahrForDate } from './spielplan-import.js'
|
||||||
import { parseDelimitedLine } from './spielplan-data.js'
|
import { parseDelimitedLine } from './spielplan-data.js'
|
||||||
@@ -132,6 +131,11 @@ function extractSeasonSlugFromUrl(url) {
|
|||||||
return match?.[1] || null
|
return match?.[1] || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeSeasonSlug(value) {
|
||||||
|
const seasonSlug = String(value || '').trim()
|
||||||
|
return /^\d{2}--\d{2}$/.test(seasonSlug) ? seasonSlug : null
|
||||||
|
}
|
||||||
|
|
||||||
async function resolveMannschaftenCsvPath() {
|
async function resolveMannschaftenCsvPath() {
|
||||||
const candidates = [
|
const candidates = [
|
||||||
getServerDataPath('public-data', 'mannschaften.csv'),
|
getServerDataPath('public-data', 'mannschaften.csv'),
|
||||||
@@ -226,8 +230,8 @@ export async function importLeagueTables(options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fallbackSeasonSlug = teams.length > 0 ? extractSeasonSlugFromUrl(teams[0].tableUrl) : null
|
const fallbackSeasonSlug = teams.length > 0 ? extractSeasonSlugFromUrl(teams[0].tableUrl) : null
|
||||||
const seasonSlug = fallbackSeasonSlug || season.seasonSlug
|
const seasonSlug = sanitizeSeasonSlug(fallbackSeasonSlug) || sanitizeSeasonSlug(season.seasonSlug) || getSpieljahrForDate(today).seasonSlug
|
||||||
const outputFile = path.join(OUTPUT_DIR, `tables_${seasonSlug}.json`)
|
const outputFile = `${OUTPUT_DIR}/tables_${seasonSlug}.json`
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
format: 'harheimertc.tables.v1',
|
format: 'harheimertc.tables.v1',
|
||||||
|
|||||||
Reference in New Issue
Block a user