Enhance security by adding role-checking functions in ESLint configuration and updating Vue components to improve content sanitization comments, while refining error handling in API endpoints for better clarity.
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 3m40s

This commit is contained in:
Torsten Schulz (local)
2025-12-20 14:19:55 +01:00
parent 19024cd87e
commit e128e1a77c
22 changed files with 40 additions and 25 deletions

View File

@@ -32,12 +32,16 @@ export default [
'setCookie': 'readonly',
'deleteCookie': 'readonly',
'getHeader': 'readonly',
'setHeader': 'readonly',
'getRouterParam': 'readonly',
'getQuery': 'readonly',
'sendStream': 'readonly',
'sendRedirect': 'readonly',
'createError': 'readonly',
'useRuntimeConfig': 'readonly',
'hasAnyRole': 'readonly',
'hasRole': 'readonly',
'hasAllRoles': 'readonly',
'process': 'readonly',
// Vue Composition API
'onUnmounted': 'readonly',

View File

@@ -166,9 +166,10 @@
Keine Empfänger gefunden
</span>
</div>
<!-- nosemgrep: javascript.vue.security.audit.xss.templates.avoid-v-html -->
<div
class="text-sm text-gray-600 prose prose-sm max-w-none mb-3"
v-html="useSanitizeHtml(post.content.substring(0, 200) + (post.content.length > 200 ? '...' : ''))" <!-- nosemgrep: javascript.vue.security.audit.xss.templates.avoid-v-html -->
v-html="useSanitizeHtml(post.content.substring(0, 200) + (post.content.length > 200 ? '...' : ''))"
/>
<!-- Empfängerliste (collapsible) -->

View File

@@ -29,7 +29,7 @@ async function loadConfig() {
try {
const data = await $fetch('/api/config')
rawContent.value = data?.seiten?.geschichte || ''
} catch (e) {
} catch (_e) {
rawContent.value = ''
}
}

View File

@@ -64,7 +64,7 @@ async function loadConfig() {
rawContent.value = satzung.content || ''
pdfUrl.value = satzung.pdfUrl || ''
}
} catch (e) {
} catch (_e) {
rawContent.value = ''
pdfUrl.value = ''
}

View File

@@ -4,9 +4,10 @@
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-6">
TT-Regeln
</h1>
<!-- nosemgrep: javascript.vue.security.audit.xss.templates.avoid-v-html -->
<div
class="prose prose-lg max-w-none"
v-html="content" <!-- nosemgrep: javascript.vue.security.audit.xss.templates.avoid-v-html -->
v-html="content"
/>
</div>
</div>
@@ -28,7 +29,7 @@ async function loadConfig() {
try {
const data = await $fetch('/api/config')
rawContent.value = data?.seiten?.ttRegeln || ''
} catch (e) {
} catch (_e) {
rawContent.value = ''
}
}

View File

@@ -4,9 +4,10 @@
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-6">
Über uns
</h1>
<!-- nosemgrep: javascript.vue.security.audit.xss.templates.avoid-v-html -->
<div
class="prose prose-lg max-w-none"
v-html="content" <!-- nosemgrep: javascript.vue.security.audit.xss.templates.avoid-v-html -->
v-html="content"
/>
</div>
</div>
@@ -28,7 +29,7 @@ async function loadConfig() {
try {
const data = await $fetch('/api/config')
rawContent.value = data?.seiten?.ueberUns || ''
} catch (e) {
} catch (_e) {
rawContent.value = ''
}
}

View File

@@ -52,7 +52,7 @@ function getDataPath(filename) {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}
@@ -125,6 +125,7 @@ async function reencryptUsers(backupDir, oldKeys) {
const data = await fs.readFile(USERS_FILE, 'utf-8')
// Backup erstellen
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
await fs.copyFile(USERS_FILE, path.join(backupDir, 'users.json')) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
console.log('✅ Backup von users.json erstellt')
@@ -167,6 +168,7 @@ async function reencryptMembers(backupDir, oldKeys) {
const data = await fs.readFile(MEMBERS_FILE, 'utf-8')
// Backup erstellen
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
await fs.copyFile(MEMBERS_FILE, path.join(backupDir, 'members.json')) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
console.log('✅ Backup von members.json erstellt')
@@ -217,7 +219,8 @@ async function reencryptMembershipApplications(backupDir, oldKeys) {
let skipped = 0
for (const file of files) {
const filePath = path.join(MEMBERSHIP_APPLICATIONS_DIR, file)
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
const filePath = path.join(MEMBERSHIP_APPLICATIONS_DIR, file)
const stat = await fs.stat(filePath)
if (stat.isDirectory()) {
@@ -226,6 +229,7 @@ async function reencryptMembershipApplications(backupDir, oldKeys) {
try {
// Backup erstellen
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
const backupPath = path.join(backupDir, 'membership-applications', file) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
await fs.mkdir(path.dirname(backupPath), { recursive: true })
await fs.copyFile(filePath, backupPath)

View File

@@ -20,7 +20,7 @@ const getDataPath = (filename) => {
}
// In development, working dir is project root
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -57,7 +57,7 @@ export default defineEventHandler(async (event) => {
filePath = path.join(cwd, '../public/data', filename)
} else {
// In development, working dir is project root
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
filePath = path.join(cwd, 'public/data', filename)
}

View File

@@ -11,7 +11,7 @@ const getDataPath = (filename) => {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -12,7 +12,7 @@ const getDataPath = (filename) => {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -11,7 +11,7 @@ const getDataPath = (filename) => {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -14,7 +14,7 @@ const getDataPath = (filename) => {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -313,7 +313,7 @@ function getDataPath(filename) {
// In der Produktion: process.cwd() ist .output, daher ein Verzeichnis zurück
const isDev = process.env.NODE_ENV === 'development'
const projectRoot = isDev ? process.cwd() : path.resolve(process.cwd(), '..')
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(projectRoot, 'server', 'data', filename)
}

View File

@@ -39,8 +39,10 @@ export default defineEventHandler(async (event) => {
})
}
const dataDir = path.join(process.cwd(), 'server/data/membership-applications') // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
const filePath = path.join(dataDir, `${id}.json`) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
const dataDir = path.join(process.cwd(), 'server/data/membership-applications')
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
const filePath = path.join(dataDir, `${id}.json`)
// Antrag laden
const fileContent = await fs.readFile(filePath, 'utf8')

View File

@@ -14,7 +14,7 @@ const getDataPath = (filename) => {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -53,7 +53,8 @@ export default defineEventHandler(async (event) => {
filePath = path.join(process.cwd(), 'public', 'documents', 'spielplaene', 'spielplan_gesamt.pdf')
} else {
// Für vordefinierte PDFs
filePath = path.join(process.cwd(), 'public', 'documents', 'spielplaene', sanitizedFilename) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
filePath = path.join(process.cwd(), 'public', 'documents', 'spielplaene', sanitizedFilename)
}
// Prüfe ob Datei existiert

View File

@@ -361,7 +361,8 @@ ${hallenListe.map(halle => {
// Verzeichnis existiert bereits
}
const tempTexFile = path.join(tempDir, `spielplan_${team}_${Date.now()}.tex`) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
const tempTexFile = path.join(tempDir, `spielplan_${team}_${Date.now()}.tex`)
await fs.writeFile(tempTexFile, latexContent, 'utf-8')
// Kompiliere LaTeX zu PDF

View File

@@ -16,7 +16,7 @@ const getDataPath = (filename) => {
}
// In development, working dir is project root
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -15,7 +15,7 @@ const getDataPath = (filename) => {
}
// In development, working dir is project root
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -13,7 +13,7 @@ const getDataPath = (filename) => {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, '../server/data', filename)
}
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'server/data', filename)
}

View File

@@ -14,7 +14,7 @@ const getDataPath = (filename) => {
}
// In development, working dir is project root
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
return path.join(cwd, 'public/data', filename)
}