Security Implications of the Design.md Export Feature: Path Traversal and Data Leakage Risks
The Design.md export command is intentionally read-only and deterministic, but creates potential security exposures through unsanitized file path arguments, stdout data leakage, and unprotected stdin streams that could be poisoned in CI environments.
The export command in the Design.md CLI transforms DESIGN.md files into token formats like Tailwind CSS, JSON, DTCG, and CSS variables. While the implementation follows a bounded execution flow that prevents arbitrary code execution, the security implications of its file I/O handling and output mechanisms require careful consideration when operating in shared or automated environments.
File System Exposure and Path Traversal Vulnerabilities
The export command does not sanitize or restrict the --file argument, creating a path traversal risk when processing the input DESIGN.md file. In packages/cli/src/commands/export.ts (lines 23-44), the command defines three arguments (file, format, prefix) using the citty framework, accepting any string value without validation.
When executing the export, the code calls readInput from packages/cli/src/utils.ts on line 61 of export.ts. This utility supports both file paths and the special - token for stdin input. Errors during file reading are caught as FileReadError on lines 62-67, but the underlying implementation trusts any path supplied by the user.
This design allows an attacker with influence over the --file parameter to read any readable file on the host file system. For example, supplying --file /etc/passwd or other sensitive system paths could expose critical configuration data. Users must ensure that only trusted paths are supplied, particularly in CI scripts or shared environments where argument injection is possible.
Data Confidentiality and Information Leakage
Exported token sets may embed proprietary design values including internal color codes, spacing tokens, and branding-specific styling that should remain confidential. The command writes all output to stdout on lines 82, 93, 104, and 115 of export.ts, which creates two distinct confidentiality risks.
First, if the export command runs in an environment where stdout is logged or intercepted—such as public CI/CD logs or container orchestration platforms—sensitive design data could be unintentionally exposed to unauthorized parties. Second, the JSON-encoded error messages printed during handler failures (lines 76-92 and 98-104) may leak internal file paths or system information.
Treat all export output as confidential and avoid printing tokens in publicly accessible logs. Consider piping output directly to secure destinations rather than capturing it in environment variables or log buffers.
Format Validation and Injection Prevention
The command mitigates code execution risks through strict format validation. On lines 49-55 of export.ts, the supplied --format value is checked against a closed FORMATS enum. Invalid values trigger a JSON-encoded error message and exit code 1, preventing the instantiation of unknown or malicious format handlers.
Each emitter handler—including TailwindEmitterHandler in packages/cli/src/linter/tailwind/handler.ts, TailwindV4EmitterHandler in packages/cli/src/linter/tailwind/v4/handler.ts, DtcgEmitterHandler in packages/cli/src/linter/dtcg/handler.ts, and CssVarsEmitterHandler in packages/cli/src/linter/css-vars/handler.ts—implements a pure-functional execute method. These handlers perform only data transformations and do not invoke external services or perform I/O beyond writing to stdout, limiting the attack surface to the initial file read operation.
STDIN Handling and Pipeline Risks
When using the - token to read DESIGN.md content from stdin, the command becomes vulnerable to STDIN poisoning. A malicious upstream process in a pipeline could feed crafted content designed to exploit downstream consumers of the export output, even though the lint function called on line 70 of export.ts validates syntax before processing.
The linting step itself is sandboxed and does not execute user code, but the content validation occurs after the data has already entered the pipeline. In automated workflows where stdin comes from curl commands or external sources, verify the integrity of the input stream before passing it to the export command.
Secure Usage Examples
When running the export command in production environments, use these patterns to minimize security exposure:
# Safe: Export to file directly, avoid logging stdout
designmd export my-design.md --format css-tailwind > ./secure-output/theme.css
# Risky: Capturing output in variable may expose in process lists
json=$(designmd export my-design.md --format json-tailwind)
# Safe: Pipe from trusted source with integrity checks
cat verified-design.md | designmd export - --format css-vars --prefix brand-
Summary
- Path traversal vulnerability: The
--fileargument accepts any readable path without sandboxing, potentially exposing sensitive system files if user input is not properly filtered. - Information disclosure: Exported design tokens written to stdout may leak proprietary data if captured in public logs or insecure environments.
- Format safety: A closed enum and pure-functional emitter handlers prevent arbitrary code execution and limit the attack surface to approved transformation logic.
- Pipeline risks: STDIN input mode introduces poisoning possibilities when upstream processes are untrusted or compromised.
Frequently Asked Questions
Can the export command execute arbitrary code on my system?
No. According to the Design.md source code, the export feature uses a closed FORMATS enum (lines 49-55 of packages/cli/src/commands/export.ts) to validate the --format argument, preventing instantiation of unknown handlers. The emitter handlers in packages/cli/src/linter/ are pure-functional transformations that do not invoke external processes or eval user content.
Is it safe to run the export command on untrusted DESIGN.md files?
While the linting process in packages/cli/src/linter/index.ts is sandboxed and does not execute code, you should treat the --file path as a privileged operation. The command can read any file path accessible to the process user, including system files like /etc/passwd, so ensure the path argument comes from a trusted source and cannot be manipulated by attackers.
How do I prevent sensitive design tokens from leaking in CI logs?
Avoid echoing or logging the output of the export command in public CI environments. Instead of capturing the output in a variable or printing it directly, pipe the results immediately to secure storage. For example: designmd export design.md --format json-tailwind > secure-output.json rather than json=$(designmd export design.md --format json-tailwind).
Why does the export command exit with code 0 even when linting finds issues?
As noted in the comments on lines 118-120 of packages/cli/src/commands/export.ts, successful exports intentionally exit with code 0 regardless of lint findings. This design separates lint warnings from export failures, but may mask security-relevant lint errors unless you explicitly check the lint output separately before running the export command.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →