Template Engine

TRECO uses a Jinja2-based template engine with custom filters for dynamic HTTP request generation.

Overview

Templates allow you to create dynamic requests with variables, conditionals, loops, and custom filters. The syntax follows Jinja2 conventions.

Basic Syntax

Variables

Use double curly braces to interpolate variables:

request: |
  GET /api/users/{{ user_id }} HTTP/1.1
  Host: {{ config.host }}
  Authorization: Bearer {{ login.token }}

Variable Sources

Variables can come from multiple sources:

# Entrypoint input
{{ username }}

# Config section
{{ config.host }}
{{ config.port }}

# Previous state extraction
{{ login.token }}
{{ get_balance.amount }}

# Thread info (race states)
{{ thread.id }}
{{ thread.count }}

# Environment variables
{{ env('API_KEY') }}

# CLI arguments
{{ argv('user', 'default') }}

Built-in Filters

TRECO provides several custom filters for security testing scenarios.

TOTP (totp)

Generate Time-based One-Time Passwords for 2FA testing.

Usage:

# Function syntax (recommended)
{{ totp(seed) }}
{{ totp('JBSWY3DPEHPK3PXP') }}

# Filter syntax
{{ seed | totp }}

Example:

states:
  login_2fa:
    request: |
      POST /api/login HTTP/1.1
      Content-Type: application/json

      {
        "username": "{{ username }}",
        "password": "{{ password }}",
        "totp_code": "{{ totp(totp_seed) }}"
      }

MD5 (md5)

Compute MD5 hash of a value.

Usage:

# Function syntax
{{ md5(password) }}

# Filter syntax
{{ password | md5 }}

Example:

states:
  login:
    request: |
      POST /api/login HTTP/1.1
      Content-Type: application/json

      {"username": "{{ username }}", "password_hash": "{{ password | md5 }}"}

SHA1 (sha1)

Compute SHA1 hash of a value.

Usage:

# Function syntax
{{ sha1(data) }}

# Filter syntax
{{ data | sha1 }}

SHA256 (sha256)

Compute SHA256 hash of a value.

Usage:

# Function syntax
{{ sha256(data) }}

# Filter syntax
{{ data | sha256 }}

Example:

states:
  api_call:
    request: |
      POST /api/secure HTTP/1.1
      X-Signature: {{ (timestamp + secret) | sha256 }}

Environment Variables (env)

Access environment variables with optional default values.

Usage:

# Without default (returns empty if not set)
{{ env('API_KEY') }}

# With default value
{{ env('API_KEY', 'default_key') }}

Example:

entrypoint:
  state: login
  input:
    username: "{{ env('USERNAME', 'testuser') }}"
    password: "{{ env('PASSWORD') }}"
    api_key: "{{ env('API_KEY', 'demo_key') }}"

CLI Arguments (argv)

Access command-line arguments passed to TRECO.

Usage:

# With default value
{{ argv('user', 'guest') }}
{{ argv('threads', '20') }}

Example:

# In YAML
entrypoint:
  state: login
  input:
    username: "{{ argv('user', 'testuser') }}"
    thread_count: "{{ argv('threads', '10') }}"

# Run with:
# treco attack.yaml --user alice --threads 20

Average (average)

Compute the average of a list of values.

Usage:

# Simple list
{{ values | average }}

# List of objects with attribute
{{ results | average(attribute='timing') }}

Example:

states:
  analyze:
    logger:
      on_state_leave: |
        Average response time: {{ timings | average }}ms

Control Structures

Conditionals

Use {% if %} blocks for conditional content:

logger:
  on_state_leave: |
    {% if status == 200 %}
    ✓ Request succeeded
    {% elif status == 401 %}
    ✗ Authentication failed
    {% else %}
    ? Unexpected status: {{ status }}
    {% endif %}

Comparison operators:

  • == - Equal

  • != - Not equal

  • <, > - Less than, greater than

  • <=, >= - Less/greater or equal

  • in - Contains

  • not in - Does not contain

Loops

Use {% for %} for iteration:

logger:
  on_state_leave: |
    Results:
    {% for result in results %}
    - Thread {{ result.thread_id }}: {{ result.status }}
    {% endfor %}

Filters

Use | to apply filters to values:

# String filters
{{ username | upper }}
{{ email | lower }}
{{ name | title }}
{{ text | length }}

# List filters
{{ items | first }}
{{ items | last }}
{{ items | join(', ') }}
{{ items | sort }}

# Number filters
{{ price | round(2) }}
{{ value | abs }}

# Default filter
{{ optional_value | default('N/A') }}

Request Templates

HTTP Request Format

Requests use raw HTTP format with templates:

request: |
  POST /api/endpoint HTTP/1.1
  Host: {{ config.host }}
  Content-Type: application/json
  Authorization: Bearer {{ token }}
  X-Custom-Header: {{ custom_value }}

  {"key": "{{ value }}", "nested": {"field": "{{ other }}"}}

Structure:

  1. Request line: METHOD /path HTTP/1.1

  2. Headers (one per line)

  3. Empty line

  4. Body (optional)

Dynamic Headers

request: |
  GET /api/data HTTP/1.1
  Host: {{ config.host }}
  Authorization: Bearer {{ login.token }}
  X-Request-Id: {{ uuid }}
  X-Timestamp: {{ timestamp }}
  {% if extra_header %}
  X-Extra: {{ extra_header }}
  {% endif %}

Dynamic Body

request: |
  POST /api/action HTTP/1.1
  Host: {{ config.host }}
  Content-Type: application/json

  {
    "user_id": {{ user_id }},
    "amount": {{ amount }},
    "timestamp": "{{ timestamp }}",
    "items": [
      {% for item in items %}
      {"id": "{{ item.id }}", "qty": {{ item.qty }}}{% if not loop.last %},{% endif %}
      {% endfor %}
    ]
  }

Logger Templates

State Logging

states:
  action:
    logger:
      on_state_enter: |
        Starting action...
        Target: {{ config.host }}

      on_state_leave: |
        Action complete.
        Status: {{ status }}
        {% if status == 200 %}
        ✓ Success
        {% else %}
        ✗ Failed
        {% endif %}

Thread Logging

states:
  race_attack:
    logger:
      on_thread_enter: |
        [Thread {{ thread.id }}/{{ thread.count }}] Preparing...

      on_thread_leave: |
        [Thread {{ thread.id }}] Complete
        Status: {{ status }}
        Time: {{ timing_ms }}ms
        Balance: {{ balance }}

Analysis Logging

states:
  verify:
    logger:
      on_state_leave: |
        ==============================
        VULNERABILITY ASSESSMENT
        ==============================
        Initial balance: {{ initial_balance }}
        Final balance: {{ final_balance }}
        Difference: {{ final_balance - initial_balance }}

        {% if final_balance > initial_balance %}
        ⚠️ VULNERABLE: Balance increased!
        {% elif final_balance == initial_balance %}
        ✓ PROTECTED: Balance unchanged
        {% else %}
        ? UNEXPECTED: Balance decreased
        {% endif %}
        ==============================

Special Variables

Thread Context

Available in race states:

{{ thread.id }}      # Thread index (0 to N-1)
{{ thread.count }}   # Total number of threads

Config Access

{{ config.host }}
{{ config.port }}
{{ config.threads }}
{{ config.tls.enabled }}

State Results

Access extracted variables from previous states:

{{ state_name.variable_name }}
{{ login.token }}
{{ get_balance.amount }}

Best Practices

Security

  1. Use env() for secrets: Never hardcode passwords or API keys

  2. Use argv() for flexibility: Allow runtime configuration

  3. Validate inputs: Use conditionals to check values

Readability

  1. Use meaningful variable names: auth_token not t

  2. Add comments in YAML: Explain complex templates

  3. Break up long templates: Use multiple lines for clarity

Performance

  1. Minimize template complexity: Simple templates render faster

  2. Cache computed values: Extract once, use multiple times

  3. Avoid loops in hot paths: Pre-compute where possible

Troubleshooting

Template syntax error:

  • Check for unclosed {{ }} or {% %}

  • Verify filter syntax (use | not .)

  • Escape special characters if needed

Variable not found:

  • Check variable spelling

  • Verify state name prefix

  • Use {{ variable | default('N/A') }} for optional values

Filter not working:

  • Check filter name spelling

  • Verify argument syntax

  • Some filters require specific types

See Also