SpecPress and SpecPressExt implement security measures to prevent unauthorized file access outside the specification root directory. This document describes the security model and validation mechanisms.
All file access must be restricted to the specification root directory and explicitly configured external locations.
The specification root (specpress.specificationRootPath) defines the trusted boundary for file operations. All relative paths in markdown files (images, JsonTable, etc.) are resolved relative to this root.
The following operations are restricted to spec root:
- Image loading - Images referenced in markdown
- JsonTable files - JSON files linked from markdown
- ASN.1 files - ASN.1 modules included in spec
- Markdown files - Specification content files
- All relative paths are resolved using
path.join(specRoot, relativePath) - Absolute paths are rejected or validated
- Path traversal (
../) is normalized and validated - Files outside spec root are not accessible
Comments are stored outside the spec root by default (as a sibling folder). This is intentional but requires explicit configuration.
- Separation of concerns - Comments are metadata, not spec content
- Git workflow - Easier to exclude from spec commits
- Multi-spec support - One comment folder can serve multiple spec roots
1. Mandatory Configuration
{
"specpress.commentFolder": "comments"
}- No default value - feature fails if not configured
- Forces users to make conscious decision
- Configuration includes security warning
2. Path Validation
// Validates path doesn't use tricks like /../../../etc/passwd
const normalized = path.normalize(commentFolder)
if (normalized !== commentFolder) {
throw new Error('Path normalization detected security issue')
}3. Clear Documentation
Configuration description includes:
- "Required for commenting feature"
- "Security note: Comments are stored outside spec root by default"
- Explanation of relative vs. absolute paths
Relative Path (Recommended):
{
"specpress.commentFolder": "comments"
}- Creates
workspace/comments/(sibling toworkspace/spec/) - User controls location via workspace structure
Absolute Path (Use with Caution):
{
"specpress.commentFolder": "/shared/project-comments"
}- Allows shared comment storage
- User takes full responsibility for security
CR cover page files are inside spec root:
- Located in
specRoot/assets/CRxxxx.json - No path traversal possible
- Strict filename pattern:
CR[x0-9]{4}.json
1. Directory Restriction
const assetsDir = path.join(specRoot, 'assets')
// Only looks in specRoot/assets/, nowhere else2. Filename Pattern
/^CR[x0-9]{4}\.json$/i
// Strict pattern prevents arbitrary file access3. File Type Validation
if (!crFilePath.toLowerCase().endsWith('.json')) {
return { errors: ['Invalid file type: must be a .json file'] }
}4. Path Normalization
const normalized = path.normalize(crFilePath)
if (normalized !== crFilePath) {
return { errors: ['Invalid file path: path traversal detected'] }
}Front page data file location is configured by user:
specpress.frontPageDatasetting- Can be inside or outside spec root
- User's responsibility to ensure safe location
Place front page data inside spec root:
{
"specpress.frontPageData": "assets/frontpage.json"
}All linked resources must be inside spec root:
- Images:
 - JsonTable:
[JsonTable](path/to/table.json)
1. Relative Path Resolution
const imagePath = path.join(specRoot, relativePath)
// Always resolved from spec root2. Existence Check
if (!fs.existsSync(imagePath)) {
// File not found - no access attempt
}3. No Absolute Paths
Absolute paths in markdown are rejected or ignored.
Mermaid SVG cache is stored outside spec root:
- Location:
cached/folder (sibling to spec root) - Contains only generated SVG files
- No user input in filenames (SHA-256 hashes)
1. Hash-Based Filenames
const hash = crypto.createHash('sha256').update(source).digest('hex')
const filename = `${hash}.svg`
// No user-controlled path components2. Automatic Cleanup
Unused cache files are automatically deleted, preventing accumulation.
When adding new file access features:
- Is the file inside spec root?
- If outside, is it explicitly configured?
- Is path normalization validated?
- Are absolute paths handled safely?
- Is there a filename/extension whitelist?
- Is the security model documented?
- Are users warned about security implications?
- Path Traversal -
../../../etc/passwdattacks prevented - Arbitrary File Read - Only spec root files accessible
- Symlink Attacks - Path normalization catches symlink tricks
- File Type Confusion - Extension validation prevents execution
- Malicious Spec Content - Users can include harmful markdown
- Resource Exhaustion - Large files can consume memory
- Zip Bombs - Compressed content not validated
- XSS in Preview - Markdown can contain scripts (VS Code webview handles this)
Users are responsible for:
- Trusting the specification content they open
- Configuring safe paths for external folders (comments, etc.)
- Not opening specifications from untrusted sources
- Understanding security implications of absolute paths
If you discover a security vulnerability:
- Do not open a public GitHub issue
- Email security concerns to: [security contact needed]
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
Security fixes are released as patch versions and documented in CHANGELOG.md with a Security section.