Each note follows Observation → Hypothesis → Approach → Result → Discussion → Takeaway, grounded in the repository’s documentation and its own published test/benchmark figures.
Note 1 — Operator-first lexing fixes multi-character encoding
Observation. Naive character-level encoding turns >= into %3E= (broken); the framework encodes it as %3E%3D.
Hypothesis. Recognizing multi-character operators as single tokens before single-character ones prevents corrupting them during encoding.
Approach. Lex >=, <=, <>, != with multi-char checks ordered first, then URL-encode the resulting operator tokens atomically.
Result. Operators encode completely (>= → %3E%3D), and the lexer test suite covers multi-char operators.
Discussion. This is a classic lexer ordering rule: longest-match-first for operators. The bug is invisible until an encoded payload silently becomes invalid SQL, which is why the framework treats it as a headline fix.
Takeaway. Longest-operator-first lexing is mandatory for any tamper tool that encodes operators; character-level handling is a correctness bug, not a style choice.
Note 2 — UUID token identity survives value changes
Observation. Wrapping SELECT into /*!50000SELECT*/ invalidates position-based tracking; the framework assigns each token a UUID at lex time.
Hypothesis. Tracking tokens by stable UUID rather than position keeps transformations coherent even as token lengths change across passes.
Approach. Assign a UUID per token in the lexer and reference tokens by UUID through transformation and reapplication-protection logic.
Result. Transformations remain correct after length-changing wraps; UUID tracking is covered by lexer tests and cited as a core fix.
Discussion. Position tracking is O(fragile): any length-changing transform desynchronizes it. UUID identity decouples “which token” from “where it currently sits,” which is what makes multi-rule chains safe.
Takeaway. Stable token identity (UUID) is the enabler for multi-pass, reapplication-safe transformation; position indices cannot do this job.
Note 3 — Clause context prevents over- and under-transformation
Observation. The same = should be encoded in WHERE but not in SELECT; the Context Tracker maintains clause state.
Hypothesis. Tracking the active SQL clause lets rules fire only where a transformation is both safe and useful.
Approach. Walk tokens maintaining clause state (SELECT/FROM/WHERE, nesting depth); rules declare allowed_clauses and consult context.clause.
Result. value_encode encodes id>=5 in WHERE while leaving * in SELECT untouched, per the documented example.
Discussion. Context blindness forces a bad choice: encode everywhere (break queries) or nowhere (fail to bypass). Clause tracking removes the dilemma and is what “context-aware” actually means here.
Takeaway. Clause-aware rule gating is the difference between a tamper script that works and one that either breaks SQL or fails to evade.
Note 4 — Determinism as both engineering and safety property
Observation. The framework guarantees deterministic output (same input yields same output) with no random mutations, and states this is deliberate to prevent abuse.
Hypothesis. Deterministic transformation makes outputs testable and reproducible while denying misuse as a random-variant generator.
Approach. Implement all transformations as pure functions of token+context (including “version vary” as deterministic, not random) and assert reproducibility in tests.
Result. Transformer tests assert deterministic output; the docs frame reproducibility as a research/verification feature and a safety guarantee.
Discussion. Randomized tampering would maximize evasion unpredictability but is exactly what makes a tool a spray weapon and untestable. Choosing determinism trades that away on purpose.
Takeaway. Determinism is a legitimate design choice for an offensive tool: it buys testability, reproducibility, and abuse resistance at the cost of randomized evasion.
Note 5 — Reapplication protection enables safe chaining
Observation. The transformer includes reapplication protection, and transformations are chained (multiple rules, WAF scripts combine several).
Hypothesis. Guarding against transforming an already-transformed token prevents double-encoding when rules are chained or rerun.
Approach. Track which tokens (by UUID) a rule has already transformed and skip them on reapplication.
Result. Chained transformations (as in cloudflare2025.py’s four-stage chain) compose without double-encoding.
Discussion. Without reapplication protection, chaining value_encode after another rule could re-encode %3E%3D into nonsense. UUID identity (Note 2) is what makes this guard possible.
Takeaway. Reapplication protection is the safety net that makes multi-rule tamper chains reliable; it depends on stable token identity.
Note 6 — Modular transformations scale the technique library
Observation. v2.0.0 shipped 4 core transformations; v2.1.0 added 7 advanced ones (homoglyph, function_wrap, numeric_obfuscation, comment_chaos, logical_operator_swap, hex_encode, version_comment_vary), each a separate module.
Hypothesis. Implementing each evasion technique as an independent module composed as a rule lets the library grow without engine changes.
Approach. Add each new technique as a module exposing a rule; compose rules onto transformers and into WAF scripts.
Result. The jump from 4 to 11+ transformations and 2 to 7 WAF scripts in one release was additive, per the changelog.
Discussion. A monolithic transformer would have made each addition a risky edit; the module-per-technique design localizes change and risk. This is standard good architecture paying off in a fast-moving domain.
Takeaway. Module-per-technique with rule composition is the right structure for an evasion library that must track evolving WAFs.
Note 7 — WAF-specific chains encode per-firewall knowledge
Observation. Seven scripts target specific WAFs with specific technique chains (e.g. Imperva → homoglyphs + function wrap; ModSecurity CRS → case first + math numbers).
Hypothesis. Pre-composing technique chains tuned to a firewall’s known behavior is more effective than a one-size-fits-all transform.
Approach. Build a per-WAF script selecting the transformations empirically suited to that firewall’s rules.
Result. Seven ready-to-run scripts map WAFs to technique chains; meta_tamper.py selects chains via environment variables.
Discussion. A WAF bypass is always relative to a ruleset, so per-target chains are the natural unit. Encoding that knowledge into named scripts turns tester expertise into reusable configuration.
Takeaway. Per-WAF technique chains (plus an env-driven combiner) are the practical delivery form for evasion knowledge; the bypass is only ever relative to the target.
Note 8 — Token-based vs AST-based is a speed/accuracy dial
Observation. The framework offers both token-based and AST-based transformation; the docs recommend token-based for most cases and AST-based for complex/nested queries.
Hypothesis. A flat token stream is faster but weaker on nesting; an AST is slower but handles subqueries and function calls accurately.
Approach. Provide both engines and let the user choose per query complexity; benchmark representative queries.
Result. Reported benchmarks: ~1ms (10 tokens), ~5ms (100 tokens), ~10ms (nested subquery); AST-based recommended for nested cases.
Discussion. Exposing the tradeoff to the user is honest: nested subqueries are exactly where a flat tokenizer’s clause tracking can slip, and the AST builder exists for those. Both are millisecond-scale, so the choice is about correctness, not speed.
Takeaway. Offer token-based by default and AST-based for nesting; at millisecond scale the decision is accuracy-driven, not performance-driven.
Note 9 — MySQL-dialect focus is a deliberate scope boundary
Observation. The framework targets MySQL 5.7+/MariaDB 10.x and documents that it may not work with PostgreSQL, MSSQL, or Oracle.
Hypothesis. Focusing on one SQL dialect yields correct, idiomatic transformations (version comments, &&/||, hex literals) at the cost of cross-database generality.
Approach. Implement transformations around MySQL syntax (e.g. /*!50000...*/ version comments, which are MySQL-specific execution-in-comment behavior).
Result. MySQL-idiomatic techniques work; non-MySQL databases are out of scope with a “modify for target” workaround.
Discussion. Version-comment wrapping literally relies on MySQL’s conditional-comment execution, which other databases do not share, so cross-DB generality would mean a different technique set entirely. Scoping to MySQL is the correct call for correctness.
Takeaway. Dialect-specific tampering is more correct than dialect-agnostic; the MySQL focus is a feature of correctness, not a gap.
Note 10 — Benchmarks and tests are published, but with an unreconciled test count
Observation. The repo publishes benchmarks (1/5/10ms) and a test breakdown (10+10+13 = 33/33), but the README badge says “49+ passing.”
Hypothesis. The itemized 33 reflects the documented breakdown while 49+ reflects tests added later; both are real, and the discrepancy is documentation lag, not fabrication.
Approach. Preserve both figures, attribute each to its source, and treat the itemized 33 as the enumerated ground truth pending reconciliation.
Result. Two test counts coexist in the project’s own docs; the benchmarks are single-sourced and consistent.
Discussion. Publishing tests and benchmarks at all is a maturity signal for a tamper-script repo. The count mismatch is minor but worth flagging rather than silently picking one, since a reader auditing the claim will hit it.
Takeaway. The project reports real tests and benchmarks; the 33-vs-49+ gap is a doc-consistency issue to reconcile, not a correctness problem.
REGAAN R