Skip to content

Loop Context Variables (index, counter, first, last) #5

@maycon

Description

@maycon

Loop Context Variables (index, counter, first, last)

Summary

Provide loop iteration context variables (loop.index, loop.counter, loop.first, loop.last) accessible in request templates and conditionals.

Motivation

When executing loops, attack scenarios often need to:

  • Use the iteration number in requests (e.g., testing sequential IDs)
  • Treat the first iteration differently (e.g., setup)
  • Treat the last iteration differently (e.g., cleanup)
  • Display human-friendly iteration numbers in logs

Current limitation: No way to access iteration information within templates.

Proposed Syntax

states:
  test_user_enumeration:
    description: "Test sequential user IDs"
    request: |
      GET /api/users/{{ loop.counter }}
      Authorization: Bearer {{ token }}
    
    loop:
      iterations: 100
    
    logger:
      on_thread_enter: |
        {% if loop.first %}
          Starting user enumeration attack...
        {% endif %}
        Testing user ID: {{ loop.counter }}
      
      on_thread_leave: |
        {% if loop.last %}
          Completed user enumeration. Found {{ valid_users | length }} users.
        {% endif %}
    
    extract:
      user_exists:
        type: jpath
        pattern: "$.exists"
    
    next:
      - on_loop_complete:
        goto: analyze_results

Loop Context Variables

loop.index

  • Type: Integer
  • Range: 0 to (iterations - 1)
  • Description: Zero-based iteration index
  • Use case: Array/list access, algorithmic calculations
request: |
  POST /api/test
  {"value": {{ test_values[loop.index] }}}

loop.counter

  • Type: Integer
  • Range: 1 to iterations
  • Description: Human-friendly iteration counter
  • Use case: User IDs, sequential numbering, logs
request: |
  GET /api/users/{{ loop.counter }}

loop.first

  • Type: Boolean
  • Value: true on first iteration, false otherwise
  • Description: True only on first iteration (index=0)
  • Use case: Initialization, setup requests
logger:
  on_thread_enter: |
    {% if loop.first %}
      🚀 Starting attack sequence...
    {% endif %}

loop.last

  • Type: Boolean
  • Value: true on last iteration, false otherwise
  • Description: True only on last iteration (index=iterations-1)
  • Use case: Cleanup, final validation, summaries
logger:
  on_thread_leave: |
    {% if loop.last %}
      ✅ Attack complete. Total requests: {{ loop.counter }}
    {% endif %}

loop.iteration (alias)

  • Type: Integer
  • Description: Alias for loop.counter (for clarity)
  • Note: Use loop.counter for consistency

Implementation Details

Loop Context Structure

@dataclass
class LoopContext:
    """Context information for current loop iteration."""
    index: int          # 0-based index
    counter: int        # 1-based counter
    iteration: int      # Alias for counter
    first: bool         # True on first iteration
    last: bool          # True on last iteration
    total: int          # Total iterations
    
    @staticmethod
    def create(index: int, total: int) -> 'LoopContext':
        return LoopContext(
            index=index,
            counter=index + 1,
            iteration=index + 1,
            first=(index == 0),
            last=(index == total - 1),
            total=total
        )

Template Context Injection

class LoopExecutor:
    def execute_loop_state(self, state_config, context):
        iterations = state_config['loop']['iterations']
        
        for i in range(iterations):
            # Inject loop context
            loop_context = LoopContext.create(i, iterations)
            context['loop'] = {
                'index': loop_context.index,
                'counter': loop_context.counter,
                'iteration': loop_context.iteration,
                'first': loop_context.first,
                'last': loop_context.last,
                'total': loop_context.total,
            }
            
            # Render request with loop context available
            rendered_request = self.template_engine.render(
                state_config['request'], 
                context
            )
            
            # Execute request...

Jinja2 Template Examples

{# Access in conditionals #}
{% if loop.first %}
  "action": "initialize"
{% elif loop.last %}
  "action": "finalize"
{% else %}
  "action": "process"
{% endif %}

{# Use in expressions #}
"item_id": "{{ 1000 + loop.index }}",
"batch": "{{ (loop.index // 10) + 1 }}",
"is_final": {{ loop.last | lower }}

{# Logging #}
Progress: {{ loop.counter }}/{{ loop.total }} ({{ (loop.counter / loop.total * 100) | round }}%)

Use Cases

1. User Enumeration

states:
  enumerate_users:
    request: |
      GET /api/users/{{ loop.counter }}
    loop:
      iterations: 1000

2. Batch Processing

states:
  process_batches:
    request: |
      POST /api/batch
      {
        "batch_id": {{ (loop.index // 100) + 1 }},
        "items": {{ items[loop.index:loop.index+100] | tojson }}
      }
    loop:
      iterations: 10

3. Progressive Load Testing

states:
  load_test:
    description: "Increase load progressively"
    request: |
      POST /api/heavy-endpoint
      {"load_factor": {{ loop.counter * 10 }}}
    
    logger:
      on_thread_enter: |
        {% if loop.first %}
          Starting load test...
        {% endif %}
        Load factor: {{ loop.counter * 10 }}
    
    loop:
      iterations: 50

4. Setup and Cleanup

states:
  attack_with_setup:
    request: |
      {% if loop.first %}
        POST /api/setup
        {"initialize": true}
      {% elif loop.last %}
        POST /api/cleanup
        {"finalize": true}
      {% else %}
        POST /api/attack
        {"payload": "malicious"}
      {% endif %}
    
    loop:
      iterations: 10

5. Array Iteration

entrypoints:
  - state: test_coupons
    input:
      coupons: ["SAVE10", "SAVE20", "SUMMER2024", "WINTER2024"]

states:
  test_coupons:
    request: |
      POST /api/apply-coupon
      {"code": "{{ coupons[loop.index] }}"}
    
    loop:
      iterations: "{{ coupons | length }}"
    
    logger:
      on_thread_enter: |
        [{{ loop.counter }}/{{ loop.total }}] Testing: {{ coupons[loop.index] }}

Testing Requirements

Unit Tests

  • loop.index starts at 0 and increments correctly
  • loop.counter starts at 1 and increments correctly
  • loop.first is true only on first iteration
  • loop.last is true only on last iteration
  • loop.iteration equals loop.counter (alias)
  • loop.total reflects total iterations
  • All variables accessible in request templates
  • All variables accessible in logger templates
  • Variables work correctly with iterations=1
  • Variables work correctly with large iteration counts

Integration Tests

  • Sequential ID enumeration works correctly
  • Conditional logic based on first/last works
  • Array indexing with loop.index works
  • Mathematical expressions with loop variables work
  • Nested templates with loop variables render correctly

Edge Cases

  • Loop with 1 iteration: both first and last are true
  • Loop variables not available outside loop context
  • Loop variables cleared after loop completion
  • Loop variables don't leak between states

Output Examples

Progress Logging

[Iteration 1/100] Testing user ID: 1 ✓ (exists)
[Iteration 2/100] Testing user ID: 2 ✗ (not found)
[Iteration 3/100] Testing user ID: 3 ✓ (exists)
...
[Iteration 100/100] Testing user ID: 100 ✗ (not found)

✅ Completed. Found 47 valid users.

Conditional Execution

[Iteration 1/10] 🚀 Initializing attack...
[Iteration 2/10] 📤 Sending payload...
[Iteration 3/10] 📤 Sending payload...
...
[Iteration 10/10] 🧹 Cleaning up...
✅ Attack complete.

Documentation Updates

  • Add "Loop Variables" section to README
  • Create reference table of all loop variables
  • Add examples for each loop variable
  • Document common patterns (enumeration, batching, etc.)
  • Add troubleshooting section for template errors

Schema Updates

No schema changes required. Loop context is automatically available when loop is defined.

Acceptance Criteria

  • All 6 loop variables available in templates
  • Variables have correct values at each iteration
  • loop.first and loop.last work correctly
  • Variables work in both request and logger templates
  • Variables work with Jinja2 conditionals and expressions
  • Clear error messages when loop variables used outside loop
  • Documentation includes 5+ practical examples
  • All tests pass

Follow-up Issues

  • #XX - Early Loop Exit with break_on Conditions - Phase 1.3
  • #XX - Inter-iteration Delays - Phase 1.4

Related Issues

  • Depends on: #XX Basic Loop Implementation
  • Blocks: #XX Early Loop Exit (needs loop.index for conditions)
  • Related to: Template Engine improvements

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions