A fully client-side, zero-dependency HTML tool for building, editing, and copying Snort 2.9 IDS/IPS rules visually.
- Overview
- Quick Start
- Understanding a Snort Rule
- Interface Layout
- Section Reference — Rule Header
- Section Reference — Rule Metadata
- Section Reference — Detection Options
- Section Reference — Advanced Options
- Right Panel — Live Output & Controls
- Quick Templates
- JavaScript Function Reference
- State Variables
- Event Binding System
- Syntax Highlighting System
- CSS Architecture & Design System
- Snort Rule Field Reference
- Common Workflows
- Troubleshooting
The Snort Rule Generator is a self-contained single HTML file (~1,300 lines) that allows network security analysts, SOC engineers, and students to construct valid Snort 2.9 detection rules without memorizing syntax. Every change to any form field instantly rebuilds and re-renders the rule output with full syntax highlighting.
Key characteristics:
- Zero dependencies — no frameworks, no build tools, no server. Open the file in a browser and it works.
- Fully client-side — nothing leaves your machine. Safe for use in air-gapped or restricted environments.
- Live rule preview — the output panel updates on every keystroke or click.
- Syntax-highlighted output — color-coded rule display for easy reading and review.
- Six pre-built templates for the most common detection scenarios.
- Editable raw output textarea for manual fine-tuning after generation.
- Responsive layout that collapses gracefully to a single column on smaller screens.
- Download or save
snort-rule-generator.htmlto your local machine. - Open it in any modern browser (Chrome, Firefox, Edge, Safari).
- The tool loads with a default rule pre-populated:
alert tcp any any -> any any (msg:"Suspicious activity detected"; sid:1000001; rev:1;) - Modify fields in the left column. The rule in the right panel updates live.
- Click Copy Rule to copy the plain-text rule to your clipboard.
- Paste into your
local.rules,snort.rules, or any Snort configuration file.
No internet connection is required after the initial page load (Google Fonts are the only external resource, and the tool functions perfectly without them if offline).
Every Snort rule has two parts: a header and an options body.
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"Example"; sid:1000001; rev:1;)
|_____| |____________| |_| |____________| |__________| |___________________________________|
Action Source IP S.Port Dest IP Dest Port Options body
Protocol Direction
The generator assembles this structure from the fields you fill in. Every field maps directly to a position or keyword in the Snort rule grammar.
The page is divided into two columns:
┌─────────────────────────────────────┬───────────────────┐
│ LEFT COLUMN (scrollable) │ RIGHT COLUMN │
│ │ (sticky) │
│ ┌─────────────────────────────┐ │ ┌─────────────┐ │
│ │ Rule Header │ │ │ Stats Bar │ │
│ └─────────────────────────────┘ │ └─────────────┘ │
│ ┌─────────────────────────────┐ │ ┌─────────────┐ │
│ │ Rule Metadata │ │ │ Live Output │ │
│ └─────────────────────────────┘ │ │ (terminal) │ │
│ ┌─────────────────────────────┐ │ └─────────────┘ │
│ │ Detection Options │ │ ┌─────────────┐ │
│ └─────────────────────────────┘ │ │ Copy/Reset │ │
│ ┌─────────────────────────────┐ │ └─────────────┘ │
│ │ Advanced Options │ │ ┌─────────────┐ │
│ └─────────────────────────────┘ │ │ Raw Output │ │
│ │ └─────────────┘ │
│ │ ┌─────────────┐ │
│ │ │ Templates │ │
│ │ └─────────────┘ │
└─────────────────────────────────────┴───────────────────┘
The right column is position: sticky so the rule output is always visible while you scroll through the left column.
The Rule Header card defines the first line of every Snort rule — the part before the parentheses.
Controls what Snort does when the rule matches.
| Value | Behavior |
|---|---|
alert |
Generate an alert and log the packet. Most commonly used for detection. |
log |
Log the packet only, no alert. Useful for passive recording. |
pass |
Ignore the packet. Used to whitelist trusted traffic. |
activate |
Alert and then activate a dynamic rule for subsequent packets. |
dynamic |
Triggered by an activate rule; logs subsequent packets in the same stream. |
drop |
Block the packet and log it (inline/IPS mode only). |
reject |
Block the packet, log it, and send a TCP reset or ICMP unreachable (inline/IPS mode only). |
sdrop |
Silently drop the packet without logging (inline/IPS mode only). |
Default: alert
The network protocol to inspect.
| Value | Description |
|---|---|
tcp |
Transmission Control Protocol. Used for web, SSH, FTP, etc. |
udp |
User Datagram Protocol. Used for DNS, SNMP, TFTP, etc. |
icmp |
Internet Control Message Protocol. Used for ping, traceroute, etc. |
ip |
Any IP protocol. Matches all IP traffic regardless of transport. |
Default: tcp
The IP address or network range that the traffic originates from. Accepts:
any— match all source IPs (no filtering).- A literal IP address:
192.168.1.100 - A CIDR range:
192.168.1.0/24 - A Snort variable:
$HOME_NET,$EXTERNAL_NET,$HTTP_SERVERS,$SQL_SERVERS, etc. - A negated value using
!:!192.168.1.0/24(match anything except this range). - A grouped list:
[192.168.1.0/24,10.0.0.0/8]
Default: any
The port the traffic originates from. Accepts:
any— match all ports.- A single port number:
80 - A port range:
1024:65535(from 1024 to 65535),:1023(up to 1023),1024:(from 1024 up). - A negated port:
!80 - A grouped list:
[80,443,8080] - Snort variables:
$HTTP_PORTS,$SHELLCODE_PORTS, etc.
Default: any
Controls the directionality of traffic matching.
| Option | Symbol | Meaning |
|---|---|---|
| One-way | -> |
Traffic flows only from source to destination. This is the standard choice for most rules. |
| Bidirectional | <> |
Traffic in both directions is inspected. Useful for monitoring sessions without knowing which side initiates. |
Clicking either button updates dirVal (a module-level variable) and immediately triggers renderRule().
The IP address or network range of the traffic destination. Accepts the same formats as Source IP.
Default: any
The destination port. Accepts the same formats as Source Port.
Default: any
The Rule Metadata card fills the options body with identification and classification keywords.
A human-readable string that describes what the rule detects. This string appears in Snort alert logs, SIEMs, and SOC dashboards. It is the primary way analysts identify an alert.
- Should be clear and descriptive:
"WEB-ATTACKS SQL Injection Attempt" - Convention: prefix with a category in ALL CAPS:
ET SCAN,SHELLCODE,WEB-ATTACKS,MALWARE-CNC - Colons and most special characters are allowed inside double quotes.
- The generator automatically escapes double-quote characters inside the msg value.
Default: Suspicious activity detected
The Snort Identification Number. Every rule must have a unique SID.
| SID Range | Owner |
|---|---|
| 1 – 999 | Reserved |
| 1000 – 999,999 | Snort official rules |
| 1,000,000 – 1,999,999 | Sourcefire / Talos community |
| 2,000,000+ | Emerging Threats (ET) |
| Local rules typically start at 9,000,000+ | Your organization |
Default: 1000001
An integer that increments each time the rule is modified. Snort uses the SID + rev combination to track rule versions. Always increment rev when editing an existing rule.
Default: 1
Overrides the priority assigned by classtype. Integer from 1 to 10, where 1 is highest severity.
- Priority 1: Critical / immediate threat
- Priority 2: High severity
- Priority 3: Medium / informational
If left empty, the priority is inherited from the classtype definition (if set) or omitted from the rule.
Generator ID. Identifies which Snort component generated the event. Leave blank for standard rules (defaults to GID 1, the rules engine). Used primarily by preprocessors which use GIDs like 106 (stream5), 119 (http_inspect), etc.
Associates the rule with a pre-defined attack category. Classtypes affect default priority and how alerts are grouped in reporting tools. The generator includes all 29 standard Snort classtypes:
| Classtype | Default Priority |
|---|---|
attempted-admin |
1 |
attempted-user |
1 |
attempted-dos |
2 |
attempted-recon |
3 |
bad-unknown |
2 |
default-login-attempt |
2 |
denial-of-service |
2 |
misc-activity |
3 |
misc-attack |
2 |
network-scan |
3 |
not-suspicious |
3 |
policy-violation |
1 |
protocol-command-decode |
3 |
shellcode-detect |
1 |
string-detect |
3 |
successful-admin |
1 |
successful-dos |
2 |
successful-recon-largescale |
2 |
successful-recon-limited |
3 |
successful-user |
1 |
suspicious-filename-detect |
2 |
suspicious-login |
2 |
system-call-detect |
2 |
tcp-connection |
4 |
trojan-activity |
1 |
unknown |
3 |
unusual-client-port-connection |
2 |
web-application-activity |
3 |
web-application-attack |
1 |
This card handles the payload and session inspection logic — the heart of any Snort rule.
The flow keyword restricts the rule to packets matching a specific TCP session state and direction. This dramatically reduces false positives by ensuring the rule only fires on the expected side of a conversation.
| Value | Meaning |
|---|---|
to_server,established |
Client sending data to server on an established TCP session. Standard choice for inspecting HTTP requests, POST bodies, form submissions. |
to_client,established |
Server sending data back to the client. Used for detecting malicious responses, drive-by downloads, C2 responses. |
from_server,established |
Alias for to_client,established. |
from_client,established |
Alias for to_server,established. |
established |
Any established session, either direction. |
stateless |
Inspect all matching packets regardless of session state. Used for scanning detection where no full connection is expected. |
not_established |
Only match packets that are NOT part of an established connection. Catches SYN floods, half-open scans. |
to_server,stateless |
All packets toward the server, established or not. |
to_client,stateless |
All packets toward the client, established or not. |
When set, adds content:"METHOD"; http_method to the options, restricting the rule to a specific HTTP request verb. Options: GET, POST, PUT, DELETE, HEAD, TRACE, OPTIONS, CONNECT, PATCH.
Requires an HTTP preprocessor (http_inspect) to be configured in snort.conf.
Clickable flag tags that build the flags: keyword value. Multiple flags can be selected simultaneously. The resulting string concatenates their single-letter codes.
| Tag | Code | Meaning |
|---|---|---|
| SYN | S |
Synchronize — connection initiation |
| ACK | A |
Acknowledge — confirming receipt |
| PSH | P |
Push — send data immediately |
| RST | R |
Reset — abort connection |
| FIN | F |
Finish — graceful close |
| URG | U |
Urgent — prioritized data |
| + | + |
Reserved bit set (unusual, potentially evasion) |
| any | * |
Match any flags combination |
Example: Selecting SYN + ACK produces flags:SA, which matches only SYN-ACK packets (server responding to a connection request).
Click behavior: Each tag is a <span> element. Clicking it calls the flags event listener, which toggles the flag's letter in the flagSelected Set and re-renders the rule. Active flags glow blue.
The most important part of most rules. Three types of match blocks can be added, in any order and any quantity.
Three dashed "add" buttons at the bottom of the section:
+ content— raw byte/string match in the packet payload.+ PCRE regex— Perl-Compatible Regular Expression match.+ uricontent— match specifically in the HTTP URI (requires http_inspect preprocessor).
Each click calls addContent(type), which pushes a new object into the contentItems array and calls renderContents().
Used for exact string or hex-byte matching anywhere in the payload.
- Input field: The string to match. Can be ASCII text or pipe-delimited hex bytes like
|90 90 90|. - nocase toggle: Makes the string match case-insensitive. Adds
nocasemodifier. - rawbytes toggle: Ignores any preprocessor normalization. Inspects the raw packet bytes.
- offset: Start searching from this byte position in the payload (0-indexed).
- depth: Only search within this many bytes from the start (or from offset).
- distance: After the previous content match, skip this many bytes before starting the next search.
- within: After the previous content match, only search within this many bytes.
Offset and depth are absolute positions. Distance and within are relative to the previous content match — useful for chaining multiple patterns.
Example: Matching UNION followed by SELECT within 20 bytes:
content:"UNION"; nocase; content:"SELECT"; nocase; within:20;
Matches the payload against a PCRE regular expression. The value field should contain the full PCRE expression including delimiters and flags, e.g. /union(\s|%20)+select/i.
PCRE flags can be toggled inline:
| Flag | Meaning |
|---|---|
i |
Case-insensitive match |
s |
. matches newlines too (single-line mode) |
m |
^ and $ match start/end of each line (multi-line mode) |
x |
Allow whitespace and comments in the pattern |
A |
Anchored — pattern must match at the current position |
E |
$ matches only at the end of the string |
G |
Ungreedy — quantifiers match as few characters as possible |
The selected flag letters are appended to the pcre value during rule generation.
Functionally identical to content but restricts the match to the normalized HTTP request URI. Requires http_inspect to be active. Supports nocase and rawbytes modifiers but does not support offset/depth/distance/within (those apply to the full payload, not the URI-only buffer).
Each block has a small × button in the top-right corner. Clicking it calls removeContent(i), which splices the item out of contentItems and re-renders.
Controls how many times a rule must fire before generating an alert, and over what time window. Used to suppress noisy rules or detect rate-based attacks.
| Field | Snort keyword part | Description |
|---|---|---|
| Type | type threshold / type limit / type both |
threshold fires once per N events; limit fires for the first N events only; both combines the behaviors. |
| Track by | track by_src / track by_dst |
Whether the counter is per source IP or per destination IP. |
| Count | count N |
Number of events required to trigger (or suppress after, for limit). |
| Seconds | seconds N |
The sliding time window over which events are counted. |
All four fields must be filled for the threshold: option to be included in the rule. If any one is blank, the threshold block is omitted.
Example config: type threshold, track by_src, count 10, seconds 60 — alert once if the source IP triggers this rule 10 times in 60 seconds.
Matches packets by their IP Time-To-Live value. Useful for detecting OS fingerprinting (different OSes use different default TTLs) or hop-count manipulation.
- Operator:
<,>, or= - Value: integer 0–255
Example: TTL < 5 generates ttl:<5 — catches packets that have been routed through many hops or packets deliberately crafted with a low TTL.
Matches ICMP packets by their type field value (0–255).
Common ICMP types: 0 (Echo Reply), 3 (Destination Unreachable), 8 (Echo Request / ping), 11 (Time Exceeded).
- Operator:
<,>, or= - Value: integer 0–255
Generates keyword: itype:=8
Matches the ICMP code subfield within an ICMP type. For example, ICMP type 3 (Destination Unreachable) has codes 0–15 indicating the specific reason.
- Operator:
<,>, or= - Value: integer 0–255
Generates keyword: icode:=0
Matches packets by the size of the payload data in bytes.
- Operator:
<,>,=, or<>(range, requires two values separated by<>) - Value: integer
Example: dsize:>512 fires only on packets with more than 512 bytes of payload — useful for DNS amplification detection.
Matches the IP protocol number in the IP header. Useful when using the ip protocol in the rule header to then filter by specific sub-protocols.
- Operator:
<,>,=, or!(not equal) - Value: protocol number or name (
6for TCP,17for UDP,1for ICMP,47for GRE, etc.)
Generates keyword: ip_proto:=47
A free-text field that maps directly to the detection_filter keyword — Snort's newer, more powerful alternative to threshold. Allows filtering based on rate of matching at the rule level.
Enter the full value string, for example: track by_src, count 5, seconds 60
This generates: detection_filter: track by_src, count 5, seconds 60
Unlike threshold, detection_filter does not generate an alert during the "ramp-up" period — it suppresses the alert entirely until the threshold is met.
External links that document the threat the rule detects. Each reference consists of a type and a value. References appear in Snort alert output and are clickable in some SIEMs.
| Type | Format | Example value |
|---|---|---|
url |
Full URL | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228 |
cve |
CVE number | 2021-44228 |
bugtraq |
BugTraq ID number | 12345 |
ms |
Microsoft Security Bulletin | MS08-067 |
bid |
BugTraq alternative | 67890 |
nessus |
Nessus plugin ID | 10176 |
arachnids |
Arachnids IDS alert ID | 533 |
osvdb |
OSVDB vulnerability ID | 60799 |
Click + add reference to add an entry. Each entry renders with a type dropdown and a value input. Click × to remove. Multiple references are allowed and each becomes a separate reference:type,value option.
Three metric tiles that update live as you build the rule:
- Options — total count of keyword options in the current rule body (every
key:valuepair). - Matches — number of content/pcre/uricontent blocks added.
- Refs — number of reference entries added.
These give a quick visual sense of rule complexity.
The centerpiece of the right panel. Styled as a macOS-style terminal window (with red/yellow/green decorative dots). Displays the complete generated rule with syntax highlighting applied in real time.
Color coding of the rule output:
| Color | Token type | Example |
|---|---|---|
| Red / bold | Action keyword | alert |
| Blue / bold | Protocol | tcp |
| Green | IP addresses and ports | $HOME_NET 80 |
| Muted gray | Direction arrow and punctuation | -> ( ; ) |
| Purple | Option keywords | msg sid content |
| Amber / orange | Option values (non-string) | 1000001 SA |
| Teal | Quoted string values | "Suspicious activity" |
The colored output is rendered as HTML using <span> tags with CSS classes (t-action, t-proto, t-net, t-dir, t-key, t-val, t-str, t-punc). The raw plain-text version is stored on the DOM node as element._raw for clipboard copying.
Reads rule_out._raw (the unstyled plain-text version of the rule) and writes it to the system clipboard via navigator.clipboard.writeText(). On success, the button label changes to ✓ Copied! with a green glow animation for 2 seconds, then reverts.
Calls resetAll(). Resets every field to its default value, clears all content matches, clears all references, clears TCP flag selections, and resets the direction toggle to one-way. The rule output updates immediately to the baseline default rule.
An editable textarea that mirrors the generated rule in plain text. Normally read-only (marked with the readonly HTML attribute), it stays in sync with the live output.
Two buttons below it:
- Edit — removes the
readonlyattribute, allowing you to type freely in the textarea and make manual adjustments that aren't available in the UI. - Rebuild — re-applies
readonlyand callsrenderRule()to overwrite the textarea with a freshly generated rule from the current form state. Use this to discard manual edits and re-sync with the form.
Six pre-built rule patterns that populate all relevant fields in one click, demonstrating best practices for common detection scenarios. Each template calls resetAll() first to clear any previous state.
Detects TCP SYN packets from the external network toward the internal network at a rate of more than 5 in 60 seconds from the same source — characteristic of Nmap and similar scanners.
Generated fields: action alert, proto tcp, src $EXTERNAL_NET, dst $HOME_NET, flow stateless, flags S, classtype network-scan, priority 2, threshold type threshold, track by_src, count 5, seconds 60, reference to nmap.org.
Detects GET requests to HTTP servers containing UNION SELECT (case-insensitive) in the payload, reinforced with a PCRE pattern that also catches URL-encoded variants (%20 instead of space).
Generated fields: action alert, proto tcp, dst $HTTP_SERVERS:$HTTP_PORTS, flow to_server,established, http_method GET, content UNION SELECT with nocase, PCRE /union(\s|%20)+select/i, classtype web-application-attack, priority 1, OWASP reference.
Detects two common XSS injection patterns in the URI: literal <script> tags and javascript: protocol handlers, both case-insensitive.
Generated fields: proto tcp, dst $HTTP_SERVERS:$HTTP_PORTS, flow to_server,established, two uricontent matches with nocase, classtype web-application-attack, OWASP reference.
Detects x86 NOP sleds (consecutive 0x90 bytes) in TCP payloads larger than 128 bytes — a hallmark of classic buffer overflow exploits.
Generated fields: proto tcp, content |90 90 90 90 90 90 90 90| (hex-encoded NOP sled), PCRE /\x90{20,}/ (20+ consecutive NOPs), dsize >128, classtype shellcode-detect, priority 1.
Detects UDP packets directed at port 53 (DNS) with payload larger than 512 bytes — anomalously large DNS requests used in amplification DDoS attacks.
Generated fields: proto udp, dst port 53, dsize >512, threshold type threshold, track by_src, count 100, seconds 10, classtype attempted-dos, priority 2.
Detects ICMP Echo Request (ping) floods from a single source at a rate of more than 200 per second.
Generated fields: proto icmp, itype =8, icode =0, threshold type threshold, track by_src, count 200, seconds 1, classtype attempted-dos.
All JavaScript is contained in a single <script> block at the bottom of the HTML file. There are no external scripts or module imports.
function q(id){ return document.getElementById(id); }A shorthand helper that wraps document.getElementById. Used throughout the codebase to avoid repetition.
Parameters: id — string ID of a DOM element.
Returns: The DOM element, or null if not found.
function val(id){ const el = q(id); return el ? el.value.trim() : ''; }Retrieves the trimmed .value string from a form element. Returns an empty string if the element doesn't exist, which prevents null-reference errors during rule building.
Parameters: id — string ID of a form input, select, or textarea.
Returns: Trimmed string value, or ''.
function setDir(el){ ... dirVal = el.dataset.dir; renderRule(); }Handles clicks on the direction toggle buttons. Removes the .active class from all .dir-opt elements, adds it to the clicked one, reads the data-dir attribute ("->" or "<>"), stores it in the module-level dirVal variable, then triggers a rule re-render.
Parameters: el — the clicked .dir-opt DOM element.
Side effects: Updates dirVal, triggers renderRule().
function addContent(type){ const id = ++contentId; contentItems.push({...}); renderContents(); }Creates a new content match entry and adds it to the contentItems array. Increments the global contentId counter to generate a unique ID for the item. The new item is initialized with default values (val: '', nocase: false, rawbytes: false, all position modifiers empty).
Parameters: type — one of 'content', 'pcre', or 'uricontent'.
Side effects: Pushes to contentItems, calls renderContents().
Re-renders the entire #content_list div from scratch using the current state of the contentItems array. Uses innerHTML assignment with a .map() that builds HTML strings for each item. After rendering, re-binds PCRE flag toggle event listeners (since those elements are dynamically created). Calls renderRule() at the end to update the output.
No parameters.
Side effects: Mutates #content_list DOM, re-binds PCRE flag listeners, calls renderRule().
function removeContent(i){ contentItems.splice(i, 1); renderContents(); }Removes the content item at array index i using splice. Then re-renders the content list and (indirectly) the rule.
Parameters: i — zero-based index into contentItems.
Side effects: Mutates contentItems, calls renderContents().
function escHtml(s){ return s.replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<'); }Escapes HTML special characters in strings before embedding them in dynamically generated HTML markup (primarily for content match input values). Prevents XSS within the tool's own interface.
Parameters: s — raw string.
Returns: HTML-safe escaped string.
function addRef(){ refItems.push({ type:'url', val:'' }); renderRefs(); }Adds a new blank reference entry to the refItems array with a default type of 'url'. Calls renderRefs() to update the DOM.
No parameters.
Side effects: Pushes to refItems, calls renderRefs().
Re-renders the #refs_list div from scratch using the current refItems array state. Each item becomes a flex row with a type dropdown and a text input. Inline onchange and oninput handlers directly mutate the corresponding refItems[i] properties. Calls renderRule() at the end.
No parameters.
Side effects: Mutates #refs_list DOM, calls renderRule().
The core rule-building function. Reads all form field values and constructs an array of option objects, each representing one keyword:value pair in the Snort rule body.
Each object has the shape:
{ k: 'keyword', v: 'value', mods: [...] }Where mods is an optional array of modifier objects for content keywords (nocase, offset, depth, etc.).
The function proceeds in this order:
msg(always first)flow(if set)- TCP
flags(if any selected) http_methodcontent keyword (if set)- All items from
contentItems(including their modifiers) ttl(if operator and value both set)ip_proto(if set)itype/icode(ICMP, if set)dsize(if set)classtype(if set)priority(if set)- All items from
refItems(if value is non-empty) threshold(if all four fields are set)detection_filter(if set)gid(if set)sid(always)rev(always)
Returns: Array of option objects.
Converts the options array from buildOptions() into a flat semicolon-separated string suitable for embedding in the rule body.
For items with mods, the base keyword is emitted first, then each modifier is emitted on its own. Items with v === null are emitted as bare keywords (no colon or value).
Parameters: opts — array from buildOptions().
Returns: String like msg:"Example"; flow:established; content:"foo"; nocase; sid:1000001; rev:1
Converts the rule components into a syntax-highlighted HTML string for display in the #rule_out div. Each token type is wrapped in a <span> with a CSS class:
- Action →
<span class="t-action"> - Protocol →
<span class="t-proto"> - IP/port tokens →
<span class="t-net"> - Direction →
<span class="t-dir"> - Option keywords →
<span class="t-key"> - Quoted string values →
<span class="t-str"> - Non-string values →
<span class="t-val"> - Punctuation (
:,;,(,)) →<span class="t-punc">
Parameters: All rule header parts plus the opts array.
Returns: HTML string with <span> tags for coloring.
The master update function. Called by every event listener and after every state mutation. Orchestrates the full render cycle:
- Reads all header field values via
val(). - Calls
buildOptions()to assemble the options array. - Constructs the raw plain-text rule string using
optsToStr(). - Sets
rule_out.innerHTMLto the colorized HTML fromcolorizeRule(). - Stores the raw string on
rule_out._rawfor clipboard access. - If
#raw_outis in read-only mode, updates its.valueto the raw string. - Updates the three stats bar counters.
No parameters.
Side effects: Mutates #rule_out, #raw_out, #stat_opts, #stat_content, #stat_refs.
Reads the raw rule string from rule_out._raw (falling back to raw_out.value). Writes it to the clipboard using the async navigator.clipboard.writeText() API. On success, changes the button text to ✓ Copied!, adds the .copied CSS class (which triggers the green glow animation), and schedules a 2-second timeout to restore the original label.
No parameters. Side effects: Writes to clipboard, temporarily mutates Copy button DOM.
Resets the entire tool to its default state. Iterates over a hard-coded list of all field IDs and resets selects to index 0, sets msg and sid and rev to their defaults, and blanks all other inputs. Then:
- Resets
dirValto"->". - Removes
.activefrom all direction options, sets the first one active. - Clears
flagSelectedSet. - Removes
.activefrom all flag tags. - Empties
contentItemsandrefItemsarrays. - Clears
#content_listand#refs_listinnerHTML. - Calls
renderRule().
No parameters. Side effects: Full DOM and state reset.
Calls resetAll() first, then executes one of six template functions keyed by name string. Each template function directly sets DOM element values, pushes to contentItems/refItems as needed, and calls renderContents()/renderRefs()/renderRule() as appropriate.
Template keys: 'portscan', 'sqli', 'xss', 'shellcode', 'dns', 'icmp'.
Parameters: name — string key identifying the template.
Side effects: Full reset followed by template-specific field population.
These module-level variables hold runtime state between function calls:
| Variable | Type | Initial Value | Purpose |
|---|---|---|---|
contentItems |
Array | [] |
Stores all content/pcre/uricontent match block objects |
refItems |
Array | [] |
Stores all reference entry objects |
flagSelected |
Set | new Set() |
Stores the letter codes of currently active TCP flag tags |
dirVal |
String | '->' |
Current direction operator, updated by setDir() |
contentId |
Number | 0 |
Auto-incrementing counter used to generate unique IDs for content items |
Event listeners are attached in two ways:
Static bindings (bottom of script): A hard-coded array of 30 element IDs is iterated. Both input and change events are bound to renderRule() on each element. This covers all standard form fields.
['action','proto','src_ip', ... ].forEach(id => {
const el = q(id);
if(el) { el.addEventListener('input', renderRule); el.addEventListener('change', renderRule); }
});Dynamic bindings (inside renderContents()): Because content items are created dynamically, their inner controls use inline oninput/onchange HTML attributes that directly mutate contentItems[i] properties. PCRE flag toggles are re-bound with addEventListener after each renderContents() call because they cannot use inline handlers (they need access to closure state).
The rule output uses a two-step process:
buildOptions()produces a structured array of token objects.colorizeRule()walks the token array and wraps each token in a typed<span>.
CSS classes in the .rule-display scope apply the colors:
.rule-display .t-action { color: #f96464; font-weight: 600; } /* red, bold */
.rule-display .t-proto { color: #4f9cf9; font-weight: 600; } /* blue, bold */
.rule-display .t-net { color: #3dd68c; } /* green */
.rule-display .t-dir { color: var(--text3); } /* muted gray */
.rule-display .t-key { color: #b17cf5; } /* purple */
.rule-display .t-val { color: #f5a623; } /* amber */
.rule-display .t-str { color: #3dd8d8; } /* teal */
.rule-display .t-punc { color: var(--text3); } /* muted gray */The distinction between t-val (amber) and t-str (teal) is: values that start with " are strings and get teal; all other values (numbers, keywords, operators) get amber.
The stylesheet is embedded in the <head> and uses CSS custom properties (variables) defined on :root for a consistent dark theme.
Background layers: --bg (#0d0f14) → --bg2 (#13161e) → --bg3 (#1a1e2a) → --bg4 (#21263a)
Borders: --border (7% white) → --border2 (12%) → --border3 (18%)
Text: --text (#e8eaf2) → --text2 (#8b90a8) → --text3 (#565c78)
Accent (blue): --accent (#4f9cf9), --accent2 (#2563c4), --accent-glow (15% alpha)
Status colors: --green, --orange, --red, --purple, --teal + matching -bg variants
Two font families loaded from Google Fonts:
JetBrains Mono— used for all monospaced content: rule output, form inputs, labels, buttons. Weights 300/400/500/600.Syne— used for headings and card titles. Weights 400/500/600/700.
- The page is constrained to
max-width: 1200pxand centered with auto margins. - The main content is a CSS Grid with
1fr 340pxcolumns. - The right column is
position: stickywithtop: 24px. - Internal sections use utility grid classes:
.grid2,.grid3,.grid4.
- Below 900px: main grid collapses to single column,
grid4becomesgrid2, direction arrow hides. - Below 600px:
grid3becomesgrid2, rule header grid collapses to single column.
- Card entrance:
fadeSlideIn— each card fades in and slides up 6px with staggeredanimation-delayvalues (0.05s increments). - Content item entrance: same animation applied to dynamically rendered blocks.
- Copy flash:
copyFlashkeyframe — expands and fades an outward glow ring in green when the Copy button is clicked.
Quick-reference for what each generated keyword does inside Snort's rule engine:
| Keyword | Section | Description |
|---|---|---|
msg |
Metadata | Human-readable alert description string |
sid |
Metadata | Unique rule identifier number |
rev |
Metadata | Rule revision number |
gid |
Metadata | Generator ID (component that generates the event) |
priority |
Metadata | Override alert severity (1=highest) |
classtype |
Metadata | Associate rule with a threat category |
reference |
Metadata | Link to external documentation or CVE |
flow |
Detection | Restrict to specific TCP session state/direction |
flags |
Detection | Match TCP header flags |
http_method |
Detection | Match HTTP request method verb |
content |
Detection | Match a byte string in the packet payload |
pcre |
Detection | Match payload against a PCRE regex |
uricontent |
Detection | Match a string in the HTTP URI buffer |
nocase |
Modifier | Make preceding content match case-insensitive |
rawbytes |
Modifier | Inspect raw bytes, bypassing preprocessor normalization |
offset |
Modifier | Start content search at this byte offset |
depth |
Modifier | Limit content search to this many bytes |
distance |
Modifier | Relative offset from end of previous content match |
within |
Modifier | Window size relative to end of previous content match |
ttl |
Advanced | Match IP Time-To-Live value |
ip_proto |
Advanced | Match IP protocol number |
itype |
Advanced | Match ICMP message type |
icode |
Advanced | Match ICMP message code |
dsize |
Advanced | Match payload size in bytes |
threshold |
Advanced | Rate-based alerting and suppression |
detection_filter |
Advanced | Rate-based filtering at rule evaluation time |
- Open the tool in a browser.
- In Rule Header: select your action, protocol, and fill in source/destination IPs and ports. Use
$variables like$HOME_NETfor flexibility. - Choose direction (one-way for most rules).
- In Rule Metadata: write a clear msg, set a unique SID in the 9,000,000+ range for local rules, keep rev at 1, and choose an appropriate classtype.
- In Detection Options: set flow state (almost always
to_server,establishedfor inbound web traffic), add one or more content or pcre matches. - Review the live output. Verify the rule looks correct.
- Click Copy Rule and paste into your rules file.
- Click a template button on the right panel.
- The tool populates all fields with a working baseline rule.
- Modify the SID to your organization's range.
- Adjust the msg to match your environment's naming convention.
- Add or remove content matches as needed.
- Adjust threshold values to reduce false positives in your environment.
- Copy the rule.
- Build the rule using the form.
- Click Edit under the Raw Output textarea.
- The textarea becomes editable — make your changes directly (add raw keywords, adjust spacing, etc.).
- Copy from the textarea directly.
- If you want to re-sync with the form, click Rebuild (this will overwrite your manual edits).
- Open the tool.
- Click through different templates and observe how the output changes.
- Toggle TCP flags on and off and watch the
flags:keyword update. - Add multiple content blocks and observe chained modifiers like
distanceandwithin. - Set a threshold and observe the complete
threshold:option being built.
The Copy button doesn't work.
The Clipboard API requires either HTTPS or localhost. If you open the file over a plain file:// path in some browsers (particularly older versions), clipboard access may be blocked. Try serving the file through a local web server (python3 -m http.server in the same directory) or use the Raw Output textarea to manually select and copy.
The rule is missing some options I expect. Most optional fields only appear in the output when they have a value. Check that both halves of compound fields (operator + value for TTL, ICMP, dsize) are filled. For threshold, all four fields (type, track, count, seconds) must be populated.
Content match modifiers aren't showing. Distance and within only appear if their inputs have values. They are positional and only make sense when there are multiple content matches in sequence.
The rule fails to load in Snort.
Common causes: SID conflict with an existing rule (increment the SID), missing flow keyword (add flow:established to TCP rules), syntax error from special characters in msg (the tool escapes " but not all characters — avoid unescaped backslashes).
Google Fonts don't load (offline use). The tool works fully without the fonts — it falls back to the browser's default monospace and sans-serif. Only the typography changes, all functionality is unaffected.
PCRE flags aren't appearing in the output.
PCRE flag toggles are re-bound after each renderContents() call. If you notice flags not registering, try clicking them again — the bind may have been missed if content was removed and re-added rapidly.
This README was written to match snort-rule-generator.html as generated. If you modify the HTML file, update the relevant sections above to keep documentation accurate.