What Does the sanitize.py Script Do in book-to-skill? A Security-Focused Text Sanitization Guide
The sanitize.py script removes invisible Unicode characters from extracted text to prevent document-borne prompt injection attacks.
The sanitize.py script in the virgiliojr94/book-to-skill repository implements the deterministic text-sanitization layer that secures the pipeline against Trojan-Source style attacks and hidden payload injection. Located at book_to_skill/sanitize.py, this module defines the authoritative set of invisible codepoints that must be stripped from parsed documents before the content reaches downstream language models or generated skills.
Core Responsibilities of sanitize.py
The script operates as a defensive security barrier with three primary responsibilities: defining dangerous Unicode ranges, exposing detection predicates, and providing the public sanitization API.
Defining Invisible Unicode Attack Vectors
At the top of book_to_skill/sanitize.py (lines 4-100), the script declares comprehensive sets of invisible or misleading Unicode codepoints that can be weaponized for prompt injection:
_ZERO_WIDTH_CODEPOINTS– zero-width spaces and non-joiners that hide text in plain sight_BIDI_CONTROL_CODEPOINTS– bidirectional text controls that can reorder displayed text_INVISIBLE_LETTER_CODEPOINTS– non-spacing marks and variation selectors_ANNOTATION_FORMAT_CODEPOINTS– deprecated annotation formats_TAG_BLOCK_START/END– language tag block delimiters_VARIATION_SELECTOR_RANGES– Unicode variation selectors_MUSICAL_FORMAT_RANGEand_DEPRECATED_FORMAT_RANGE– specialized formatting controls
These definitions establish a single source of truth for what constitutes a dangerous invisible character across the entire book-to-skill codebase.
The is_invisible_codepoint Predicate
The script exposes a reusable predicate function to maintain consistency between extraction and validation phases:
def is_invisible_codepoint(codepoint: int) -> bool
Implemented in lines 121-136, this function checks whether a given Unicode scalar value falls into any of the prohibited sets or ranges. Both the deterministic extractor (scripts/extract.py) and the generated-skill scanner (tools/scan_generated_skill.py) import this predicate to guarantee identical character classification.
The sanitize_extracted_text Public API
Lines 139-151 implement the primary entry point for text cleaning:
def sanitize_extracted_text(text: str) -> tuple[str, int]
This function iterates over each character in the input string, discarding any codepoint flagged by is_invisible_codepoint. It returns a tuple containing the cleaned string and the count of removed characters, enabling audit logs and security monitoring.
How sanitize.py Prevents Prompt Injection
Invisible Unicode characters enable document-borne prompt injection attacks where malicious instructions hide within seemingly benign text. For example, zero-width spaces (\u200b) can break tokenization boundaries, while bidirectional overrides (\u202e) can visually reorder text to fool human reviewers while preserving the original instruction sequence for language models.
By stripping these characters immediately after parsing (in book_to_skill/parsers/*) and before skill generation, sanitize.py ensures that the final extracted prose contains no hidden semantics that could alter model-level interpretation.
Integration with the book-to-skill Pipeline
The sanitization layer sits between document parsing and skill generation:
- Extraction Phase:
scripts/extract.pycallssanitize_extracted_textafter PDF, EPUB, or HTML parsing completes - Validation Phase:
tools/scan_generated_skill.pyre-usesis_invisible_codepointto verify that generated skills contain no injected invisible characters - Testing:
tests/test_sanitize_extracted_text.pyconfirms that all defined invisible categories are correctly removed
This architecture prevents drift between extraction and scanning by enforcing the same invisible-character definitions across both operations.
Practical Code Examples
Cleaning Extracted Book Text
from book_to_skill.sanitize import sanitize_extracted_text
# Text containing ZERO WIDTH SPACE (\u200b), ZERO WIDTH NON-JOINER (\u200c),
# and RIGHT-TO-LEFT OVERRIDE (\u202e)
raw = "Hello\u200b\u200c World\u202e!"
clean, removed = sanitize_extracted_text(raw)
print(clean) # → "Hello World!"
print(removed) # → 3
Direct Predicate Usage
from book_to_skill.sanitize import is_invisible_codepoint
print(is_invisible_codepoint(ord('\u200b'))) # → True
print(is_invisible_codepoint(ord('A'))) # → False
Summary
sanitize.pyserves as the security-focused text sanitization layer for thebook-to-skillrepository, located atbook_to_skill/sanitize.py- The script defines eight categories of dangerous invisible Unicode codepoints (lines 4-100) including zero-width characters, bidirectional controls, and deprecated annotation formats
is_invisible_codepointprovides a reusable predicate used by both the extractor and the skill scanner to maintain consistent security policiessanitize_extracted_textreturns a cleaned string and removal count, invoked after document parsing to harden content against Trojan-Source attacks- The module is consumed by
scripts/extract.pyandtools/scan_generated_skill.py, ensuring invisible-character logic remains synchronized across the pipeline
Frequently Asked Questions
What types of invisible characters does sanitize.py remove?
The script targets eight specific categories: zero-width codepoints, bidirectional controls, invisible letter codepoints, annotation format characters, tag block delimiters, variation selectors, musical format controls, and deprecated format ranges. These definitions cover the full spectrum of Unicode characters capable of altering text interpretation while remaining invisible to human readers.
How does sanitize.py integrate with the book-to-skill extractor?
After scripts/extract.py parses a document (PDF, EPUB, or HTML), it passes the raw extracted text to sanitize_extracted_text before any downstream processing occurs. This ensures that invisible payloads are eliminated immediately after parsing but before the content reaches the skill generation logic.
What does the sanitize_extracted_text function return?
According to the source code in lines 139-151, sanitize_extracted_text returns a tuple containing two elements: the sanitized string with all invisible characters removed, and an integer representing the count of characters that were stripped during the operation.
Why is Unicode sanitization important for AI-generated skills?
Invisible Unicode characters enable prompt injection attacks where malicious instructions hide within extracted text. Without sanitization, these characters could alter how language models interpret the content while remaining invisible to human reviewers, potentially causing the generated skills to execute unintended behaviors or leak sensitive information.
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 →