How to Reject a Candidate Version in Distilly: Security Validation Guide
Distilly rejects candidate versions automatically when validate_path_segment or resolve_contained_child detect unsafe path patterns, outputting errors to stderr and aborting with exit code 1.
Distilly (titanwings/distilly) is a skill storage management system designed to safeguard file system integrity. When you need to reject a candidate version in Distilly, the framework employs a multi-layered validation strategy that intercepts malicious or malformed version strings before they touch the disk. These protections are implemented in tools/version_manager.py and enforced across all version management operations.
Understanding Distilly's Version Validation Architecture
The rejection mechanism operates through defensive programming patterns that validate inputs at three distinct boundaries. According to the titanwings/distilly source code, every version candidate undergoes rigorous path segment validation to prevent directory traversal, symlink escapes, and unauthorized file system access.
The validation hierarchy checks:
- Skill slug safety before processing
- Version directory containment during resolution
- Version string integrity during backup and rollback operations
The Three Security Checkpoints for Version Rejection
Slug Validation via validate_path_segment
Before any version operation proceeds, Distilly validates the skill slug using validate_path_segment. Located at lines 94‑96 in tools/version_manager.py, this check ensures the slug contains no path traversal sequences like .. or directory separators.
If the slug contains illegal characters, the manager immediately rejects the candidate and terminates the operation, preventing the creation of or access to unsafe directory structures.
Directory Containment via resolve_contained_child
The resolve_contained_child function guarantees that resolved version paths remain strictly within the skill's versions folder. As implemented in tools/version_manager.py at lines 84‑86 (for rollback) and 45‑47 (for listing), this function raises a ValueError if the version path would escape the intended container.
This containment check effectively rejects any candidate version that attempts to reference files outside the managed skill directory, blocking path traversal attacks at the resolution stage.
Version String Sanitization
When creating backups or performing rollbacks, Distilly applies validate_path_segment directly to the version identifier. Lines 46‑48 (backup) and 102‑108 (rollback backup) in tools/version_manager.py enforce this validation, rejecting strings containing .., /, or other unsafe patterns that could manipulate the file system.
Practical Examples of Rejecting Invalid Versions
CLI Error Output for Unsafe Version Strings
When you attempt to back up a skill with a compromised version identifier, Distilly detects the violation and rejects the operation:
# Attempt to back up a skill with an unsafe version string
distilly version-manager \
--action backup \
--slug my-skill \
--character colleague \
--base-dir ./skill-storage
If the current meta.json reports a version like ../evil, the tool prints to stderr and exits:
error: version string contains unsafe path segment: ../evil
The process terminates with status 1, rejecting the candidate version before any file operations occur.
Blocking Directory Traversal in Rollbacks
Explicit attempts to escape the versions directory trigger immediate rejection:
# Attempting to roll back to a version outside the versions folder
distilly version-manager \
--action rollback \
--slug my-skill \
--version ../../outside \
--character colleague
The manager detects the traversal attempt and outputs:
error: version contains unsafe path segment: ../../outside
The function returns false, preventing the rollback operation from executing.
Programmatic Version Validation
You can leverage Distilly's validation functions directly in Python scripts to reject candidates before processing:
from pathlib import Path
from tools.version_manager import rollback
from tools.skill_schema import validate_path_segment
try:
# Returns clean string or raises ValueError
safe_version = validate_path_segment(
"../../bad",
"candidate version"
)
success = rollback(
Path("./skill-storage/colleague/my-skill"),
safe_version
)
except ValueError as e:
# Handle rejected candidate
print(f"Rejected candidate version: {e}")
exit(1)
Summary
- Multi-layer validation: Distilly rejects candidate versions through
validate_path_segment(lines 94‑96, 46‑48, 102‑108) andresolve_contained_child(lines 84‑86, 45‑47) intools/version_manager.py. - Path traversal prevention: The system blocks
..,/, and other unsafe patterns in both skill slugs and version identifiers. - Immediate termination: Rejected operations print errors to stderr and exit with status 1, ensuring no unsafe file system modifications occur.
- Container enforcement:
resolve_contained_childguarantees version paths remain within the designatedversionsdirectory.
Frequently Asked Questions
What triggers a candidate version rejection in Distilly?
Distilly rejects candidate versions containing path traversal sequences (..), forward slashes, or other characters that could escape the intended directory structure. The validate_path_segment function raises a ValueError when it detects these patterns in either the skill slug or version string, causing the version manager to abort the operation immediately.
Which functions handle version validation in the source code?
The primary validation functions are validate_path_segment (located in tools/skill_schema.py and invoked throughout tools/version_manager.py) and resolve_contained_child (defined in tools/version_manager.py). These functions check path segments at lines 94‑96, 46‑48, and 102‑108, and resolve contained paths at lines 84‑86 and 45‑47 respectively.
How does Distilly prevent directory traversal attacks?
Distilly prevents directory traversal through the resolve_contained_child function, which verifies that resolved paths remain strictly within the skill's versions folder. If a version string attempts to reference parent directories (e.g., ../../outside), the function raises a ValueError, effectively rejecting the candidate before any file system access occurs.
What exit code does Distilly return when rejecting a version?
When Distilly rejects a candidate version due to validation failures, the CLI tool prints an error message to stderr and exits with status 1. This non-zero exit code allows shell scripts and CI/CD pipelines to detect and handle rejection events programmatically.
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 →