How to Customize Scan Profiles in user-scanner: A Complete Pattern Syntax Guide

To customize scan profiles in user-scanner, construct a pattern string using square brackets for character sets, curly braces for length repetition, and backslashes for literal escaping, then supply it via the -p CLI flag or the expand_patterns() API.

The kaifcodec/user-scanner repository implements a domain-specific pattern language that defines which usernames or handles to probe during reconnaissance. By mastering this syntax, you can customize scan profiles to target specific naming conventions, ranges, or character subsets without modifying the core engine code.

Understanding the Pattern Engine Architecture

user-scanner processes every scan profile through a four-stage pipeline implemented in user_scanner/core/patterns.py. When you supply a pattern, the tool executes these steps:

  1. Lexical analysis – The Lexer class tokenizes the raw pattern string (user_scanner/core/patterns.py:L16-L29).
  2. Parsing – The _parse_patterns function converts tokens into a list of blocks, representing either plain strings or PatternBlock objects that contain character sets and length sets (L23-L52).
  3. Expansion – The expand_patterns function iterates through blocks to yield every possible username combination (L78-L93). This generator feeds directly into the HTTP request scheduler.
  4. Counting – The count_patterns function calculates total permutations beforehand (L25-L38), enabling the CLI to display progress indicators like "Scanning X of Y permutations."

The engine consumes these expanded strings via user_scanner/core/engine.py, which creates a Job for each handle and respects global flags such as concurrency (-C), timeout (-t), and --allow-loud as documented in docs/FLAGS.md.

Core Pattern Syntax for Custom Profiles

A scan profile is simply a pattern string that supports three primary customization mechanisms:

Character Sets (Square Brackets)

Define allowed characters at specific positions using square brackets. Ranges like a-z or 0-9 are expanded internally by the _char_range helper.

  • [a-c] expands to a, b, c
  • john[a-c] generates johna, johnb, johnc

Length Control (Curly Braces)

Attach a length set to a character set to control repetition. The parser builds a lenset (e.g., {0-2} translates to lengths 0, 1, and 2).

  • [0-9]{2} requires exactly two digits
  • john[0-9]{0-2} produces john, john0 through john9, and john00 through john99

Escaping Special Characters

Prepend a backslash to treat [, ], or \ literally in your pattern.

  • \\[ produces the literal character [
  • "hello\\[world\\]" generates exactly hello[world]

Implementing Custom Scan Profiles

You can deploy custom profiles through either the command-line interface or the Python library.

CLI Method

Pass your pattern directly using the -p or --pattern flag defined in user_scanner/__main__.py:

user-scanner -p "admin[0-9]{0-2}" -t 5 -C 10

This command scans admin, admin0 through admin99, using a 5-second timeout and 10 concurrent workers.

Python API Method

For programmatic control, import the expansion utilities and engine directly:

from user_scanner.core.engine import Engine
from user_scanner.core.patterns import expand_patterns

# Define a profile checking john0-john9 and jane[a-c]

profile = "john[0-9]{1} jane[a-c]"

# Generate handles manually (or let Engine handle expansion internally)

handles = list(expand_patterns(profile))

engine = Engine()
results = engine.scan(handles)  # Returns iterator of Result objects

for r in results:
    print(r.handle, r.status, r.extra)

Counting Permutations Before Scanning

Use count_patterns to preview scan scope:

python -c "import user_scanner.core.patterns as p; print(p.count_patterns('user[0-9]{3}'))"

Output: 1000

This confirms the scan will generate exactly 1,000 usernames before consuming bandwidth.

How the Engine Processes Your Profile

Once expanded, the list of handles flows into user_scanner/core/engine.py, where each string becomes a Job instance. The engine respects your CLI flags for rate limiting and timeout configurations, then outputs structured Result objects defined in user_scanner/core/result.py. The entire pipeline—from pattern parsing in user_scanner/core/patterns.py to final status reporting—operates as a lazy iterator, ensuring memory efficiency even when your custom profile generates millions of permutations.

Summary

  • Pattern strings are the core mechanism to customize scan profiles in user-scanner, parsed by the Lexer and _parse_patterns functions in user_scanner/core/patterns.py.
  • Square brackets define character sets, curly braces control repetition length, and backslashes escape special characters.
  • Use expand_patterns() to generate handles programmatically or the -p CLI flag for direct execution.
  • Call count_patterns() before scanning to predict total permutation counts and resource requirements.
  • The engine processes expanded handles via user_scanner/core/engine.py, creating Job instances that respect global concurrency and timeout flags.

Frequently Asked Questions

What is the pattern syntax for user-scanner scan profiles?

Scan profiles use a custom DSL where square brackets define character sets (e.g., [a-z]), curly braces specify repetition counts (e.g., {2}), and backslashes escape literal characters. This syntax is parsed by classes in user_scanner/core/patterns.py and fully documented in the repository's docs/PATTERNS.md.

How do I count how many usernames a pattern will generate?

Import count_patterns from user_scanner.core.patterns and pass your pattern string. This function, implemented between lines 25-38 of user_scanner/core/patterns.py, calculates the Cartesian product of all character and length sets without expanding them into memory.

Can I use multiple character sets in a single pattern?

Yes. The _parse_patterns function (lines 23-52) tokenizes complex patterns containing multiple blocks. For example, "admin[0-9][a-c]{2}" combines numeric and alphabetic sets with length control, generating combinations like admin0aa, admin0ab, through admin9cc.

Where does user-scanner process the pattern string internally?

The pattern string enters the pipeline in user_scanner/core/patterns.py, where the Lexer class performs tokenization (lines 16-29) and _parse_patterns builds block representations. The expanded results then flow to user_scanner/core/engine.py, which orchestrates the actual HTTP scanning via the Engine class.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →