fix(security): resolve code scanning alerts
Add least-privilege 'permissions: contents: read' to quality, security and docker-ci workflows (actions/missing-workflow-permissions). Harden markdown table cell escaping in exportMarkdown: escape backslash before pipe and collapse newlines so untrusted values can't break the table (js/incomplete-sanitization). Add regression tests. ha-relevant: maybe
This commit is contained in:
@@ -6,6 +6,9 @@ on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
smoke-and-integration:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -6,6 +6,9 @@ on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint-scripts:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1' # Weekly on Monday
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
secrets-scan:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -53,6 +53,33 @@ describe('generateMarkdownTable', () => {
|
||||
expect(md).toContain('A\\|B')
|
||||
})
|
||||
|
||||
it('escapes backslashes before pipes so the escape char is not ambiguous', () => {
|
||||
const nodes = [makeNode({ label: 'A\\|B' })]
|
||||
const md = generateMarkdownTable(nodes)
|
||||
// backslash doubled, then the literal pipe escaped
|
||||
expect(md).toContain('A\\\\\\|B')
|
||||
})
|
||||
|
||||
it('collapses newlines in cell values so they do not break the table', () => {
|
||||
const nodes = [makeNode({ label: 'line1\nline2', hostname: 'a\r\nb' })]
|
||||
const lines = generateMarkdownTable(nodes).split('\n')
|
||||
// header + separator + exactly one data row (no extra line from the value)
|
||||
expect(lines).toHaveLength(3)
|
||||
expect(lines[2]).toContain('line1 line2')
|
||||
expect(lines[2]).toContain('a b')
|
||||
})
|
||||
|
||||
it('escapes pipe characters inside service names', () => {
|
||||
const nodes = [makeNode({
|
||||
label: 'Server',
|
||||
services: [{ port: 80, protocol: 'tcp', service_name: 'web|proxy' }],
|
||||
})]
|
||||
const lines = generateMarkdownTable(nodes).split('\n')
|
||||
// header + separator + exactly one data row — the pipe must not add a column
|
||||
expect(lines).toHaveLength(3)
|
||||
expect(lines[2]).toContain('web\\|proxy')
|
||||
})
|
||||
|
||||
it('generates one row per non-groupRect node', () => {
|
||||
const nodes = [
|
||||
makeNode({ type: 'server', label: 'A' }, '1'),
|
||||
|
||||
@@ -5,8 +5,11 @@ const EMPTY = '—'
|
||||
|
||||
function cell(v: string | null | undefined): string {
|
||||
if (!v) return EMPTY
|
||||
// Escape pipe chars so they don't break the table
|
||||
return v.replace(/\|/g, '\\|')
|
||||
// Escape backslashes first, then pipes, and collapse newlines so they don't break the table
|
||||
return v
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/\|/g, '\\|')
|
||||
.replace(/\r?\n/g, ' ')
|
||||
}
|
||||
|
||||
export function generateMarkdownTable(nodes: Node<NodeData>[]): string {
|
||||
@@ -27,7 +30,7 @@ export function generateMarkdownTable(nodes: Node<NodeData>[]): string {
|
||||
cell(d.ip),
|
||||
cell(d.hostname),
|
||||
cell(d.status),
|
||||
services,
|
||||
cell(services),
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user