Back to skill

Security audit

Sap Journal Auditor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it asks for more authority than it needs and handles sensitive financial exports with unsafe persistent output and parsing risks.

Review before installing in environments with sensitive SAP exports. Use it only in a sandboxed workspace with limited filesystem access, remove shell permission if possible, avoid opening generated CSVs from untrusted source data until formula-neutralization is fixed, and verify dependency updates or compensating size/time limits for spreadsheet parsing.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T01 · Skill Instruction Hijacking

Error
Location
lib/reporter.js:227
Finding
Mandatory Third-Party Branding and External Links in Generated Reports<![CDATA[ ## Vulnerability Details **File Location**: `lib/reporter.js:227-243` **Vulnerability Type**: Persistent output manipulation **Risk Level**: High ### Vulnerable Code ```javascript return `# ${t.title} _${t.subtitle}_ --- ## ${lang === "de" ? "Metadaten" : "Metadata"} | | | |---|---| | **${t.meta_date}** | ${now} | | **${t.meta_period}** | ${period || t.all_periods} | | **${t.meta_entries}** | ${entries.length} | | **${t.meta_findings}** | ${findings.length} | | **${t.meta_risk}** | ${riskEmoji[overallRisk]} ${riskLabel[lang][overallRisk]} | --- ## ${t.sec_exec} ${execSummary} --- ## ${t.sec_summary} ${summaryTable} --- ## ${t.sec_findings} ${findingsSection} --- ## ${t.sec_recs} ${recsSection} --- ## ${t.sec_footer} _${t.footer_text}_ --- *SAP Journal Auditor v1.0.0 — [github.com/dda-oo/sap-journal-auditor](https://github.com/dda-oo/sap-journal-auditor) — Built by [RadarRoster](https://radarroster.com)* `; ``` ### Technical Analysis Every generated audit memo receives a fixed branded subtitle, disclaimer, repository link, and business link. This behavior is unconditional and is not required to parse journals, identify anomalies, or deliver audit findings. The fixed content controls part of the final artifact independently of the user's requested report content. It therefore acts as persistent output manipulation and exceeds the minimum behavior necessary for the declared auditing functionality. The links themselves do not download or execute code. In particular, the static pre-scan signal at `README.md:10` is an ordinary GitHub hyperlink containing an `img.shields.io` badge image, not an executable download. The issue here is mandatory insertion into generated user deliverables, not remote payload execution. ### Attack Path 1. A user uploads a journal file and requests an audit. 2. The handler invokes `generateMemo`. 3. The renderer unconditionally inserts third-party branding and external links. 4. The resulting branded me ...[truncated 460 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove mandatory business branding and promotional links from generated reports. - Keep security-relevant disclaimers, but make attribution optional and clearly separate from audit content. - Add a user-controlled option such as `includeAttribution`, defaulting to `false`. - If attribution is legally required by the license, include only the minimum required notice and avoid promotional language. - Add tests confirming that report generation does not inject external links unless explicitly requested. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
lib/exporter.js:14
Finding
Spreadsheet Formula Injection in Flagged CSV Exports<![CDATA[ ## Vulnerability Details **File Location**: `lib/exporter.js:14-21, 45-65` **Vulnerability Type**: CSV/spreadsheet formula injection **Risk Level**: High ### Vulnerable Code ```javascript function escapeCSV(val) { if (val === null || val === undefined) return ""; const str = String(val); if (str.includes(",") || str.includes('"') || str.includes("\n")) { return `"${str.replace(/"/g, '""')}"`; } return str; } ``` ```javascript const rows = findings.map((f) => [ f.id, f.risk, f.checkType, (f.docNumbers || []).join("; "), f.amount !== undefined ? String(f.amount) : "", f.currency || "", f.account || "", f.costCenter || "", fmtDate(f.postingDate), f.user || "", f.description || "", f.recommendation || "", ]); const lines = [headers.map(escapeCSV).join(","), ...rows.map((r) => r.map(escapeCSV).join(","))]; fs.writeFileSync(outputPath, lines.join("\n"), "utf8"); ``` ### Technical Analysis The exporter correctly escapes commas, quotes, and newlines, but it does not neutralize values interpreted as formulas by spreadsheet applications. Journal-controlled fields—including document numbers, currency, account, cost center, user, and values embedded into descriptions—can reach the CSV export. Cells beginning with characters such as `=`, `+`, `-`, or `@` may be evaluated as formulas when the generated file is opened in Microsoft Excel, LibreOffice Calc, or similar software. CSV quoting alone does not reliably prevent formula evaluation. ### Attack Path 1. An attacker supplies or influences a journal field with a value such as `=HYPERLINK("https://attacker.example","Review document")`. 2. The parser preserves the value as a string. 3. An audit finding copies the field into `docNumbers`, `account`, `costCenter`, `user`, or a description. 4. `escapeCSV` performs syntactic CSV quoting but does not neutralize the leading formula character. 5. A reviewer opens `flagged_entries.csv` in spreadsheet software. 6. The spreadsh ...[truncated 514 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Neutralize cells whose first non-whitespace character is `=`, `+`, `-`, `@`, tab, carriage return, or other formula-triggering characters. - Prefix dangerous values with a single quote or another spreadsheet-safe neutralization marker before CSV quoting. - Apply neutralization to every string column, including values embedded in descriptions. - Consider exporting a format with explicit string cell types when spreadsheet interoperability is required. - Document that exports contain untrusted source data. - Add tests covering formulas with leading whitespace, tabs, line breaks, and each common formula prefix. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
skill.json:34
Finding
Unnecessary Shell Permission Violates Least Privilege<![CDATA[ ## Vulnerability Details **File Location**: `skill.json:34` **Vulnerability Type**: Excessive tool capability **Risk Level**: Medium ### Vulnerable Code ```json "tools": ["file_read", "shell", "memory"], ``` ### Technical Analysis The manifest grants the Skill access to a general-purpose shell. The reviewed implementation performs parsing, analysis, report generation, and output writing through Node.js APIs and contains no legitimate shell invocation. A shell is substantially more powerful than the declared auditing task requires. Granting it increases the consequences of prompt injection, malicious instruction changes, dependency compromise, or future implementation defects. The `memory` capability is used only to store bounded run metadata, while file access is needed for uploaded journals and generated reports. The shell capability has no corresponding use in the reviewed execution path. ### Attack Path 1. The Skill is loaded with shell access enabled. 2. A malicious or compromised instruction path causes the Agent to invoke the available shell tool. 3. Commands execute with the permissions of the Agent runtime. 4. The resulting access may extend beyond journal parsing and reporting. No direct shell-exploitation instruction was found in the current repository. This finding concerns unnecessary privilege exposure rather than confirmed command execution. ### Impact Assessment If abused, shell access could permit reading or modifying any files available to the runtime, executing installed programs, initiating network connections where permitted, or interfering with other processes. The exact scope is limited by the operating-system account, container, sandbox, and network policy hosting the Agent. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `shell` from the declared tool list. - Retain only the minimum file capability needed to read the uploaded journal and write controlled output files. - Retain `memory` only if audit-run history is a required feature; otherwise remove it as well. - Enforce tool permissions at runtime rather than relying only on manifest declarations. - Add a regression check that rejects future manifest changes introducing unused high-risk capabilities. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
lib/xlsx-lite.js:20
Finding
Unbounded Synchronous XLSX Decompression Enables Denial of Service<![CDATA[ ## Vulnerability Details **File Location**: `lib/xlsx-lite.js:20-43, 121-142` **Vulnerability Type**: Unbounded archive decompression and resource consumption **Risk Level**: Medium ### Vulnerable Code ```javascript function readZipEntry(buffer, targetName) { let offset = 0; while (offset < buffer.length - 30) { const sig = buffer.readUInt32LE(offset); if (sig !== 0x04034b50) break; const flags = buffer.readUInt16LE(offset + 6); const compression = buffer.readUInt16LE(offset + 8); const compressedSize = buffer.readUInt32LE(offset + 18); const fileNameLength = buffer.readUInt16LE(offset + 26); const extraLength = buffer.readUInt16LE(offset + 28); const fileName = buffer.slice(offset + 30, offset + 30 + fileNameLength).toString("utf8"); const dataOffset = offset + 30 + fileNameLength + extraLength; if (fileName === targetName) { const compressedData = buffer.slice(dataOffset, dataOffset + compressedSize); if (compression === 0) return compressedData; if (compression === 8) return zlib.inflateRawSync(compressedData); } offset = dataOffset + compressedSize; if (flags & 0x8) offset += 16; } return null; } ``` ```javascript function readExcel(filePath) { const buffer = fs.readFileSync(filePath); const ssRaw = readZipEntry(buffer, "xl/sharedStrings.xml"); const sharedStrings = ssRaw ? parseSharedStrings(ssRaw.toString("utf8")) : []; const wbRaw = readZipEntry(buffer, "xl/workbook.xml"); if (!wbRaw) throw new Error("Invalid XLSX: workbook.xml not found"); const wbXml = wbRaw.toString("utf8"); const sheetMatch = wbXml.match(/<sheet[^>]*name="([^"]*)"[^>]*r:id="([^"]*)"/); if (!sheetMatch) throw new Error("No sheets found in workbook"); ``` ### Technical Analysis The fallback XLSX reader loads the complete uploaded file into memory and synchronously inflates selected ZIP entries. It does not enforce: - A maximum uploaded-file size. - A maximum expanded-entr ...[truncated 1334 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject files exceeding a strict compressed-size limit before parsing. - Enforce maximum expanded sizes and compression ratios for every ZIP entry. - Cap worksheet rows, columns, cells, shared strings, and total parsed characters. - Avoid synchronous decompression on the main event loop; parse in a bounded worker or isolated process. - Apply memory, CPU, and execution-time limits to the parser process. - Validate ZIP offsets and lengths before slicing buffers. - Add tests using oversized worksheets, malformed ZIP headers, and high-compression-ratio archives. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:85
Finding
Predictable Output Paths Permit File Overwrite and Symlink Redirection<![CDATA[ ## Vulnerability Details **File Location**: `index.js:85-92` **Vulnerability Type**: Unsafe output-file creation **Risk Level**: Medium ### Vulnerable Code ```javascript const memoPath = path.join(path.dirname(filePath), "audit_memo.md"); const memoContent = generateMemo(auditResults, entries, { language, period }); fs.writeFileSync(memoPath, memoContent, "utf8"); // ── 5. Export flagged CSV ─────────────────────────────────────────────── const csvPath = path.join(path.dirname(filePath), "flagged_entries.csv"); exportFlaggedCSV(auditResults.findings, csvPath); ``` The exporter ultimately performs another unrestricted write: ```javascript fs.writeFileSync(outputPath, lines.join("\n"), "utf8"); ``` ### Technical Analysis The handler always creates two fixed filenames in the input file's directory. It neither uses exclusive creation nor verifies that the destinations are regular files. Existing files are silently truncated. If an attacker can control or prepopulate the input directory, a symbolic link named `audit_memo.md` or `flagged_entries.csv` can redirect the write to another path accessible to the Agent process. Even without symlinks, legitimate files with those names are overwritten. ### Attack Path 1. An attacker obtains write access to the directory containing the uploaded journal. 2. The attacker creates `audit_memo.md` or `flagged_entries.csv` as an existing file or symbolic link to another writable target. 3. A journal audit is initiated. 4. `writeFileSync` follows the destination and truncates or replaces its content. 5. The Skill then attempts to return the resulting path to the user. Exploitation requires control over the input directory or a race-capable shared directory. The reachable target remains constrained by the filesystem permissions of the Agent process. ### Impact Assessment Potential impact includes destruction of existing files and modification of arbitrary writable targets through symbolic links. If the runtime ...[truncated 173 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Write outputs to a dedicated, runtime-controlled directory rather than beside the uploaded file. - Generate unpredictable per-run filenames. - Create files with exclusive semantics such as the `wx` flag. - Use `lstat` and platform-appropriate safe-open controls to reject symbolic links. - Restrict output-directory permissions to the Agent process. - Avoid replacing existing files unless the user explicitly authorizes it. - Clean up generated artifacts after delivery according to a defined retention policy. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (15)

Known Vulnerable Dependency: brace-expansion==1.1.12 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
94% confidence
Finding
brace-expansion 1.1.12 is flagged with multiple DoS advisories affecting expansion parsing. Although it appears only as a transitive devDependency here, vulnerable globbing/parsing logic can still be exercised during local tooling or CI runs, causing hangs or memory exhaustion if attacker-controlled patterns are processed.

Known Vulnerable Dependency: flatted==3.3.4 — 2 advisory(ies): CVE-2026-32141 (flatted vulnerable to unbounded recursion DoS in parse() revive phase); CVE-2026-33228 (Prototype Pollution via parse() in NodeJS flatted)

High
Category
Supply Chain
Confidence
87% confidence
Finding
flatted 3.3.4 is reported vulnerable to prototype pollution and unbounded recursion during parse revival. In this lockfile it is only present as a transitive devDependency, which lowers exposure, but a vulnerable parser in tooling can still be abused in development or CI contexts if attacker-controlled serialized input is processed.

Known Vulnerable Dependency: js-yaml==4.1.1 — 4 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); CVE-2026-59869 (js-yaml: YAML merge-key chains can force quadratic CPU consumption); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026) +1 more

High
Category
Supply Chain
Confidence
89% confidence
Finding
js-yaml 4.1.1 has multiple CPU consumption advisories related to merge-key and mapping resolution. Here it is a transitive devDependency of eslint tooling rather than application runtime code, which reduces but does not eliminate risk because malicious YAML config processed in CI or local lint workflows could trigger denial of service.

Known Vulnerable Dependency: xlsx==0.18.5 — 2 advisory(ies): CVE-2023-30533 (Prototype Pollution in sheetJS); CVE-2024-22363 (SheetJS Regular Expression Denial of Service (ReDoS))

High
Category
Supply Chain
Confidence
97% confidence
Finding
xlsx 0.18.5 is a direct runtime dependency with known prototype pollution and ReDoS issues. In the context of a journal-auditing skill that likely ingests user-supplied spreadsheet files, this is especially relevant because crafted XLSX content could cause process slowdown, denial of service, or unsafe object mutation during parsing.

Known Vulnerable Dependency: xlsx==0.18.5 — 2 advisory(ies): CVE-2023-30533 (Prototype Pollution in sheetJS); CVE-2024-22363 (SheetJS Regular Expression Denial of Service (ReDoS))

High
Category
Supply Chain
Confidence
98% confidence
Finding
xlsx 0.18.5 is affected by known issues including prototype pollution and regular-expression denial of service. This is especially relevant here because the skill is explicitly designed to ingest spreadsheet-based journal entry exports; a malicious or crafted workbook from an untrusted source could crash processing, hang the audit workflow, or corrupt application logic through polluted objects.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## Unacceptable Behavior

- Harassment, personal attacks, or discrimination.
- Sharing private information without consent.
- Disruptive or off-topic behavior.

## Enforcement
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The trigger phrase 'review postings' is broad enough to match ordinary accounting requests that may not specifically intend to invoke this skill. That can cause unintended activation, especially in enterprise environments where users commonly discuss reviewing postings in routine workflows.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The phrase 'flag anomalies' is highly generic and lacks any SAP, journal, or finance-specific constraint. Because many unrelated tasks may ask to 'flag anomalies,' this creates a higher risk of accidental invocation and misrouting of user requests to the skill.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill writes audit artifacts derived from SAP journal data directly to disk in the same directory as the uploaded file, creating `audit_memo.md` and `flagged_entries.csv` without explicit user consent or any retention/cleanup controls. Because SAP FI/CO exports commonly contain sensitive financial and user activity data, these files can persist on shared hosts, agent workspaces, or backup systems and be exposed to other users or processes.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The manifest trigger list includes generic phrases such as "audit journal," "check journal entries," "review postings," and "flag anomalies" without narrowing context or exclusion conditions. In a manifest file, these broad activators can cause unintended invocation because they are not specific to this exact skill or usage boundary.

Known Vulnerable Dependency: csv-parse==5.6.0 — 1 advisory(ies): CVE-2026-85063 (node-csv: Prototype replacement still reachable via columns path)

Low
Category
Supply Chain
Confidence
78% confidence
Finding
csv-parse 5.6.0 is a direct runtime dependency and is reported vulnerable to prototype manipulation via a columns-related parsing path. Because this skill appears to process CSV input, untrusted spreadsheet data could potentially trigger object-shape corruption or downstream logic issues if unsafe parsing options are used.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "homepage": "https://github.com/dda-oo/sap-journal-auditor#readme",
  "dependencies": {
    "csv-parse": "^5.5.0",
    "xlsx": "^0.18.5"
  },
  "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Known Vulnerable Dependency: csv-parse==5.6.0 — 1 advisory(ies): CVE-2026-85063 (node-csv: Prototype replacement still reachable via columns path)

Low
Category
Supply Chain
Confidence
71% confidence
Finding
The allowed csv-parse version includes a release with a reported prototype replacement issue reachable via the `columns` path. In a skill that audits SAP journal exports from CSV files, parsing attacker-controlled or untrusted CSV input could let malformed data tamper with object behavior, potentially causing logic corruption or unsafe downstream handling.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"homepage": "https://github.com/dda-oo/sap-journal-auditor#readme",
  "dependencies": {
    "csv-parse": "^5.5.0",
    "xlsx": "^0.18.5"
  },
  "devDependencies": {
    "eslint": "^8.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"xlsx": "^0.18.5"
  },
  "devDependencies": {
    "eslint": "^8.0.0"
  },
  "engines": {
    "node": ">=16.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.