├── README.md ├── Suricata ├── Suricata.sublime-color-scheme └── Suricata.sublime-syntax ├── double_buffer.png └── showcase.png /README.md: -------------------------------------------------------------------------------- 1 | # Sublime Suricata 2 | 3 | ![Alt text](/showcase.png) 4 | 5 | Basic Suricata syntax highlighter for Sublime Text (tested on Sublime Text 4). 6 | 7 | Now built with a custom color scheme instead of just a highlighter based on existing color schemes. 8 | 9 | Includes all features from the Suricata-7.0.0rc1 branch. 10 | 11 | # Warning/Error highlighting 12 | Experimental idea - this syntax highlighter will point out some warnings/errors (warnings being rule styling that does not conform with Emerging Threats styling, errors being bad logic). Currently there are only 2 scenarios (as I said, experimental, if this is annoying it will be scrapped): 13 | - WARNING: Double spaces, highlighted with yellow background 14 | - ERROR: Double sticky buffer, highlighted with red background 15 | 16 | ![Alt text](/double_buffer.png) 17 | 18 | # Getting it working 19 | - Save the Suricata folder in your 'Packages' folder 20 | - Windows: 'C:\Users\\%user%\AppData\Roaming\Sublime Text 3\Packages' 21 | - Mac: $HOME/Library/Application Support/Sublime Text/Packages 22 | - Linux: $home/.config/sublime-text-3/Packages 23 | - Open Sublime Text 24 | - View > Syntax > Suricata 25 | - Set your color scheme to 'Suricata' 26 | -------------------------------------------------------------------------------- /Suricata/Suricata.sublime-color-scheme: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Suricata Color Scheme", 3 | "globals": 4 | { 5 | "background": "rgb(34, 34, 34)", 6 | "foreground": "#EEEEEE", 7 | "caret": "white" 8 | }, 9 | "rules": 10 | [ 11 | { 12 | "name": "Comment", 13 | "scope": "comment.suricata", 14 | "foreground": "#999999", 15 | }, 16 | { 17 | "name": "Action", 18 | "scope": "rule.action.suricata", 19 | "foreground": "#63a3db", 20 | }, 21 | { 22 | "name": "Protocol", 23 | "scope": "rule.proto.suricata", 24 | "foreground": "#fdb372", 25 | }, 26 | { 27 | "name": "Lesser Used Keywords", 28 | "scope": "rule.lesser.main.keywords.suricata", 29 | "foreground": "#ff914d", 30 | }, 31 | { 32 | "name": "Payload Keywords", 33 | "scope": "rule.payload.keywords.suricata", 34 | "foreground": "#d6696e", 35 | }, 36 | { 37 | "name": "PCRE", 38 | "scope": "rule.pcre.suricata", 39 | "foreground": "#a37cf2", 40 | }, 41 | { 42 | "name": "More Common Keywords", 43 | "scope": "rule.common.main.keywords.suricata", 44 | "foreground": "#fcf705", 45 | }, 46 | { 47 | "name": "App-layer-event", 48 | "scope": "rule.app.layer.event.suricata", 49 | "foreground": "#d1ff5e", 50 | }, 51 | { 52 | "name": "Fast Pattern", 53 | "scope": "rule.fast.pattern.suricata", 54 | "foreground": "#56b2f0", 55 | }, 56 | { 57 | "name": "Xbits/Flowbits", 58 | "scope": "rule.flow.suricata", 59 | "foreground": "#5bf076", 60 | }, 61 | { 62 | "name": "Negations", 63 | "scope": "rule.negations.suricata", 64 | "foreground": "#f71115", 65 | }, 66 | { 67 | "name": "Metadata", 68 | "scope": "rule.metadata.suricata", 69 | "foreground": "#de99e0", 70 | }, 71 | { 72 | "name": "String", 73 | "scope": "string", 74 | "foreground": "hsla(50, 100%, 80%, 1)", 75 | }, 76 | { 77 | "name": "WARNING", 78 | "scope": "rule.warning.suricata", 79 | "background": "#fcf705", 80 | }, 81 | { 82 | "name": "ERROR", 83 | "scope": "rule.error.suricata", 84 | "foreground": "#000000", 85 | "background": "#e60b25", 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /Suricata/Suricata.sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | # See http://www.sublimetext.com/docs/syntax.html 4 | name: Suricata 5 | file_extensions: 6 | - rules 7 | scope: source.example-c 8 | 9 | contexts: 10 | main: 11 | # Negations 12 | - match: '[a-z]+:![^;]+;' 13 | scope: rule.negations.suricata 14 | 15 | # URL Reference Schema 16 | - match: 'reference:url,https?://[^;]+;' 17 | scope: rule.error.suricata 18 | 19 | # Bad MD5 Reference 20 | - match: 'reference:md5,([A-Fa-f0-9]{,31}|[A-Fa-f0-9]{33,});' 21 | scope: rule.error.suricata 22 | 23 | # Error - Missing semi-colon at the end of a content 24 | - match: 'content:\"[^\"]+\"(?!;)' 25 | scope: rule.error.suricata 26 | 27 | # Error - Missing semi-colon at end of buffer 28 | - match: '(?:dns\.query|http\.method|(?:http\.uri(?!\.raw)|http\.uri\.raw)|http\.cookie|http\.user_agent|http\.accept(?!_(?:enc|lang))|http\.accept_enc|http\.accept_lang|http\.connection|http\.content_len|http\.content_type|http\.referer|http\.start|http\.header_names|http\.request_body|http\.stat_code|http\.stat_msg|http\.response_line|http\.response_body|http\.server|http\.location|http\.host(?!\.raw)|http\.host\.raw|file[\._]data)(?!;)' 29 | scope: rule.error.suricata 30 | 31 | # Error - Missing semi-colon on no option payload keywords 32 | - match: '(?:nocase|startswith|endswith|fast_pattern)(?!;)' 33 | scope: rule.error.suricata 34 | 35 | # Error - Missing semi-colon on transformations 36 | - match: '\b(?:dotprefix|strip_whitespace|compress_whitespace|to_(md5|sha1|sha256)|pcrexform|url_decode|xor)(?!;)\b' 37 | scope: rule.error.suricata 38 | 39 | # Error - Missing options or semi-colon payload keywords that accept option 40 | # Support [a-zA-Z0-9\-_] for byte_extract variable names 41 | # This also catches unrequired spaces 42 | - match: '\b(depth|offset|distance|within)(?!:!?([0-9]+|[a-zA-Z\-_]+);)' 43 | scope: rule.error.suricata 44 | 45 | # Error - Missing options on bsize/dsize payload keywords 46 | # Support [a-zA-Z0-9\-_] for byte_extract variable names 47 | # Support for ranges ie 100<>250 48 | # This also catches unrequired spaces 49 | - match: '\b[bd]size(?!:!?(?:[0-9]+|[a-zA-Z0-9\-_]+)?(?:<|>|[<>]=|<>)?(?:[0-9]+|[a-zA-Z0-9\-_]+);)' 50 | scope: rule.error.suricata 51 | 52 | # Error - Missing options or semi-colon on isdataat 53 | # Support for optional ,relative 54 | # Support [a-zA-Z0-9\-_] for byte_extract variable names 55 | # This also catches unrequired spaces 56 | - match: '\bisdataat(?!:!?(?:[0-9]+|[a-zA-Z\-_]+)(,relative)?;)' 57 | scope: rule.error.suricata 58 | 59 | # Error - Missing options or semi-colon payload keywords that accept option 60 | # This also catches unrequired spaces 61 | - match: '\bdns.opcode(?!:!?[0-9]+;)' 62 | scope: rule.error.suricata 63 | 64 | # Error - Double Sticky Buffer 65 | - match: '(dns\.query;\s*dns\.query;|dns\.opcode;\s*dns\.opcode;|http\.method;\s*http\.method;|http\.uri;\s*http\.uri;|http\.uri\.raw;\s*http\.uri\.raw;|http\.cookie;\s*http\.cookie;|http\.user_agent;\s*http\.user_agent;|http\.accept;\s*http\.accept;|http\.accept_enc;\s*http\.accept_enc;|http\.accept_lang;\s*http\.accept_lang;|http\.connection;\s*http\.connection;|http\.content_len;\s*http\.content_len|http\.content_type;\s*http\.content_type|http\.referer;\s*http\.referer;|http\.start;\s*http\.start;|http\.header_names;\s*http\.header_names;|http\.request_body;\s*http\.request_body;|http\.stat_code;\s*http\.stat_code;|http\.stat_msg;\s*http\.stat_msg;|http\.response_line;\s*http\.response_line|http\.response_body;\s*http\.response_body;|http\.server;\s*http\.server;|http\.location;\s*http\.location|http\.host;\s*http\.host;|http\.host\.raw;\s*http\.host\.raw;|file[\._]data;\s*file[\._]data;)' 66 | scope: rule.error.suricata 67 | 68 | # Warning - Double Space 69 | - match: ';\s{2,}' 70 | scope: rule.warning.suricata 71 | 72 | # Character 73 | - match: '"' 74 | scope: rule.double.quoted.suricata 75 | push: double_quoted_string 76 | 77 | # Comments 78 | - match: '#' 79 | scope: punctuation.definition.comment.example-c 80 | push: line_comment 81 | 82 | # Actions 83 | - match: '\b(alert|drop|reject|pass)\b' 84 | scope: rule.action.suricata 85 | 86 | # Protocols 87 | - match: '\s(http|dns|tcp|udp|icmp|ip|ftp|tls|smb|dcerpc|dhcp|ssh|smtp|imap|modbus|dnp3|enip|nfs|ike|krb5|bittorrent-dht|ntp|rfb|rdp|snmp|tftp|sip|http2)\s' 88 | scope: rule.proto.suricata 89 | 90 | # App-layer-event/dataset keywords 91 | - match: '\b(app-layer-event:|dataset)\b' 92 | scope: rule.app.layer.event.suricata 93 | 94 | # TCP/UDP keywords 95 | - match: '\b(seq|ack|window|tcp\.(mss|hdr)|udp\.hdr)' 96 | scope: rule.lesser.main.keywords.suricata 97 | 98 | # IP keywords 99 | - match: '\s(ttl|ipopts|sameip|ip_proto|ipv4\.hdr|ipv6\.hdr|id|geoip|fragbits|fragoffset|tos|iprep)|ip\.(src|dst)' 100 | scope: rule.lesser.main.keywords.suricata 101 | 102 | # ICMP keywords 103 | - match: '\b(itype|icode|icmp_(id|seq)|icmpv(4|6)\.hdr)\b' 104 | scope: rule.lesser.main.keywords.suricata 105 | 106 | # HTTP keywords 107 | - match: '\b(http\.(method|uri(\.raw)?|protocol|request_line|header(\.raw)?|cookie|user_agent|accept(_enc|_lang)?|connection|content(_len|_type)?|referer|start|header_names|request_body|stat(_code|_msg)|response_line|response_body|server|location|host(\.raw)?)|file[\._]data|urilen)\b' 108 | scope: rule.common.main.keywords.suricata 109 | 110 | # DNS keywords 111 | - match: '\b(dns\.(query|opcode))\b' 112 | scope: rule.common.main.keywords.suricata 113 | 114 | # TLS keywords 115 | - match: '\b(tls\.(cert_(subject|issuer|serial|fingerprint))|sni|certs|version|subject|issuerdn|fingerprint|store|random(_time|_bytes)?)|ssl_(version|state)|tls_(cert_(notbefore|notafter|expired|valid))\b' 116 | scope: rule.common.main.keywords.suricata 117 | 118 | # Payload keywords 119 | - match: '\b(content|nocase|depth|startswith|endswith|offset|distance|within|rawbytes|isdataat|[bd]size|byte_(test|math|jump|extract)|rpc|replace)\b' 120 | scope: rule.payload.keywords.suricata 121 | 122 | # Transformations 123 | - match: '\b(dotprefix|strip_whitespace|compress_whitespace|to_(md5|sha1|sha256)|pcrexform|url_decode|xor)\b' 124 | scope: rule.lesser.main.keywords.suricata 125 | 126 | # SSH keywords 127 | - match: '\b(ssh\.(proto|software|protoversion|softwareversion|hassh(\.(string|server(\.string)?)?)?))\b' 128 | scope: rule.lesser.main.keywords.suricata 129 | 130 | # File keywords 131 | - match: '\bfile(name|ext|magic|store|md5|sha1|sha256|size)\b' 132 | scope: rule.lesser.main.keywords.suricata 133 | 134 | # JA3 keywords 135 | - match: '\bja3s?\.(hash|string)\b' 136 | scope: rule.lesser.main.keywords.suricata 137 | 138 | # MODBUS keywords 139 | #- match: '\bmodbus:\s?(access|function|unit)' 140 | # scope: rule.lesser.main.keywords.suricata 141 | 142 | # DCERPC keywords 143 | - match: '\bdcerpc\.(iface|opnum|stub_data)\b' 144 | scope: rule.lesser.main.keywords.suricata 145 | 146 | # DHCP keywords 147 | - match: '\bdhcp\.(leasetime|rebinding_time|renewal_time)\b' 148 | scope: rule.lesser.main.keywords.suricata 149 | 150 | # DNP3 keywords 151 | - match: '\bdnp3_(func|ind|obj|data)\b' 152 | scope: rule.lesser.main.keywords.suricata 153 | 154 | # ENIP/CIP keywords 155 | - match: '\b(enip_command|cip_service)\b' 156 | scope: rule.lesser.main.keywords.suricata 157 | 158 | # FTP/FTP-DATA keywords 159 | - match: '\b(ftpdata_command|ftpbounce)\b' 160 | scope: rule.lesser.main.keywords.suricata 161 | 162 | # Kerberos keywords 163 | - match: '\b(krb5_(msg_type|cname|sname|err_code)|krb5\.(weak_encryption|malformed_data|ticket_encryption))\b' 164 | scope: rule.lesser.main.keywords.suricata 165 | 166 | # SNMP keywords 167 | - match: '\bsnmp\.(version|community|usm|pdu_type)\b' 168 | scope: rule.lesser.main.keywords.suricata 169 | 170 | # Base64 keywords 171 | - match: '\bbase64_d(ecode|ata)\b' 172 | scope: rule.lesser.main.keywords.suricata 173 | 174 | # SIP keywords 175 | - match: '\bsip\.(method|uri|request_line|stat_(code|msg)|response_line|protocol)\b' 176 | scope: rule.lesser.main.keywords.suricata 177 | 178 | # RFB keywords 179 | - match: '\brfb\.(name|secresult|sectype)\b' 180 | scope: rule.lesser.main.keywords.suricata 181 | 182 | # MQTT keywords 183 | - match: '\bmqtt\.(protocol_version|type|flags|qos|reason_code|connack\.session_present|connect\.(clientid|flags|password|username|willmessage|willtopic)|publish\.(message|topic)|subscribe\.topic|unsubscribe\.topic)\b' 184 | scope: rule.lesser.main.keywords.suricata 185 | 186 | # IKE keywords 187 | - match: '\bike\.(init_spi|resp_spi|chosen_sa_attribute|exchtype|vendor|key_exchange_payload|key_exchange_payload_length|nonce_payload(_length)?)\b' 188 | scope: rule.lesser.main.keywords.suricata 189 | 190 | # HTTP2 keywords 191 | - match: '\bhttp2\.(frametype|errorcode|priority|window|size_update|settings|header_name|header)\b' 192 | scope: rule.common.main.keywords.suricata 193 | 194 | # QUIC keywords 195 | - match: '\bquic\.(cyu\.(hash|string)|version)\b' 196 | scope: rule.lesser.main.keywords.suricata 197 | 198 | # Xbits/Flow/Threshold keywords 199 | - match: '\b(xbits|flow(bits|int|\.age)?|stream_size|threshold)\b' 200 | scope: rule.flow.suricata 201 | 202 | # PCRE 203 | - match: '\bpcre\b' 204 | scope: rule.pcre.suricata 205 | 206 | # Prefiltering keywords 207 | - match: '\b(prefilter|fast_pattern)\b' 208 | scope: rule.fast.pattern.suricata 209 | 210 | # Metadata 211 | - match: '\b(sid|rev|gid|classtype|reference|priority|target|metadata)\b' 212 | scope: rule.metadata.suricata 213 | 214 | double_quoted_string: 215 | - meta_scope: string.quoted.double.example-c 216 | - match: '\\.' 217 | scope: constant.character.escape.example-c 218 | - match: '"' 219 | scope: punctuation.definition.string.end.example-c 220 | pop: true 221 | 222 | line_comment: 223 | - meta_scope: comment.suricata 224 | - match: $ 225 | pop: true 226 | -------------------------------------------------------------------------------- /double_buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozuriexv/SublimeSuricata/b75bcc3b09708ed1a89030f5e82281ba796fd032/double_buffer.png -------------------------------------------------------------------------------- /showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozuriexv/SublimeSuricata/b75bcc3b09708ed1a89030f5e82281ba796fd032/showcase.png --------------------------------------------------------------------------------