From 3f11a370ead5ec1dee7279e4c091f79022225747 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 12:32:33 +0100 Subject: [PATCH 01/13] Replace Python SQLi script with language-agnostic Hurl test harness The old tests/spiracle_sqli_test.py was Python 2 and used a bespoke data format. Replace it with Hurl (hurl.dev): a generator turns the existing mysql.txt/oracle.txt payload matrices into .hurl files under tests/hurl/rasp/, with the block status as a {{block_status}} variable. Because the 550 block code is only emitted when the Waratek RASP agent intercepts the query, that suite is RASP-efficacy only; a separate tests/hurl/smoke/ suite runs against a plain (unprotected) deployment for CI, proving the app serves and that injections succeed unprotected. Includes run.sh and docs. --- tests/hurl/README.md | 134 ++++++++++++ tests/hurl/generate.py | 173 +++++++++++++++ tests/hurl/rasp/mysql/get_implicit_join.hurl | 179 +++++++++++++++ tests/hurl/rasp/mysql/get_int.hurl | 179 +++++++++++++++ tests/hurl/rasp/mysql/get_string.hurl | 119 ++++++++++ tests/hurl/rasp/mysql/get_union.hurl | 173 +++++++++++++++ .../rasp/mysql/implicit_join_namespace.hurl | 179 +++++++++++++++ .../hurl/rasp/oracle/get_full_outer_join.hurl | 173 +++++++++++++++ tests/hurl/rasp/oracle/get_implicit_join.hurl | 173 +++++++++++++++ tests/hurl/rasp/oracle/get_int.hurl | 179 +++++++++++++++ tests/hurl/rasp/oracle/get_int_groupby.hurl | 11 + tests/hurl/rasp/oracle/get_int_having.hurl | 11 + tests/hurl/rasp/oracle/get_int_inline.hurl | 191 ++++++++++++++++ tests/hurl/rasp/oracle/get_int_no_quote.hurl | 197 +++++++++++++++++ tests/hurl/rasp/oracle/get_int_nooutput.hurl | 179 +++++++++++++++ .../rasp/oracle/get_int_partialunion.hurl | 179 +++++++++++++++ tests/hurl/rasp/oracle/get_string.hurl | 125 +++++++++++ .../hurl/rasp/oracle/get_string_no_quote.hurl | 203 ++++++++++++++++++ tests/hurl/rasp/oracle/get_union.hurl | 173 +++++++++++++++ tests/hurl/rasp/protected.env | 3 + tests/hurl/run.sh | 71 ++++++ tests/hurl/smoke/local.env | 2 + tests/hurl/smoke/smoke.hurl | 32 +++ tests/spiracle_sqli_test.py | 51 ----- 24 files changed, 3038 insertions(+), 51 deletions(-) create mode 100644 tests/hurl/README.md create mode 100644 tests/hurl/generate.py create mode 100644 tests/hurl/rasp/mysql/get_implicit_join.hurl create mode 100644 tests/hurl/rasp/mysql/get_int.hurl create mode 100644 tests/hurl/rasp/mysql/get_string.hurl create mode 100644 tests/hurl/rasp/mysql/get_union.hurl create mode 100644 tests/hurl/rasp/mysql/implicit_join_namespace.hurl create mode 100644 tests/hurl/rasp/oracle/get_full_outer_join.hurl create mode 100644 tests/hurl/rasp/oracle/get_implicit_join.hurl create mode 100644 tests/hurl/rasp/oracle/get_int.hurl create mode 100644 tests/hurl/rasp/oracle/get_int_groupby.hurl create mode 100644 tests/hurl/rasp/oracle/get_int_having.hurl create mode 100644 tests/hurl/rasp/oracle/get_int_inline.hurl create mode 100644 tests/hurl/rasp/oracle/get_int_no_quote.hurl create mode 100644 tests/hurl/rasp/oracle/get_int_nooutput.hurl create mode 100644 tests/hurl/rasp/oracle/get_int_partialunion.hurl create mode 100644 tests/hurl/rasp/oracle/get_string.hurl create mode 100644 tests/hurl/rasp/oracle/get_string_no_quote.hurl create mode 100644 tests/hurl/rasp/oracle/get_union.hurl create mode 100644 tests/hurl/rasp/protected.env create mode 100755 tests/hurl/run.sh create mode 100644 tests/hurl/smoke/local.env create mode 100644 tests/hurl/smoke/smoke.hurl delete mode 100755 tests/spiracle_sqli_test.py diff --git a/tests/hurl/README.md b/tests/hurl/README.md new file mode 100644 index 0000000..9663d9f --- /dev/null +++ b/tests/hurl/README.md @@ -0,0 +1,134 @@ +# Spiracle Hurl Test Harness + +Language-agnostic HTTP tests using [Hurl](https://hurl.dev) (v5+). +Replaces the old `tests/spiracle_sqli_test.py` (Python 2, bespoke `` format). + +--- + +## The critical semantic: 550 requires the Waratek RASP agent + +The number `550` is **not** a standard HTTP status code. +`SelectUtil.verifySQLException` emits it **only** when the SQLException +message is exactly `"Attempted to execute a query with one or more bad +parameters."` — that string is produced by the **Waratek RASP agent** +intercepting the query before it reaches the database. + +**Without the Waratek agent** (e.g. plain Tomcat, CI Docker stack): + +| Payload type | Status code | +|--------------------------|-------------| +| Valid-SQL injection | **200** | +| Malformed/syntax error | **500** | +| Agent-blocked injection | **550** | + +You will **never** see 550 on a plain deployment. +The RASP suite will fail entirely without the agent — this is expected. + +--- + +## Suite layout + +``` +tests/hurl/ +├── generate.py # Generator: mysql.txt + oracle.txt → .hurl files +├── run.sh # Runner wrapper +├── rasp/ # RASP-efficacy suite (needs Waratek agent) +│ ├── protected.env # Variables: host, port, block_status=550 +│ ├── mysql/ # MySQL servlet tests (139 cases, 5 files) +│ │ ├── get_int.hurl +│ │ ├── get_string.hurl +│ │ ├── get_union.hurl +│ │ ├── get_implicit_join.hurl +│ │ └── implicit_join_namespace.hurl +│ └── oracle/ # Oracle servlet tests (301 cases, 12 files) +│ ├── get_int.hurl +│ ├── get_string.hurl +│ └── ... +└── smoke/ # Functional smoke suite (no agent required) + ├── local.env # Variables: host=localhost, port=8080 + └── smoke.hurl # 3 tests: up-check, benign query, SQLi succeeds +``` + +Source of truth for the RASP payload matrices: +- `tests/mysql.txt` (139 cases) +- `tests/oracle.txt` (301 cases) + +--- + +## Running the smoke suite (plain Docker / CI) + +The smoke suite validates: +1. App root responds `200` +2. `GET /spiracle/MySql_Get_string?name=Patrick` → `200`, body contains `Moss` +3. SQLi payload widens the result set (body contains `Thomas`) → `200` + (documenting that injections are NOT blocked without the agent) + +```sh +# Start the Docker MySQL stack (requires docker-compose from feat/docker branch) +docker-compose up -d + +# Run smoke tests +./tests/hurl/run.sh smoke localhost 8080 + +# Or with hurl directly +hurl --test --variables-file tests/hurl/smoke/local.env \ + tests/hurl/smoke/smoke.hurl +``` + +--- + +## Running the RASP suite (Waratek agent required) + +```sh +# With agent attached to Tomcat: +./tests/hurl/run.sh rasp localhost 8080 + +# Override host/port: +./tests/hurl/run.sh rasp myserver.internal 9090 + +# Override expected block status (if agent uses a different code): +BLOCK_STATUS=403 ./tests/hurl/run.sh rasp localhost 8080 + +# Run a single servlet's cases: +hurl --test \ + --variables-file tests/hurl/rasp/protected.env \ + tests/hurl/rasp/mysql/get_int.hurl +``` + +Reports are written as JUnit XML to `/tmp/spiracle-{smoke,rasp}-report/junit.xml`. +Override with `REPORT_DIR=/path/to/dir ./tests/hurl/run.sh ...`. + +--- + +## Regenerating the .hurl files + +If `mysql.txt` or `oracle.txt` are updated, regenerate: + +```sh +python3 tests/hurl/generate.py +``` + +The generator: +- Reads `tests/mysql.txt` and `tests/oracle.txt` (one case per line, `` delimiter) +- Groups cases by servlet path +- Encodes URL-illegal characters (`space`, `|`, `"`, `<`, `>`) in query strings +- Emits `status == {{block_status}}` for 550-expected cases (variable-driven) +- Emits `status == 200` (literal) for the one benign probe case in mysql.txt +- Overwrites all files under `tests/hurl/rasp/` + +Commit the regenerated files — the suite must run without needing to regenerate. + +--- + +## Hurl assertion form used + +`HTTP {{var}}` in the status line is **not** valid in Hurl 5.x. +All files use the `[Asserts]` form: + +``` +HTTP * +[Asserts] +status == {{block_status}} +``` + +This was verified against Hurl 5.0.1 before committing. diff --git a/tests/hurl/generate.py b/tests/hurl/generate.py new file mode 100644 index 0000000..4c56310 --- /dev/null +++ b/tests/hurl/generate.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +""" +Generate Hurl RASP test files from the -delimited payload matrices. + +Usage: + python3 tests/hurl/generate.py + +Reads: + tests/mysql.txt + tests/oracle.txt + +Writes: + tests/hurl/rasp/mysql/.hurl + tests/hurl/rasp/oracle/.hurl + +Each file contains one Hurl entry per test case for that servlet. +Status assertion uses: + HTTP * + [Asserts] + status == {{block_status}} + +so the expected status (550 for RASP-blocked, 200 for benign) is +injected at runtime via --variable block_status=550. + +NOTE: Raw spaces in URLs break Hurl's URL parser; the generator +percent-encodes bare spaces (0x20) only, preserving all other +characters (including already-encoded sequences like %25, %27, etc.) +exactly as they appear in the source files. +""" + +import os +import re +import sys +from collections import defaultdict + +TESTS_DIR = os.path.join(os.path.dirname(__file__), "..") +RASP_DIR = os.path.join(os.path.dirname(__file__), "rasp") + +SOURCES = { + "mysql": os.path.join(TESTS_DIR, "mysql.txt"), + "oracle": os.path.join(TESTS_DIR, "oracle.txt"), +} + +# Map servlet path segment → output filename (lowercase, underscores) +SERVLET_NAME_MAP = { + # MySQL servlets + "MySql_Get_int": "get_int", + "MySql_Get_string": "get_string", + "MySql_Get_Implicit_Join": "get_implicit_join", + "MySql_Implicit_Join_Namespace": "implicit_join_namespace", + "Get_Union": "get_union", # shared by both; mysql.txt uses it + # Oracle servlets + "Get_int": "get_int", + "Get_int_no_quote": "get_int_no_quote", + "Get_int_partialunion": "get_int_partialunion", + "Get_int_groupby": "get_int_groupby", + "Get_int_nooutput": "get_int_nooutput", + "Get_int_having": "get_int_having", + "Get_int_inline": "get_int_inline", + "Get_string": "get_string", + "Get_string_no_quote": "get_string_no_quote", + "Get_Implicit_Join": "get_implicit_join", + "Get_Full_Outer_Join": "get_full_outer_join", +} + + +def encode_url_illegal(s): + """ + Percent-encode characters that Hurl's URL parser rejects in GET lines. + + Hurl rejects: space, |, ", <, > + Everything else (including already-encoded %xx sequences, ', (, ), etc.) + is left intact so payload semantics are preserved exactly. + """ + replacements = [ + (" ", "%20"), + ("|", "%7C"), + ('"', "%22"), + ("<", "%3C"), + (">", "%3E"), + ] + for char, enc in replacements: + s = s.replace(char, enc) + return s + + +def servlet_from_path(path): + """Extract servlet name from /spiracle/.""" + return path.lstrip("/").split("/")[-1] + + +def output_filename(servlet): + return SERVLET_NAME_MAP.get(servlet, servlet.lower()) + ".hurl" + + +def parse_data_file(filepath): + """Return list of (path, querystring, expected_status) tuples.""" + cases = [] + with open(filepath, encoding="utf-8") as f: + for lineno, line in enumerate(f, 1): + line = line.rstrip("\n") + if not line: + continue + parts = line.split("") + if len(parts) != 3: + print( + f"WARNING: {filepath}:{lineno} — expected 3 parts, got {len(parts)}: {line!r}", + file=sys.stderr, + ) + continue + cases.append((parts[0], parts[1], parts[2])) + return cases + + +def generate_hurl_file(cases, base_url_template): + """ + Build Hurl file content for a list of (path, querystring, expected_status). + base_url_template: string with {path} and {querystring} slots. + """ + lines = [] + for path, qs, expected_status in cases: + # Encode URL-illegal chars in querystring only (not path) + safe_qs = encode_url_illegal(qs) + url = "http://{{{{host}}}}:{{{{port}}}}{path}{qs}".format( + path=path, qs=safe_qs + ) + lines.append(f"GET {url}") + lines.append("") + lines.append("HTTP *") + lines.append("[Asserts]") + # Use {{block_status}} variable for the standard blocked status (550). + # Cases with a different expected status (e.g. 200 for a benign probe) + # get the literal value so they remain correct regardless of variables. + if expected_status == "550": + lines.append(f"status == {{{{block_status}}}}") + else: + lines.append(f"status == {expected_status}") + lines.append("") + return "\n".join(lines) + + +def main(): + total = 0 + by_db = {} + + for db, filepath in SOURCES.items(): + cases = parse_data_file(filepath) + print(f"Read {len(cases)} cases from {filepath}") + total += len(cases) + by_db[db] = cases + + # Group by servlet + groups = defaultdict(list) + for path, qs, expected_status in cases: + servlet = servlet_from_path(path) + groups[servlet].append((path, qs, expected_status)) + + out_dir = os.path.join(RASP_DIR, db) + os.makedirs(out_dir, exist_ok=True) + + for servlet, servlet_cases in sorted(groups.items()): + fname = output_filename(servlet) + out_path = os.path.join(out_dir, fname) + content = generate_hurl_file(servlet_cases, "") + with open(out_path, "w", encoding="utf-8") as f: + f.write(content) + print(f" Wrote {len(servlet_cases):3d} cases → {out_path}") + + print(f"\nTotal: {total} cases converted.") + + +if __name__ == "__main__": + main() diff --git a/tests/hurl/rasp/mysql/get_implicit_join.hurl b/tests/hurl/rasp/mysql/get_implicit_join.hurl new file mode 100644 index 0000000..4e2428d --- /dev/null +++ b/tests/hurl/rasp/mysql/get_implicit_join.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20or%20users.id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20or%20'users.name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=''%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=''%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=%27%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mysql/get_int.hurl b/tests/hurl/rasp/mysql/get_int.hurl new file mode 100644 index 0000000..ce71aeb --- /dev/null +++ b/tests/hurl/rasp/mysql/get_int.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=2'%20OR%20name%20LIKE%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20%7C%7C%20name%20like%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20or%20users.name%20LIKE%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20or%20id%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20OR%20100=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20OR%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20OR%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20OR%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20or%201=1%20or%20'x'='y'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=a'%20or%20'a'%20=%20'a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=%27%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id='%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1'%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mysql/get_string.hurl b/tests/hurl/rasp/mysql/get_string.hurl new file mode 100644 index 0000000..8ab638e --- /dev/null +++ b/tests/hurl/rasp/mysql/get_string.hurl @@ -0,0 +1,119 @@ +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=x'%20or%20name%20like%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=%3C%3E%22%2527%25%3B)(%26%2B + +HTTP * +[Asserts] +status == 200 + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=x'%20AND%20100=(SELECT%20COUNT(*)%20FROM%20address)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=x'%20OR%20cvv%20IS%20NULL-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=anything'%20OR%20'x'='x + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=x'%20or%201=1%20or%20'x'='y + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='or%201=1%20or%20''=' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=a'%20or%20'a'%20=%20'a + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='%20UNION%20SELECT%20*%20from%20users-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=%27%20or%20%27x%27=%27x + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='%20or%201%20--' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name='%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=x'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mysql/get_union.hurl b/tests/hurl/rasp/mysql/get_union.hurl new file mode 100644 index 0000000..9b88ada --- /dev/null +++ b/tests/hurl/rasp/mysql/get_union.hurl @@ -0,0 +1,173 @@ +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20'name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mysql/implicit_join_namespace.hurl b/tests/hurl/rasp/mysql/implicit_join_namespace.hurl new file mode 100644 index 0000000..db24b55 --- /dev/null +++ b/tests/hurl/rasp/mysql/implicit_join_namespace.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20or%20users.id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20or%20'users.name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=''%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=''%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MySql_Implicit_Join_Namespace?id=%27%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_full_outer_join.hurl b/tests/hurl/rasp/oracle/get_full_outer_join.hurl new file mode 100644 index 0000000..477e1fd --- /dev/null +++ b/tests/hurl/rasp/oracle/get_full_outer_join.hurl @@ -0,0 +1,173 @@ +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20or%20users.id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20or%20'users.name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=''%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=%27%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Full_Outer_Join?id=''%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_implicit_join.hurl b/tests/hurl/rasp/oracle/get_implicit_join.hurl new file mode 100644 index 0000000..6b2df79 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_implicit_join.hurl @@ -0,0 +1,173 @@ +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20or%20users.id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20or%20'users.name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=''%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=''%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Implicit_Join?id=%27%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int.hurl b/tests/hurl/rasp/oracle/get_int.hurl new file mode 100644 index 0000000..4664b88 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int?id=2'%20OR%20name%20LIKE%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20or%20name%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20or%20id%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id='%20or%20name%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20AND%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20AND%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20OR%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20or%201=1%20or%20'x'='y'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=a'%20or%20'a'%20=%20'a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id='%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=1'%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int_groupby.hurl b/tests/hurl/rasp/oracle/get_int_groupby.hurl new file mode 100644 index 0000000..9225de0 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int_groupby.hurl @@ -0,0 +1,11 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int_groupby?id=name%20union%20all%20select%20null,%20to_char(dob)%20from%20users + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_groupby?id=name%20union%20select%20null,%20to_char(dob)%20from%20users + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int_having.hurl b/tests/hurl/rasp/oracle/get_int_having.hurl new file mode 100644 index 0000000..253fb73 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int_having.hurl @@ -0,0 +1,11 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int_having?id=1%20union%20select%20to_char(dob)%20from%20users + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_having?id=1%20union%20all%20select%20to_char(dob)%20from%20users + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int_inline.hurl b/tests/hurl/rasp/oracle/get_int_inline.hurl new file mode 100644 index 0000000..9983372 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int_inline.hurl @@ -0,0 +1,191 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20'x'%20OR%20name%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20''%20or%20name%20like%20'' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20id%20=%20''%20or%20id%20like%20'' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20''%20or%20name%20like%20'' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20='x'%20AND%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20'x'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20='Joe'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20='Joe'%20AND%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20='anything'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20'x'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20='Joe'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20''%20or%201=1%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20'a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20'joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20='Joe'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name%20=%20(%27%27)%20or%20(%27x%27=%27x%27) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name=('')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='hi'%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name=''%20or%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20or%200=0%20# + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe%20%27%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_inline?id=select%20*%20from%20users%20where%20name='Joe'%20or%203=3 + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int_no_quote.hurl b/tests/hurl/rasp/oracle/get_int_no_quote.hurl new file mode 100644 index 0000000..c4d8ab6 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int_no_quote.hurl @@ -0,0 +1,197 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=2%20OR%20name%20LIKE%20'%25' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%20name%20like%20'%25' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%20id%20like%20'%25' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%20name%20like%20'%25' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20AND%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20AND%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%201=1%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=''%20or%201=1%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=(%27%27)%20or%20(%27x%27=%27x%27) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=('')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='hi'%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=''%20or%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='Joe'%20or%200=0%20# + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='Joe'%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id=1%20or%20''='' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='Joe'%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='Joe'%20%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='Joe%20%27%20or%201=1 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_no_quote?id='Joe'%20or%203=3 + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int_nooutput.hurl b/tests/hurl/rasp/oracle/get_int_nooutput.hurl new file mode 100644 index 0000000..bb6e27e --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int_nooutput.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=2'%20OR%20name%20LIKE%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20or%20name%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20or%20id%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20or%20name%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20AND%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20AND%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20OR%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20or%201=1%20or%20'x'='y'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=a'%20or%20'a'%20=%20'a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1'%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id=1%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_nooutput?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_int_partialunion.hurl b/tests/hurl/rasp/oracle/get_int_partialunion.hurl new file mode 100644 index 0000000..a457a3b --- /dev/null +++ b/tests/hurl/rasp/oracle/get_int_partialunion.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=2'%20OR%20name%20LIKE%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20or%20name%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20or%20id%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20or%20name%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20AND%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20AND%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20OR%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20or%201=1%20or%20'x'='y'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=a'%20or%20'a'%20=%20'a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=1'%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_int_partialunion?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_string.hurl b/tests/hurl/rasp/oracle/get_string.hurl new file mode 100644 index 0000000..b7a8263 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_string.hurl @@ -0,0 +1,125 @@ +GET http://{{host}}:{{port}}/spiracle/Get_string?name=x'%20or%20name%20like%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=t'exec%20master..xp_cmdshell%20'nslookup%20www%2egoogle%2ecom'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%3b%20exec%20master%2e%2exp_cmdshell%20'ping%20172%2e10%2e1%2e255'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=x'%20AND%20100=(SELECT%20COUNT(*)%20FROM%20address)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=x'%20OR%20cvv%20IS%20NULL-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=anything'%20OR%20'x'='x + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=x'%20or%201=1%20or%20'x'='y + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='or%201=1%20or%20''=' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=a'%20or%20'a'%20=%20'a + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%20UNION%20SELECT%20*%20from%20users-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=%27%20or%20%27x%27=%27x + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%20or%201%20--' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name='%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string?name=x'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_string_no_quote.hurl b/tests/hurl/rasp/oracle/get_string_no_quote.hurl new file mode 100644 index 0000000..a7ac1d7 --- /dev/null +++ b/tests/hurl/rasp/oracle/get_string_no_quote.hurl @@ -0,0 +1,203 @@ +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='2'%20OR%20name%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Jon'%20or%20name%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20or%20id%20like%20'2' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20or%20name%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20or%20name%20like%20'Sammy' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20AND%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20AND%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20or%201=1%20or%20'x'='y'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=(1)%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=1%20or%20'a'%20=%20'a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=1%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=('')%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=('')%20or%20('x'='x')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=''%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=%27%27%20or%20%27x%27%3D%27x%27-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20or%200=0%20# + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='1'%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe%20%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_string_no_quote?name='Joe'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/oracle/get_union.hurl b/tests/hurl/rasp/oracle/get_union.hurl new file mode 100644 index 0000000..9b88ada --- /dev/null +++ b/tests/hurl/rasp/oracle/get_union.hurl @@ -0,0 +1,173 @@ +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20'name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/protected.env b/tests/hurl/rasp/protected.env new file mode 100644 index 0000000..b6c1f83 --- /dev/null +++ b/tests/hurl/rasp/protected.env @@ -0,0 +1,3 @@ +host=localhost +port=8080 +block_status=550 diff --git a/tests/hurl/run.sh b/tests/hurl/run.sh new file mode 100755 index 0000000..645b954 --- /dev/null +++ b/tests/hurl/run.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# run.sh — Spiracle Hurl test runner +# +# Usage: +# ./tests/hurl/run.sh smoke [host] [port] +# ./tests/hurl/run.sh rasp [host] [port] +# +# Arguments: +# suite — "smoke" or "rasp" +# host — hostname/IP of Spiracle (default: localhost) +# port — TCP port (default: 8080) +# +# Environment: +# BLOCK_STATUS — expected RASP block code (default: 550) +# only used by the "rasp" suite +# +# Requirements: +# hurl v5+ at ~/.local/bin/hurl or on PATH + +set -eu + +SUITE="${1:-smoke}" +HOST="${2:-localhost}" +PORT="${3:-8080}" +BLOCK_STATUS="${BLOCK_STATUS:-550}" + +# Resolve hurl binary +HURL="" +if command -v hurl >/dev/null 2>&1; then + HURL="hurl" +elif [ -x "$HOME/.local/bin/hurl" ]; then + HURL="$HOME/.local/bin/hurl" +else + echo "ERROR: hurl not found. Install from https://hurl.dev" >&2 + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +case "$SUITE" in + smoke) + VARS_FILE="$SCRIPT_DIR/smoke/local.env" + FILES="$SCRIPT_DIR/smoke/smoke.hurl" + REPORT_DIR="${REPORT_DIR:-/tmp/spiracle-smoke-report}" + ;; + rasp) + VARS_FILE="$SCRIPT_DIR/rasp/protected.env" + FILES="$SCRIPT_DIR/rasp/mysql/*.hurl $SCRIPT_DIR/rasp/oracle/*.hurl" + REPORT_DIR="${REPORT_DIR:-/tmp/spiracle-rasp-report}" + ;; + *) + echo "ERROR: unknown suite '$SUITE'. Use 'smoke' or 'rasp'." >&2 + exit 1 + ;; +esac + +mkdir -p "$REPORT_DIR" + +echo "Running Spiracle $SUITE suite against http://$HOST:$PORT" +echo "Report: $REPORT_DIR/junit.xml" +echo "" + +# shellcheck disable=SC2086 +$HURL \ + --test \ + --variables-file "$VARS_FILE" \ + --variable host="$HOST" \ + --variable port="$PORT" \ + --variable block_status="$BLOCK_STATUS" \ + --report-junit "$REPORT_DIR/junit.xml" \ + $FILES diff --git a/tests/hurl/smoke/local.env b/tests/hurl/smoke/local.env new file mode 100644 index 0000000..0182d07 --- /dev/null +++ b/tests/hurl/smoke/local.env @@ -0,0 +1,2 @@ +host=localhost +port=8080 diff --git a/tests/hurl/smoke/smoke.hurl b/tests/hurl/smoke/smoke.hurl new file mode 100644 index 0000000..c6fe4ac --- /dev/null +++ b/tests/hurl/smoke/smoke.hurl @@ -0,0 +1,32 @@ +# Spiracle smoke suite — runs against a PLAIN (no RASP agent) deployment. +# +# Purpose: prove the app is up, DB round-trips work, and that SQLi is +# NOT blocked without the Waratek agent. A 550 here would mean the +# agent is unexpectedly present; plain Tomcat returns 200 for valid +# SQL injections and 500 for malformed ones. + +# ── 1. App root is reachable ───────────────────────────────────────── +GET http://{{host}}:{{port}}/spiracle/ + +HTTP 200 + +# ── 2. Benign query returns the expected row ───────────────────────── +# Patrick Moss is id=1, name='Patrick' in the seed data. +# MySql_Get_string queries by name and returns the surname. +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=Patrick + +HTTP 200 +[Asserts] +body contains "Moss" + +# ── 3. SQL injection succeeds unprotected ─────────────────────────── +# Without the Waratek RASP agent the injection is NOT blocked. +# The payload ' OR '1'='1 widens the WHERE clause to match all rows. +# Margaret Thomas (id=2) is NOT in the result set for name='Patrick', +# but WILL appear when the injection succeeds. Asserting her surname +# proves more rows were returned than the benign query would give. +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=Patrick'%20OR%20'1'='1 + +HTTP 200 +[Asserts] +body contains "Thomas" diff --git a/tests/spiracle_sqli_test.py b/tests/spiracle_sqli_test.py deleted file mode 100755 index 3433c51..0000000 --- a/tests/spiracle_sqli_test.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/python - -from collections import defaultdict -import requests -import argparse - -parser = argparse.ArgumentParser(description="Run SQLI Tests") -parser.add_argument("-hn", "--hostname", help="Hostname", required=True) -parser.add_argument("-p", "--port", help="Port", required=True) -parser.add_argument("-f", "--file", help="Data file", required=True) -parser.add_argument("-d", "--debug", action="store_true") -args = parser.parse_args() - -input_file = open(args.file) -expected_dict = defaultdict(list) -actual_dict = defaultdict(list) -url = "http://{0}:{1}".format(args.hostname, args.port) - -for entry in input_file: - parts = entry.strip().split("") - if parts[0] in expected_dict: - expected_dict[parts[0]].append((parts[1], parts[2])) - else: - expected_dict[parts[0]] = [] - expected_dict[parts[0]].append((parts[1], parts[2])) - -for key in expected_dict.keys(): - actual_dict[key] = [] - -for key in expected_dict.keys(): - for entry in expected_dict[key]: - r = requests.get("{0}{1}{2}" - .format(url, key, entry[0])) - if args.debug: - print r.url, r.status_code - - actual_dict[key].append((entry[0], r.status_code)) - -successful_tests = 0 -log = open("results.csv", "a+") -for key in actual_dict.keys(): - counter = len(actual_dict[key]) - for x in range(0, counter): - if str(expected_dict[key][x][1]) == str(actual_dict[key][x][1]): - successful_tests += 1 - log.write("{0}{1},{2},{3}\n".format(url, key, expected_dict[key][x][1], - actual_dict[key][x][1])) - print("Servlet {0} had {1} tests. Pass: {2} Fail {3}" - .format(key, counter, successful_tests, counter - successful_tests)) - successful_tests = 0 -log.close() From 763547e47411a9b6e4c05ecd0c4b3c7b8cac96d3 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 12:00:16 +0100 Subject: [PATCH 02/13] Make setupdb_mysql.sql idempotent with IF (NOT) EXISTS (#33) First run failed on DROP TABLE (tables absent); re-runs failed on CREATE USER (user already present). Guard CREATE USER with IF NOT EXISTS and the three DROP TABLE statements with IF EXISTS so the script can be run repeatedly without manual cleanup. --- src/main/webapp/conf/setupdb_mysql.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/conf/setupdb_mysql.sql b/src/main/webapp/conf/setupdb_mysql.sql index e97603a..ba771b8 100644 --- a/src/main/webapp/conf/setupdb_mysql.sql +++ b/src/main/webapp/conf/setupdb_mysql.sql @@ -1,13 +1,13 @@ -CREATE USER 'test'@'localhost' IDENTIFIED BY 'test'; +CREATE USER IF NOT EXISTS 'test'@'localhost' IDENTIFIED BY 'test'; GRANT ALL PRIVILEGES ON *.* TO 'test'@'localhost' WITH GRANT OPTION; CREATE DATABASE IF NOT EXISTS test; use test; -DROP TABLE users; -DROP TABLE address; -DROP TABLE TEXT_STORE; +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS address; +DROP TABLE IF EXISTS TEXT_STORE; CREATE TABLE users ( id int, From 773df729ba87887450ed534489f24224cdd458ad Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 11:59:29 +0100 Subject: [PATCH 03/13] Set Content-Type on SendRedirect fallback response (#8) SendRedirect wrote plaintext instructions via getWriter() with no Content-Type header when the redirectMeTo param was absent. Every other output path in the app already sets Content-Type via setHeader; this was the last servlet response missing one. Use text/plain since the body is plain instructional text, not HTML. --- src/main/java/com/waratek/spiracle/misc/SendRedirect.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/waratek/spiracle/misc/SendRedirect.java b/src/main/java/com/waratek/spiracle/misc/SendRedirect.java index 2dce521..00d9d17 100644 --- a/src/main/java/com/waratek/spiracle/misc/SendRedirect.java +++ b/src/main/java/com/waratek/spiracle/misc/SendRedirect.java @@ -36,6 +36,7 @@ protected void executeRequest(HttpServletRequest request, HttpServletResponse re response.sendRedirect(redirectURI); } else { + response.setHeader("Content-Type", "text/plain;charset=UTF-8"); response.getWriter().println("Parameter '" + inputUriParam + "' not set in the URI."); response.getWriter().println("Please update URI to include '?" + inputUriParam + "=URI_TO_REDIRECT_TO'"); } From fd2f880817ccc284a7b4765712465ea1c8634be9 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 12:02:40 +0100 Subject: [PATCH 04/13] Fix Oracle NullPointerException from null JDBC URL in CreateC3p0Connection (#103) CreateC3p0Connection.init() read non-existent bare property keys (c3p0.url, c3p0.classname, ...) instead of the per-database keys defined in Spiracle.properties (c3p0.oracle.url, ...). url resolved to null, so ComboPooledDataSource.setJdbcUrl(null) led to DriverManager.getDriver(null) and OracleDriver.acceptsURL(null) threw NPE. Derive the key prefix from the default.connection property (matching SpiracleInit's convention) so the correct per-database connection settings are loaded. --- .../spiracle/sql/c3p0/CreateC3p0Connection.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/waratek/spiracle/sql/c3p0/CreateC3p0Connection.java b/src/main/java/com/waratek/spiracle/sql/c3p0/CreateC3p0Connection.java index f8439b8..11bfe56 100644 --- a/src/main/java/com/waratek/spiracle/sql/c3p0/CreateC3p0Connection.java +++ b/src/main/java/com/waratek/spiracle/sql/c3p0/CreateC3p0Connection.java @@ -65,12 +65,16 @@ public void init() { // TODO Auto-generated catch block e.printStackTrace(); } - jdbcDriver = prop.getProperty("c3p0.classname"); - url = prop.getProperty("c3p0.url"); - username = prop.getProperty("c3p0.username"); - password = prop.getProperty("c3p0.password"); + String prefix = prop.getProperty("default.connection"); + if (prefix == null || prefix.trim().isEmpty()) { + prefix = "c3p0.oracle"; + } + jdbcDriver = prop.getProperty(prefix + ".classname"); + url = prop.getProperty(prefix + ".url"); + username = prop.getProperty(prefix + ".username"); + password = prop.getProperty(prefix + ".password"); try { - maxPoolSize = Integer.parseInt(prop.getProperty("c3p0.maxPoolSize")); + maxPoolSize = Integer.parseInt(prop.getProperty(prefix + ".maxPoolSize")); } catch (NumberFormatException e) { maxPoolSize = 25; } From 8fe67948df747bcccfc216e7a7a6ee03c7a0e355 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 12:14:58 +0100 Subject: [PATCH 05/13] Add Docker support: per-database compose stacks with auto-seed (#68) Multi-stage Dockerfile builds the WAR (JDK 8 / Maven) and deploys it on Tomcat 9 with the MySQL, MSSQL and Oracle JDBC drivers preinstalled. An entrypoint rewrites conf/Spiracle.properties from env vars (default connection + DB host/URL) so the committed config is untouched. One compose file per database (mysql/mssql/oracle) brings up the app plus a seeded database for a one-command, no-local-install test target. --- .dockerignore | 5 +++ Dockerfile | 51 ++++++++++++++++++++++++++ docker-compose.mssql.yml | 52 ++++++++++++++++++++++++++ docker-compose.mysql.yml | 33 +++++++++++++++++ docker-compose.oracle.yml | 29 +++++++++++++++ docker/README.md | 67 ++++++++++++++++++++++++++++++++++ docker/docker-grants-mysql.sql | 6 +++ docker/entrypoint.sh | 27 ++++++++++++++ docker/mssql-create-login.sql | 17 +++++++++ docker/mysql-seed.sh | 15 ++++++++ 10 files changed, 302 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.mssql.yml create mode 100644 docker-compose.mysql.yml create mode 100644 docker-compose.oracle.yml create mode 100644 docker/README.md create mode 100644 docker/docker-grants-mysql.sql create mode 100644 docker/entrypoint.sh create mode 100644 docker/mssql-create-login.sql create mode 100644 docker/mysql-seed.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1aff237 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +target/ +.git/ +.idea/ +*.iml +mise.toml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e0c7a68 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +# Stage 1: build the WAR using Java 8 + Maven +FROM maven:3.9-eclipse-temurin-8 AS build + +WORKDIR /build +COPY pom.xml . +COPY src ./src + +RUN mvn install -Dversion.webxml=30 -DskipTests -q + +# --------------------------------------------------------------------------- +# Stage 2: runtime — Tomcat 9 + JRE 8 +# --------------------------------------------------------------------------- +FROM tomcat:9-jre8-temurin AS runtime + +# Remove default webapps +RUN rm -rf "$CATALINA_HOME/webapps/ROOT" \ + "$CATALINA_HOME/webapps/docs" \ + "$CATALINA_HOME/webapps/examples" \ + "$CATALINA_HOME/webapps/host-manager" \ + "$CATALINA_HOME/webapps/manager" + +# Copy and pre-explode the WAR so the entrypoint can edit conf/ on disk +COPY --from=build /build/target/spiracle.war /tmp/spiracle.war +RUN apt-get update -qq && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/* \ + && mkdir -p "$CATALINA_HOME/webapps/spiracle" \ + && unzip -q /tmp/spiracle.war -d "$CATALINA_HOME/webapps/spiracle" \ + && rm /tmp/spiracle.war + +# ---- JDBC drivers (downloaded at image build time from Maven Central) ---- + +# MySQL Connector/J 5.1.49 — has com.mysql.jdbc.Driver (legacy classname) +RUN curl -fsSL \ + "https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar" \ + -o "$CATALINA_HOME/lib/mysql-connector-java-5.1.49.jar" + +# MSSQL JDBC — jre8 classifier +RUN curl -fsSL \ + "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.4.2.jre8/mssql-jdbc-12.4.2.jre8.jar" \ + -o "$CATALINA_HOME/lib/mssql-jdbc-12.4.2.jre8.jar" + +# Oracle ojdbc8 — Java 8 compatible +RUN curl -fsSL \ + "https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/21.13.0.0/ojdbc8-21.13.0.0.jar" \ + -o "$CATALINA_HOME/lib/ojdbc8-21.13.0.0.jar" + +# ---- entrypoint ---- +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +EXPOSE 8080 +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/docker-compose.mssql.yml b/docker-compose.mssql.yml new file mode 100644 index 0000000..a59bd62 --- /dev/null +++ b/docker-compose.mssql.yml @@ -0,0 +1,52 @@ +services: + db: + image: mcr.microsoft.com/mssql/server:2022-latest + environment: + ACCEPT_EULA: "Y" + SA_PASSWORD: "Spiracle_SA_2024!" + MSSQL_PID: Developer + healthcheck: + test: + - "CMD-SHELL" + - | + /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P 'Spiracle_SA_2024!' \ + -No -Q 'SELECT 1' > /dev/null 2>&1 + interval: 10s + timeout: 5s + retries: 15 + start_period: 30s + + db-init: + image: mcr.microsoft.com/mssql/server:2022-latest + environment: + SA_PASSWORD: "Spiracle_SA_2024!" + volumes: + - ./src/main/webapp/conf/setupdb_mssql.sql:/init/setupdb_mssql.sql:ro + - ./docker/mssql-create-login.sql:/init/mssql-create-login.sql:ro + depends_on: + db: + condition: service_healthy + entrypoint: + - /bin/bash + - -c + - | + /opt/mssql-tools18/bin/sqlcmd -S db -U SA -P "$$SA_PASSWORD" -No \ + -i /init/setupdb_mssql.sql && \ + /opt/mssql-tools18/bin/sqlcmd -S db -U SA -P "$$SA_PASSWORD" -No \ + -d spiracle -i /init/mssql-create-login.sql + restart: "no" + + app: + build: + context: . + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + SPIRACLE_DEFAULT_CONNECTION: c3p0.mssql + SPIRACLE_DB_HOST: db + depends_on: + db: + condition: service_healthy + db-init: + condition: service_completed_successfully diff --git a/docker-compose.mysql.yml b/docker-compose.mysql.yml new file mode 100644 index 0000000..e3b259a --- /dev/null +++ b/docker-compose.mysql.yml @@ -0,0 +1,33 @@ +services: + db: + image: mysql:8.0 + command: --default-authentication-plugin=mysql_native_password + environment: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: test + MYSQL_USER: test + MYSQL_PASSWORD: test + volumes: + # Seed SQL mounted outside initdb.d so the shell script controls execution + - ./src/main/webapp/conf/setupdb_mysql.sql:/init/setupdb_mysql.sql:ro + # Shell script in initdb.d runs seed with --force (bare DROPs on fresh DB) + - ./docker/mysql-seed.sh:/docker-entrypoint-initdb.d/01-seed.sh:ro + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "--password=rootpassword"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + + app: + build: + context: . + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + SPIRACLE_DEFAULT_CONNECTION: c3p0.mysql + SPIRACLE_DB_HOST: db + depends_on: + db: + condition: service_healthy diff --git a/docker-compose.oracle.yml b/docker-compose.oracle.yml new file mode 100644 index 0000000..a845e43 --- /dev/null +++ b/docker-compose.oracle.yml @@ -0,0 +1,29 @@ +services: + db: + image: gvenzl/oracle-xe:21-slim + environment: + ORACLE_PASSWORD: oraclepassword + APP_USER: test + APP_USER_PASSWORD: test + volumes: + - ./src/main/webapp/conf/setupdb_oracle.sql:/container-entrypoint-initdb.d/setupdb_oracle.sql:ro + healthcheck: + test: ["CMD", "healthcheck.sh"] + interval: 30s + timeout: 10s + retries: 20 + start_period: 120s + + app: + build: + context: . + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + SPIRACLE_DEFAULT_CONNECTION: c3p0.oracle + # gvenzl runs initdb scripts in the PDB (XEPDB1); use service-name URL form + SPIRACLE_DB_URL: "jdbc:oracle:thin:@//db:1521/XEPDB1" + depends_on: + db: + condition: service_healthy diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..02461aa --- /dev/null +++ b/docker/README.md @@ -0,0 +1,67 @@ +# Spiracle — Docker usage + +**WARNING: Spiracle is an intentionally-vulnerable application. Run on localhost / throwaway networks only. Never expose to the internet.** + +## Prerequisites + +- Docker Engine 24+ with the Compose v2 plugin (`docker compose`) +- No local Tomcat or database installation needed + +## Quick start + +Pick one database and run the corresponding compose file from the repo root. + +### MySQL (recommended for first run — smallest image) + +```sh +docker compose -f docker-compose.mysql.yml up --build +``` + +Browse to: http://localhost:8080/spiracle/ + +Tear down (removes volumes): +```sh +docker compose -f docker-compose.mysql.yml down -v +``` + +### Microsoft SQL Server + +```sh +docker compose -f docker-compose.mssql.yml up --build +``` + +SQL Server image (~1.5 GB). A one-shot `db-init` service seeds the database after SQL Server becomes healthy. + +Browse to: http://localhost:8080/spiracle/ + +```sh +docker compose -f docker-compose.mssql.yml down -v +``` + +### Oracle XE + +```sh +docker compose -f docker-compose.oracle.yml up --build +``` + +Oracle XE image (~2–4 GB). First pull takes several minutes. The container has a long startup; wait for the `db` service to show `healthy` before the app becomes ready. + +Browse to: http://localhost:8080/spiracle/ + +```sh +docker compose -f docker-compose.oracle.yml down -v +``` + +## How it works + +- A multi-stage Dockerfile builds the WAR with JDK 8 / Maven, then deploys it on Tomcat 9. +- MySQL (Connector/J 5.1.49), MSSQL (mssql-jdbc jre8) and Oracle (ojdbc8) JDBC drivers are bundled in the image. +- `docker/entrypoint.sh` rewrites `conf/Spiracle.properties` from environment variables before Tomcat starts. The committed `Spiracle.properties` is never modified. + +## Environment variables (app service) + +| Variable | Purpose | Example | +|---|---|---| +| `SPIRACLE_DEFAULT_CONNECTION` | Sets `default.connection` in properties | `c3p0.mysql` | +| `SPIRACLE_DB_HOST` | Replaces `localhost` in the chosen db URL | `db` | +| `SPIRACLE_DB_URL` | Overrides the entire URL line (Oracle service-name form) | `jdbc:oracle:thin:@//db:1521/XEPDB1` | diff --git a/docker/docker-grants-mysql.sql b/docker/docker-grants-mysql.sql new file mode 100644 index 0000000..4aef196 --- /dev/null +++ b/docker/docker-grants-mysql.sql @@ -0,0 +1,6 @@ +-- Docker-only: grant remote access for the 'test' user created by setupdb_mysql.sql. +-- setupdb_mysql.sql creates 'test'@'localhost'; that cannot connect from the app container. +-- This file runs AFTER setupdb_mysql.sql (lexicographic order ensures 0-prefix runs first). +CREATE USER IF NOT EXISTS 'test'@'%' IDENTIFIED BY 'test'; +GRANT ALL PRIVILEGES ON *.* TO 'test'@'%' WITH GRANT OPTION; +FLUSH PRIVILEGES; diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..e52039c --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Rewrite Spiracle.properties from env vars before Tomcat starts. +# All changes are made to the exploded webapp copy only — source files untouched. + +PROPS="$CATALINA_HOME/webapps/spiracle/conf/Spiracle.properties" + +# SPIRACLE_DEFAULT_CONNECTION — e.g. "c3p0.mysql" +if [ -n "$SPIRACLE_DEFAULT_CONNECTION" ]; then + sed -i "s|^default\.connection=.*|default.connection=${SPIRACLE_DEFAULT_CONNECTION}|" "$PROPS" +fi + +# Derive the db key from the connection name (e.g. c3p0.mysql → mysql) +if [ -n "$SPIRACLE_DEFAULT_CONNECTION" ]; then + DB_KEY="${SPIRACLE_DEFAULT_CONNECTION#c3p0.}" +else + DB_KEY="" +fi + +# SPIRACLE_DB_URL — override the whole url line for this db (takes priority) +if [ -n "$SPIRACLE_DB_URL" ] && [ -n "$DB_KEY" ]; then + sed -i "s|^c3p0\.${DB_KEY}\.url=.*|c3p0.${DB_KEY}.url=${SPIRACLE_DB_URL}|" "$PROPS" +elif [ -n "$SPIRACLE_DB_HOST" ] && [ -n "$DB_KEY" ]; then + # Replace only 'localhost' in the specific db url line + sed -i "/^c3p0\.${DB_KEY}\.url=/ s|localhost|${SPIRACLE_DB_HOST}|g" "$PROPS" +fi + +exec "$CATALINA_HOME/bin/catalina.sh" run diff --git a/docker/mssql-create-login.sql b/docker/mssql-create-login.sql new file mode 100644 index 0000000..5c70204 --- /dev/null +++ b/docker/mssql-create-login.sql @@ -0,0 +1,17 @@ +-- Docker-only: create the 'test' login and user that Spiracle.properties expects. +-- setupdb_mssql.sql does not create this login; run this first. +IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = 'test') +BEGIN + CREATE LOGIN [test] WITH PASSWORD = 'Mssql1234', CHECK_POLICY = OFF; +END +GO + +USE spiracle; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = 'test') +BEGIN + CREATE USER [test] FOR LOGIN [test]; + ALTER ROLE db_owner ADD MEMBER [test]; +END +GO diff --git a/docker/mysql-seed.sh b/docker/mysql-seed.sh new file mode 100644 index 0000000..45c36e8 --- /dev/null +++ b/docker/mysql-seed.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Docker-only init script for MySQL. +# The canonical setupdb_mysql.sql has bare DROP TABLE (no IF EXISTS) which +# errors on a fresh DB. Run it with --force to skip those errors. +set -e + +mysql --force -u root -p"${MYSQL_ROOT_PASSWORD}" < /init/setupdb_mysql.sql + +# Grant remote access for 'test'@'%' so the app container (different host) +# can connect. The canonical seed only creates 'test'@'localhost'. +mysql -u root -p"${MYSQL_ROOT_PASSWORD}" <<'SQL' +CREATE USER IF NOT EXISTS 'test'@'%' IDENTIFIED WITH mysql_native_password BY 'test'; +GRANT ALL PRIVILEGES ON *.* TO 'test'@'%' WITH GRANT OPTION; +FLUSH PRIVILEGES; +SQL From 76c0502c5c0bcdb2e635820cc3435dbd828a1808 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 14:57:22 +0100 Subject: [PATCH 06/13] Add functional Hurl suite (plain-stack, no agent): redirect/sql/xss/traversal/negative Expands test coverage beyond smoke with endpoint-level functional tests that run against an unprotected deployment: a regression test for the SendRedirect Content-Type fix (#8), benign + injection SQL behavior, reflected XSS, path traversal, and negative cases. Validated green against the MySQL Docker stack. (The rasp/ matrix remains agent-only.) --- tests/hurl/README.md | 39 +++++++++++++++++- tests/hurl/functional/local.env | 2 + tests/hurl/functional/negative.hurl | 29 ++++++++++++++ tests/hurl/functional/redirect.hurl | 24 +++++++++++ tests/hurl/functional/sql.hurl | 60 ++++++++++++++++++++++++++++ tests/hurl/functional/traversal.hurl | 39 ++++++++++++++++++ tests/hurl/functional/xss.hurl | 28 +++++++++++++ tests/hurl/run.sh | 14 +++++-- 8 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 tests/hurl/functional/local.env create mode 100644 tests/hurl/functional/negative.hurl create mode 100644 tests/hurl/functional/redirect.hurl create mode 100644 tests/hurl/functional/sql.hurl create mode 100644 tests/hurl/functional/traversal.hurl create mode 100644 tests/hurl/functional/xss.hurl diff --git a/tests/hurl/README.md b/tests/hurl/README.md index 9663d9f..84bfd88 100644 --- a/tests/hurl/README.md +++ b/tests/hurl/README.md @@ -32,6 +32,13 @@ The RASP suite will fail entirely without the agent — this is expected. tests/hurl/ ├── generate.py # Generator: mysql.txt + oracle.txt → .hurl files ├── run.sh # Runner wrapper +├── functional/ # Endpoint-level functional suite (no agent required) +│ ├── local.env # Variables: host=localhost, port=8080 +│ ├── redirect.hurl # 2 tests: SendRedirect regression (#8 Content-Type fix) +│ ├── sql.hurl # 5 tests: benign queries + unprotected SQLi demo +│ ├── xss.hurl # 2 tests: reflected XSS via customTag.jsp +│ ├── traversal.hurl # 4 tests: benign file + path traversal demo +│ └── negative.hurl # 3 tests: 404, empty result set, no-param graceful ├── rasp/ # RASP-efficacy suite (needs Waratek agent) │ ├── protected.env # Variables: host, port, block_status=550 │ ├── mysql/ # MySQL servlet tests (139 cases, 5 files) @@ -44,7 +51,7 @@ tests/hurl/ │ ├── get_int.hurl │ ├── get_string.hurl │ └── ... -└── smoke/ # Functional smoke suite (no agent required) +└── smoke/ # Smoke suite (no agent required) ├── local.env # Variables: host=localhost, port=8080 └── smoke.hurl # 3 tests: up-check, benign query, SQLi succeeds ``` @@ -55,6 +62,36 @@ Source of truth for the RASP payload matrices: --- +## Running the functional suite (plain Docker / CI) + +The functional suite validates endpoint behaviour without any RASP agent: + +| File | Requests | What it covers | +|------------------|----------|----------------| +| `redirect.hurl` | 2 | SendRedirect: no-param→200+text/plain (#8 regression); param→302+Location | +| `sql.hurl` | 5 | MySql_Get_int, MySql_Get_string, MySql_Get_Implicit_Join (benign + SQLi), MySql_Get_Union | +| `xss.hurl` | 2 | customTag.jsp benign name; `` reflected unescaped | +| `traversal.hurl` | 4 | FileInputStreamServlet01 benign TestFile; `../TestFile` traversal succeeds | +| `negative.hurl` | 3 | 404 on unknown path; empty result set; no-param graceful 200 | + +```sh +# Start the Docker MySQL stack +docker compose -f docker-compose.mysql.yml up -d + +# Run functional tests +./tests/hurl/run.sh functional localhost 8080 + +# Or with hurl directly +hurl --test --variables-file tests/hurl/functional/local.env \ + tests/hurl/functional/*.hurl +``` + +**XSS note:** The ReadHTML-based servlets (`XSSWebAppHSRPW` etc.) do NOT reflect +the `taintedtext` param because `xss.html` contains no literal `"XSS"` token. +`customTag.jsp` is the GET-accessible reflected-XSS endpoint used here. + +--- + ## Running the smoke suite (plain Docker / CI) The smoke suite validates: diff --git a/tests/hurl/functional/local.env b/tests/hurl/functional/local.env new file mode 100644 index 0000000..0182d07 --- /dev/null +++ b/tests/hurl/functional/local.env @@ -0,0 +1,2 @@ +host=localhost +port=8080 diff --git a/tests/hurl/functional/negative.hurl b/tests/hurl/functional/negative.hurl new file mode 100644 index 0000000..4fd3108 --- /dev/null +++ b/tests/hurl/functional/negative.hurl @@ -0,0 +1,29 @@ +# negative.hurl — boundary and error cases. +# +# These cases verify that the app responds deterministically to invalid +# or missing inputs, providing a stable baseline for regression. + +# ── 1. Non-existent servlet path → 404 ─────────────────────────────── +GET http://{{host}}:{{port}}/spiracle/NoSuchServletXYZ + +HTTP 404 + +# ── 2. MySql_Get_string with unknown name → 200, empty results ─────── +# No rows match name='NoSuchUser99'; the app still returns 200 with an +# empty result table (no error, no crash). +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=NoSuchUser99 + +HTTP 200 +[Asserts] +body contains "Results" +# App echoes param in SQL query preview but no rows should appear +body not contains "NoSuchUser99" + +# ── 3. MySql_Get_int with no param → 200, empty results ───────────── +# ParameterNullFix sanitises null to empty string; the SQL query +# WHERE id = '' returns zero rows — app handles gracefully. +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int + +HTTP 200 +[Asserts] +body contains "Results" diff --git a/tests/hurl/functional/redirect.hurl b/tests/hurl/functional/redirect.hurl new file mode 100644 index 0000000..57eede2 --- /dev/null +++ b/tests/hurl/functional/redirect.hurl @@ -0,0 +1,24 @@ +# redirect.hurl — regression tests for SendRedirect behaviour. +# +# Source: src/main/java/com/waratek/spiracle/misc/SendRedirect.java +# @WebServlet("/SendRedirect") +# +# Regression for #8: when redirectMeTo param is absent the servlet must +# respond 200 with Content-Type: text/plain and explain the missing param. +# If the fix is reverted the Content-Type header will be missing/wrong. + +# ── 1. No param → 200 plain text with usage hint ───────────────────── +GET http://{{host}}:{{port}}/spiracle/SendRedirect + +HTTP 200 +[Asserts] +header "Content-Type" contains "text/plain" +body contains "redirectMeTo" + +# ── 2. Valid param → 302 redirect to supplied URL ──────────────────── +# Hurl does NOT follow redirects by default; the 302 is directly observable. +GET http://{{host}}:{{port}}/spiracle/SendRedirect?redirectMeTo=https://example.com + +HTTP 302 +[Asserts] +header "Location" contains "example.com" diff --git a/tests/hurl/functional/sql.hurl b/tests/hurl/functional/sql.hurl new file mode 100644 index 0000000..d9a54bb --- /dev/null +++ b/tests/hurl/functional/sql.hurl @@ -0,0 +1,60 @@ +# sql.hurl — functional SQL tests against the unprotected MySQL stack. +# +# Sources: +# MySql_Get_int → @WebServlet("/MySql_Get_int") param: id +# MySql_Get_string → @WebServlet("/MySql_Get_string") param: name +# MySql_Get_Implicit_Join → @WebServlet("/MySql_Get_Implicit_Join") param: id +# MySql_Get_Union → @WebServlet("/MySql_Get_Union") param: id +# +# Seed data (setupdb_mysql.sql): +# id=1 → Patrick Moss +# id=2 → Margaret Thomas +# address id=1 → 2128 Vestibulum, St. / Dubuisson / San Marino + +# ── 1. MySql_Get_int benign: id=1 returns Patrick Moss ─────────────── +GET http://{{host}}:{{port}}/spiracle/MySql_Get_int?id=1 + +HTTP 200 +[Asserts] +body contains "Patrick" +body contains "Moss" + +# ── 2. MySql_Get_string benign: name=Patrick returns surname Moss ──── +GET http://{{host}}:{{port}}/spiracle/MySql_Get_string?name=Patrick + +HTTP 200 +[Asserts] +body contains "Moss" + +# ── 3. MySql_Get_Implicit_Join benign: id=1 returns row + address ──── +# Query: SELECT * FROM users, address WHERE users.id = 1 AND users.id = address.id +# Proves the join works for a known row. +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=1 + +HTTP 200 +[Asserts] +body contains "Patrick" +body contains "Dubuisson" + +# ── 4. Unprotected SQL injection on MySql_Get_Implicit_Join ────────── +# Payload: id=1 OR 1=1 +# Query becomes: SELECT * FROM users, address WHERE users.id = 1 OR 1=1 AND users.id = address.id +# Without the Waratek RASP agent this executes and returns ALL users. +# id=1 only returns Patrick Moss; injection adds Margaret Thomas (id=2). +# Asserting both surnames proves injection executed and widened the result. +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Implicit_Join?id=1%20OR%201%3D1 + +HTTP 200 +[Asserts] +body contains "Moss" +body contains "Thomas" + +# ── 5. MySql_Get_Union benign: id=1 returns user + address rows ────── +# Query: SELECT name,surname,dob FROM users ... UNION SELECT address_1,address_2,address_3 FROM address ... +# Both result sets for id=1 are returned. +GET http://{{host}}:{{port}}/spiracle/MySql_Get_Union?id=1 + +HTTP 200 +[Asserts] +body contains "Moss" +body contains "Dubuisson" diff --git a/tests/hurl/functional/traversal.hurl b/tests/hurl/functional/traversal.hurl new file mode 100644 index 0000000..a3e9fd8 --- /dev/null +++ b/tests/hurl/functional/traversal.hurl @@ -0,0 +1,39 @@ +# traversal.hurl — path traversal demonstration (unprotected). +# +# Source: src/main/java/com/waratek/spiracle/path_traversal/FileInputStreamServlet01.java +# @WebServlet("/FileInputStreamServlet01") param: FileInputStream01 +# +# The servlet constructs: +# absolutePathToTestFile = /pathTraversal/testFilesParent/testFilesChild/ +# + File.separator + FileInputStream01 +# No sanitisation is applied. A "../" sequence traverses up one directory. +# The servlet stores "File input stream opened for file: ''" in the +# session, then redirects to pathTraversal.jsp which renders it. +# Hurl follows the redirect automatically (session cookie carried). +# +# Known test files in the deployment: +# testFilesChild/TestFile (the intended target) +# testFilesParent/TestFile (one level up — requires traversal) + +# ── 1. Benign: access known file in testFilesChild ─────────────────── +GET http://{{host}}:{{port}}/spiracle/FileInputStreamServlet01?FileInputStream01=TestFile +[Options] +location: true + +HTTP 200 +[Asserts] +body contains "File input stream opened for file:" +body contains "testFilesChild" + +# ── 2. Path traversal: ../TestFile escapes testFilesChild ──────────── +# %2F = / so ../TestFile URL-encodes to ..%2FTestFile +# The constructed path resolves to testFilesParent/TestFile. +# Without a RASP agent the traversal is NOT blocked and the stream opens. +GET http://{{host}}:{{port}}/spiracle/FileInputStreamServlet01?FileInputStream01=..%2FTestFile +[Options] +location: true + +HTTP 200 +[Asserts] +body contains "File input stream opened for file:" +body contains "testFilesParent" diff --git a/tests/hurl/functional/xss.hurl b/tests/hurl/functional/xss.hurl new file mode 100644 index 0000000..2ad1e3c --- /dev/null +++ b/tests/hurl/functional/xss.hurl @@ -0,0 +1,28 @@ +# xss.hurl — reflected XSS demonstration (unprotected). +# +# Source: src/main/webapp/customTag.jsp +# The JSP reads request.getParameter("name") and passes it to HelloUserTag, +# which writes: Hello Spiracle user: NAME! +# No escaping is applied, so a script payload in "name" is reflected verbatim. +# +# Note: the ReadHTML-based XSS servlets (XSSWebAppHSRPW, XSSWebAppSRPW etc.) +# do NOT reflect the param because xss.html contains no literal "XSS" token +# to substitute — those are agent-trigger targets, not reflection endpoints. +# customTag.jsp is the cleanest GET-accessible reflected-XSS endpoint. + +# ── 1. Benign name: confirm normal reflection ──────────────────────── +GET http://{{host}}:{{port}}/spiracle/customTag.jsp?name=Alice + +HTTP 200 +[Asserts] +body contains "Hello Spiracle user" +body contains "Alice" + +# ── 2. XSS payload reflected unescaped ────────────────────────────── +# %3Cscript%3Ealert%281%29%3C%2Fscript%3E = +# Without sanitisation the raw tag appears in the HTML body. +GET http://{{host}}:{{port}}/spiracle/customTag.jsp?name=%3Cscript%3Ealert%281%29%3C%2Fscript%3E + +HTTP 200 +[Asserts] +body contains "" diff --git a/tests/hurl/run.sh b/tests/hurl/run.sh index 645b954..19b8aab 100755 --- a/tests/hurl/run.sh +++ b/tests/hurl/run.sh @@ -2,11 +2,12 @@ # run.sh — Spiracle Hurl test runner # # Usage: -# ./tests/hurl/run.sh smoke [host] [port] -# ./tests/hurl/run.sh rasp [host] [port] +# ./tests/hurl/run.sh smoke [host] [port] +# ./tests/hurl/run.sh functional [host] [port] +# ./tests/hurl/run.sh rasp [host] [port] # # Arguments: -# suite — "smoke" or "rasp" +# suite — "smoke", "functional", or "rasp" # host — hostname/IP of Spiracle (default: localhost) # port — TCP port (default: 8080) # @@ -43,13 +44,18 @@ case "$SUITE" in FILES="$SCRIPT_DIR/smoke/smoke.hurl" REPORT_DIR="${REPORT_DIR:-/tmp/spiracle-smoke-report}" ;; + functional) + VARS_FILE="$SCRIPT_DIR/functional/local.env" + FILES="$SCRIPT_DIR/functional/redirect.hurl $SCRIPT_DIR/functional/sql.hurl $SCRIPT_DIR/functional/xss.hurl $SCRIPT_DIR/functional/traversal.hurl $SCRIPT_DIR/functional/negative.hurl" + REPORT_DIR="${REPORT_DIR:-/tmp/spiracle-functional-report}" + ;; rasp) VARS_FILE="$SCRIPT_DIR/rasp/protected.env" FILES="$SCRIPT_DIR/rasp/mysql/*.hurl $SCRIPT_DIR/rasp/oracle/*.hurl" REPORT_DIR="${REPORT_DIR:-/tmp/spiracle-rasp-report}" ;; *) - echo "ERROR: unknown suite '$SUITE'. Use 'smoke' or 'rasp'." >&2 + echo "ERROR: unknown suite '$SUITE'. Use 'smoke', 'functional', or 'rasp'." >&2 exit 1 ;; esac From ae918fc22947cd79372d985a179fd371f6e2d086 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Fri, 29 May 2026 15:26:49 +0100 Subject: [PATCH 07/13] docs: document Docker, Hurl test suites, and the Java build matrix README now covers the per-database Docker compose stacks, the Hurl test suites (smoke/functional run on a plain deployment, rasp is agent-only), and the -Dversion.jdk/-Dversion.webxml build matrix with the master (Java 5-8) vs java4 branch split. Notes the SendRedirect Content-Type (#8), setupdb idempotency (#33), and Oracle NPE (#103) fixes. --- README.adoc | 123 +++++++++++++++++++++++++++++++++++++++---- tests/hurl/README.md | 4 +- 2 files changed, 114 insertions(+), 13 deletions(-) diff --git a/README.adoc b/README.adoc index 73ecd73..7fe5825 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,6 @@ = Spiracle -Spiracle is an insecure web application used to test system security controls. +Spiracle is an insecure web application used to test system security controls. It can be used to read/write arbitrary files and open network connections. The application is also vulnerable to numerous other vulnerabilities such as: @@ -12,7 +12,7 @@ The application is also vulnerable to numerous other vulnerabilities such as: * Deserialization (CWE-502) * and many more... -CAUTION: Due to its insecure design, this application should NOT be deployed on an unsecured network or system. +CAUTION: Due to its insecure design, this application should NOT be deployed on an unsecured network or system. Run on localhost or throwaway networks only. This application has been tested on the following application servers: @@ -21,6 +21,35 @@ This application has been tested on the following application servers: Your mileage may vary with other application servers. +== Docker (quickstart) + +The fastest way to run Spiracle. No local Tomcat or database install needed. + +Pick a database and run its compose file from the repo root: + +---- +# MySQL (smallest image — recommended for first run) +$ docker compose -f docker-compose.mysql.yml up --build + +# Microsoft SQL Server (~1.5 GB image) +$ docker compose -f docker-compose.mssql.yml up --build + +# Oracle XE (~2–4 GB image; first pull takes several minutes) +$ docker compose -f docker-compose.oracle.yml up --build +---- + +Once healthy, browse to: http://localhost:8080/spiracle/ + +Tear down (removes volumes): + +---- +$ docker compose -f docker-compose.mysql.yml down -v +---- + +The multi-stage `Dockerfile` builds the WAR with JDK 8 / Maven and deploys it on Tomcat 9. MySQL, MSSQL, and Oracle JDBC drivers are bundled in the image; `docker/entrypoint.sh` rewrites `conf/Spiracle.properties` from environment variables at startup. + +Full Docker reference: link:docker/README.md[docker/README.md] + == Installation * Download pre-built `spiracle.war` file from the releases page: https://github.com/waratek/spiracle/releases @@ -54,22 +83,24 @@ $ jar xvf /path/to/downloaded/spiracle.war - - httpPort="9080" + httpPort="9080" httpsPort="9443"/> ---- + <1> Enable `Servlet-3.0` as a feature <2> Add a `webApplication` tag referencing Spiracle -<3> Change `httpSession` parameter length +<3> Change `httpSession` parameter length <4> Add a `host` attribute === Database setup If you would like to run the SQL injection tests, the database should be populated as follows. Data files are available in the web applications `spiracle/conf/` directory after the `spiracle.war` file has been deployed and exploded. +NOTE: When using Docker, database initialisation is handled automatically by the compose stack. Manual setup is only needed for bare-metal deployments. + ==== Oracle . Ensure that the link:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html[Oracle Database JDBC Driver] (ojdbc6.jar) is installed in the applications `WEB-INF/lib/` directory after the `spiracle.war` file is exploded on first run. @@ -124,23 +155,72 @@ Properties file can be overridden when submitting the request by appending the n &connectionType=c3p0.mysql ---- +== Testing + +Spiracle ships with a link:tests/hurl/README.md[Hurl] test harness under `tests/hurl/`. Tests are endpoint-based and run against any deployment (Docker or bare-metal). + +=== Suites + +[cols="1,1,3",options="header"] +|=== +|Suite |Agent required |What it covers + +|`smoke/` +|No +|App root responds 200; benign query returns data; unblocked SQLi widens result set (confirming injections succeed without agent) + +|`functional/` +|No +|SendRedirect, SQL queries, reflected XSS via `customTag.jsp`, path traversal, 404/empty-result/no-param negative cases + +|`rasp/` +|Yes (Waratek RASP) +|440-case SQL injection matrix (139 MySQL, 301 Oracle); asserts status `550`, which is only emitted when the Waratek agent intercepts the query +|=== + +=== Quick run (plain Docker stack) + +---- +# Bring up MySQL stack +$ docker compose -f docker-compose.mysql.yml up -d + +# Smoke +$ ./tests/hurl/run.sh smoke localhost 8080 + +# Functional +$ ./tests/hurl/run.sh functional localhost 8080 +---- + +The `rasp/` suite will fail on a plain deployment — expected. Run it only with the Waratek agent attached to Tomcat. + +Full harness reference: link:tests/hurl/README.md[tests/hurl/README.md] + == Building Prerequisites: -* Java >= 1.6 +* Java 5–8 (see toolchain note below) * Apache Maven * link:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html[Oracle Database JDBC Driver] (ojdbc6.jar) If you wish to use the database features, ensure that the Oracle database JDBC driver file `ojdbc6.jar` is available under `./src/main/webapp/WEB-INF/lib` -To build the Spiracle Test Application WAR file, run: +=== Build flags + +Two flags parameterise the build: - $ mvn install -Dversion.webxml=30 +`-Dversion.jdk=`:: Sets the Java source and target compiler level. Supported values on `master`: `1.5`, `1.6`, `1.7`, `1.8`. +`-Dversion.webxml=<25|30>`:: Selects the Servlet descriptor version (2.5 or 3.0). -or +Representative invocations: - $ mvn install -Dversion.webxml=25 +---- +# Java 8, Servlet 3.0 (recommended) +$ mvn install -Dversion.jdk=1.8 -Dversion.webxml=30 + +# Java 6, Servlet 2.5 +$ mvn install -Dversion.jdk=1.6 -Dversion.webxml=25 +---- To clean the build infrastructure, run: @@ -150,6 +230,28 @@ The WAR file will be output to: ./target/spiracle.war +=== Toolchain + +The repo carries a `mise.toml` pinning Temurin 8 and Maven 3.9. With link:https://mise.jdx.dev[mise] installed: + +---- +$ mise install # installs pinned JDK + Maven +$ mvn install -Dversion.jdk=1.8 -Dversion.webxml=30 +---- + +The Docker image pins its own JDK via its base image — no local JDK is needed when building through Docker. + +=== Branch model + +`master`:: Modern, parameterised source tree. Java 5–8 source level. Use `-Dversion.jdk` to select. +`java4`:: Java-1.4-source-compatible variant. Annotations replaced by `web.xml` registration; uses a legacy dependency set. Check out this branch if you need Java 4 compatibility. + +== Recent fixes + +* *#8* — `Content-Type: text/plain` is now set on the `SendRedirect` fallback response (no-parameter case). +* *#33* — `setupdb_mysql.sql` is idempotent; uses `IF [NOT] EXISTS` guards so re-running does not error. +* *#103* — Oracle connection NPE fixed in `CreateC3p0Connection`; was reading non-existent property keys from `Spiracle.properties`. + == License ---- @@ -167,4 +269,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---- - diff --git a/tests/hurl/README.md b/tests/hurl/README.md index 84bfd88..caa8ceb 100644 --- a/tests/hurl/README.md +++ b/tests/hurl/README.md @@ -101,8 +101,8 @@ The smoke suite validates: (documenting that injections are NOT blocked without the agent) ```sh -# Start the Docker MySQL stack (requires docker-compose from feat/docker branch) -docker-compose up -d +# Start the Docker MySQL stack +docker compose -f docker-compose.mysql.yml up -d # Run smoke tests ./tests/hurl/run.sh smoke localhost 8080 From c26f462c30bf52196ebfdd68eae7102090763dab Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Mon, 8 Jun 2026 17:04:28 +0100 Subject: [PATCH 08/13] Address review: drop stale mysql-seed comment + --force; trim Hurl README --- docker/mysql-seed.sh | 6 +++--- tests/hurl/README.md | 37 ++++++------------------------------- 2 files changed, 9 insertions(+), 34 deletions(-) diff --git a/docker/mysql-seed.sh b/docker/mysql-seed.sh index 45c36e8..d67bd10 100644 --- a/docker/mysql-seed.sh +++ b/docker/mysql-seed.sh @@ -1,10 +1,10 @@ #!/bin/bash # Docker-only init script for MySQL. -# The canonical setupdb_mysql.sql has bare DROP TABLE (no IF EXISTS) which -# errors on a fresh DB. Run it with --force to skip those errors. +# Loads the canonical schema/seed (idempotent: IF [NOT] EXISTS), then grants +# remote access for the app container. set -e -mysql --force -u root -p"${MYSQL_ROOT_PASSWORD}" < /init/setupdb_mysql.sql +mysql -u root -p"${MYSQL_ROOT_PASSWORD}" < /init/setupdb_mysql.sql # Grant remote access for 'test'@'%' so the app container (different host) # can connect. The canonical seed only creates 'test'@'localhost'. diff --git a/tests/hurl/README.md b/tests/hurl/README.md index caa8ceb..ae271c5 100644 --- a/tests/hurl/README.md +++ b/tests/hurl/README.md @@ -5,7 +5,7 @@ Replaces the old `tests/spiracle_sqli_test.py` (Python 2, bespoke `` form --- -## The critical semantic: 550 requires the Waratek RASP agent +## The 550 status requires the Waratek RASP agent The number `550` is **not** a standard HTTP status code. `SelectUtil.verifySQLException` emits it **only** when the SQLException @@ -28,37 +28,12 @@ The RASP suite will fail entirely without the agent — this is expected. ## Suite layout -``` -tests/hurl/ -├── generate.py # Generator: mysql.txt + oracle.txt → .hurl files -├── run.sh # Runner wrapper -├── functional/ # Endpoint-level functional suite (no agent required) -│ ├── local.env # Variables: host=localhost, port=8080 -│ ├── redirect.hurl # 2 tests: SendRedirect regression (#8 Content-Type fix) -│ ├── sql.hurl # 5 tests: benign queries + unprotected SQLi demo -│ ├── xss.hurl # 2 tests: reflected XSS via customTag.jsp -│ ├── traversal.hurl # 4 tests: benign file + path traversal demo -│ └── negative.hurl # 3 tests: 404, empty result set, no-param graceful -├── rasp/ # RASP-efficacy suite (needs Waratek agent) -│ ├── protected.env # Variables: host, port, block_status=550 -│ ├── mysql/ # MySQL servlet tests (139 cases, 5 files) -│ │ ├── get_int.hurl -│ │ ├── get_string.hurl -│ │ ├── get_union.hurl -│ │ ├── get_implicit_join.hurl -│ │ └── implicit_join_namespace.hurl -│ └── oracle/ # Oracle servlet tests (301 cases, 12 files) -│ ├── get_int.hurl -│ ├── get_string.hurl -│ └── ... -└── smoke/ # Smoke suite (no agent required) - ├── local.env # Variables: host=localhost, port=8080 - └── smoke.hurl # 3 tests: up-check, benign query, SQLi succeeds -``` +`tests/hurl/` holds three suites: + +- `smoke/` and `functional/` — endpoint behaviour on a plain (no-agent) deployment. +- `rasp/` — the RASP-efficacy matrix under `mysql/` and `oracle/`; requires the Waratek agent. -Source of truth for the RASP payload matrices: -- `tests/mysql.txt` (139 cases) -- `tests/oracle.txt` (301 cases) +The `rasp/` payload matrices are generated from `tests/mysql.txt` and `tests/oracle.txt`. --- From 41892777ab1fcadae70c131aebbf7f17544e9c56 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Mon, 8 Jun 2026 17:11:07 +0100 Subject: [PATCH 09/13] Remove obsolete Hurl generator (generate.py + mysql.txt/oracle.txt); the .hurl files are now the source of truth --- tests/hurl/README.md | 22 --- tests/hurl/generate.py | 173 ----------------------- tests/mysql.txt | 139 ------------------- tests/oracle.txt | 301 ----------------------------------------- 4 files changed, 635 deletions(-) delete mode 100644 tests/hurl/generate.py delete mode 100644 tests/mysql.txt delete mode 100644 tests/oracle.txt diff --git a/tests/hurl/README.md b/tests/hurl/README.md index ae271c5..a0f6c16 100644 --- a/tests/hurl/README.md +++ b/tests/hurl/README.md @@ -33,8 +33,6 @@ The RASP suite will fail entirely without the agent — this is expected. - `smoke/` and `functional/` — endpoint behaviour on a plain (no-agent) deployment. - `rasp/` — the RASP-efficacy matrix under `mysql/` and `oracle/`; requires the Waratek agent. -The `rasp/` payload matrices are generated from `tests/mysql.txt` and `tests/oracle.txt`. - --- ## Running the functional suite (plain Docker / CI) @@ -112,26 +110,6 @@ Override with `REPORT_DIR=/path/to/dir ./tests/hurl/run.sh ...`. --- -## Regenerating the .hurl files - -If `mysql.txt` or `oracle.txt` are updated, regenerate: - -```sh -python3 tests/hurl/generate.py -``` - -The generator: -- Reads `tests/mysql.txt` and `tests/oracle.txt` (one case per line, `` delimiter) -- Groups cases by servlet path -- Encodes URL-illegal characters (`space`, `|`, `"`, `<`, `>`) in query strings -- Emits `status == {{block_status}}` for 550-expected cases (variable-driven) -- Emits `status == 200` (literal) for the one benign probe case in mysql.txt -- Overwrites all files under `tests/hurl/rasp/` - -Commit the regenerated files — the suite must run without needing to regenerate. - ---- - ## Hurl assertion form used `HTTP {{var}}` in the status line is **not** valid in Hurl 5.x. diff --git a/tests/hurl/generate.py b/tests/hurl/generate.py deleted file mode 100644 index 4c56310..0000000 --- a/tests/hurl/generate.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -""" -Generate Hurl RASP test files from the -delimited payload matrices. - -Usage: - python3 tests/hurl/generate.py - -Reads: - tests/mysql.txt - tests/oracle.txt - -Writes: - tests/hurl/rasp/mysql/.hurl - tests/hurl/rasp/oracle/.hurl - -Each file contains one Hurl entry per test case for that servlet. -Status assertion uses: - HTTP * - [Asserts] - status == {{block_status}} - -so the expected status (550 for RASP-blocked, 200 for benign) is -injected at runtime via --variable block_status=550. - -NOTE: Raw spaces in URLs break Hurl's URL parser; the generator -percent-encodes bare spaces (0x20) only, preserving all other -characters (including already-encoded sequences like %25, %27, etc.) -exactly as they appear in the source files. -""" - -import os -import re -import sys -from collections import defaultdict - -TESTS_DIR = os.path.join(os.path.dirname(__file__), "..") -RASP_DIR = os.path.join(os.path.dirname(__file__), "rasp") - -SOURCES = { - "mysql": os.path.join(TESTS_DIR, "mysql.txt"), - "oracle": os.path.join(TESTS_DIR, "oracle.txt"), -} - -# Map servlet path segment → output filename (lowercase, underscores) -SERVLET_NAME_MAP = { - # MySQL servlets - "MySql_Get_int": "get_int", - "MySql_Get_string": "get_string", - "MySql_Get_Implicit_Join": "get_implicit_join", - "MySql_Implicit_Join_Namespace": "implicit_join_namespace", - "Get_Union": "get_union", # shared by both; mysql.txt uses it - # Oracle servlets - "Get_int": "get_int", - "Get_int_no_quote": "get_int_no_quote", - "Get_int_partialunion": "get_int_partialunion", - "Get_int_groupby": "get_int_groupby", - "Get_int_nooutput": "get_int_nooutput", - "Get_int_having": "get_int_having", - "Get_int_inline": "get_int_inline", - "Get_string": "get_string", - "Get_string_no_quote": "get_string_no_quote", - "Get_Implicit_Join": "get_implicit_join", - "Get_Full_Outer_Join": "get_full_outer_join", -} - - -def encode_url_illegal(s): - """ - Percent-encode characters that Hurl's URL parser rejects in GET lines. - - Hurl rejects: space, |, ", <, > - Everything else (including already-encoded %xx sequences, ', (, ), etc.) - is left intact so payload semantics are preserved exactly. - """ - replacements = [ - (" ", "%20"), - ("|", "%7C"), - ('"', "%22"), - ("<", "%3C"), - (">", "%3E"), - ] - for char, enc in replacements: - s = s.replace(char, enc) - return s - - -def servlet_from_path(path): - """Extract servlet name from /spiracle/.""" - return path.lstrip("/").split("/")[-1] - - -def output_filename(servlet): - return SERVLET_NAME_MAP.get(servlet, servlet.lower()) + ".hurl" - - -def parse_data_file(filepath): - """Return list of (path, querystring, expected_status) tuples.""" - cases = [] - with open(filepath, encoding="utf-8") as f: - for lineno, line in enumerate(f, 1): - line = line.rstrip("\n") - if not line: - continue - parts = line.split("") - if len(parts) != 3: - print( - f"WARNING: {filepath}:{lineno} — expected 3 parts, got {len(parts)}: {line!r}", - file=sys.stderr, - ) - continue - cases.append((parts[0], parts[1], parts[2])) - return cases - - -def generate_hurl_file(cases, base_url_template): - """ - Build Hurl file content for a list of (path, querystring, expected_status). - base_url_template: string with {path} and {querystring} slots. - """ - lines = [] - for path, qs, expected_status in cases: - # Encode URL-illegal chars in querystring only (not path) - safe_qs = encode_url_illegal(qs) - url = "http://{{{{host}}}}:{{{{port}}}}{path}{qs}".format( - path=path, qs=safe_qs - ) - lines.append(f"GET {url}") - lines.append("") - lines.append("HTTP *") - lines.append("[Asserts]") - # Use {{block_status}} variable for the standard blocked status (550). - # Cases with a different expected status (e.g. 200 for a benign probe) - # get the literal value so they remain correct regardless of variables. - if expected_status == "550": - lines.append(f"status == {{{{block_status}}}}") - else: - lines.append(f"status == {expected_status}") - lines.append("") - return "\n".join(lines) - - -def main(): - total = 0 - by_db = {} - - for db, filepath in SOURCES.items(): - cases = parse_data_file(filepath) - print(f"Read {len(cases)} cases from {filepath}") - total += len(cases) - by_db[db] = cases - - # Group by servlet - groups = defaultdict(list) - for path, qs, expected_status in cases: - servlet = servlet_from_path(path) - groups[servlet].append((path, qs, expected_status)) - - out_dir = os.path.join(RASP_DIR, db) - os.makedirs(out_dir, exist_ok=True) - - for servlet, servlet_cases in sorted(groups.items()): - fname = output_filename(servlet) - out_path = os.path.join(out_dir, fname) - content = generate_hurl_file(servlet_cases, "") - with open(out_path, "w", encoding="utf-8") as f: - f.write(content) - print(f" Wrote {len(servlet_cases):3d} cases → {out_path}") - - print(f"\nTotal: {total} cases converted.") - - -if __name__ == "__main__": - main() diff --git a/tests/mysql.txt b/tests/mysql.txt deleted file mode 100644 index c4ecf66..0000000 --- a/tests/mysql.txt +++ /dev/null @@ -1,139 +0,0 @@ -/spiracle/MySql_Get_int?id=2' OR name LIKE '%25'--550 -/spiracle/MySql_Get_int?id=1' || name like '%25'--550 -/spiracle/MySql_Get_int?id=1' or users.name LIKE '%25'--550 -/spiracle/MySql_Get_int?id=1' or id like '%25550 -/spiracle/MySql_Get_int?id=1' OR 100=(SELECT COUNT(*) FROM users) --550 -/spiracle/MySql_Get_int?id=1' OR users.cvv IS NULL --550 -/spiracle/MySql_Get_int?id=1' AND DOB IS NULL --550 -/spiracle/MySql_Get_int?id=1' OR cvv IS NULL --550 -/spiracle/MySql_Get_int?id=1' OR 'x'='x'--550 -/spiracle/MySql_Get_int?id=1' or 1=1 or 'x'='y'--550 -/spiracle/MySql_Get_int?id=1' UNION ALL SELECT * from users where id=1 or 1=1 --550 -/spiracle/MySql_Get_int?id=1' or 1=1 or ''=''--550 -/spiracle/MySql_Get_int?id=' or 1=1 or ''=''--550 -/spiracle/MySql_Get_int?id=("hi")' or ('a'='a')--550 -/spiracle/MySql_Get_int?id=a' or 'a' = 'a'--550 -/spiracle/MySql_Get_int?id=joe' or '1'='1'--550 -/spiracle/MySql_Get_int?id=1' UNION SELECT * from users where id=1 or 1=1--550 -/spiracle/MySql_Get_int?id=hi' or 1=1--550 -/spiracle/MySql_Get_int?id=hi' or 'a'='a'--550 -/spiracle/MySql_Get_int?id=a' or 1=1--550 -/spiracle/MySql_Get_int?id=%27%20or%20'x'='x'--550 -/spiracle/MySql_Get_int?id=' or 0=0 --550 -/spiracle/MySql_Get_int?id=a' or 3=3--550 -/spiracle/MySql_Get_int?id=Joe' or 0=0 --550 -/spiracle/MySql_Get_int?id=Joe' or 'a'='a'--550 -/spiracle/MySql_Get_int?id=1' or ''=''--550 -/spiracle/MySql_Get_int?id=' or 1=1--550 -/spiracle/MySql_Get_int?id='%20or%201=1--550 -/spiracle/MySql_Get_int?id=%27%20or%201=1--550 -/spiracle/MySql_Get_int?id=' or 3=3--550 -/spiracle/MySql_Get_string?name=x' or name like '%25'--550 -/spiracle/MySql_Get_string?name=%3C%3E%22%2527%25%3B)(%26%2B200 -/spiracle/MySql_Get_string?name=x' AND 100=(SELECT COUNT(*) FROM address) --550 -/spiracle/MySql_Get_string?name=x' OR cvv IS NULL--550 -/spiracle/MySql_Get_string?name=anything' OR 'x'='x550 -/spiracle/MySql_Get_string?name=x' or 1=1 or 'x'='y550 -/spiracle/MySql_Get_string?name=' UNION ALL SELECT * from users where id=1 or 1=1--550 -/spiracle/MySql_Get_string?name='or 1=1 or ''='550 -/spiracle/MySql_Get_string?name=a' or 'a' = 'a550 -/spiracle/MySql_Get_string?name=' or '1'='1'--550 -/spiracle/MySql_Get_string?name=' UNION SELECT * from users--550 -/spiracle/MySql_Get_string?name=a' or 1=1--550 -/spiracle/MySql_Get_string?name=%27%20or%20%27x%27=%27x550 -/spiracle/MySql_Get_string?name='or 0=0 --550 -/spiracle/MySql_Get_string?name=a' or 3=3--550 -/spiracle/MySql_Get_string?name=' or 1=1--550 -/spiracle/MySql_Get_string?name=' or 1 --'550 -/spiracle/MySql_Get_string?name=' or 'a'='a'--550 -/spiracle/MySql_Get_string?name=%27%20or%201=1--550 -/spiracle/MySql_Get_string?name=x' or 3=3--550 -/spiracle/Get_Union?id='2' OR 'name' LIKE 'Joe'550 -/spiracle/Get_Union?id='1' or 'name' like 'Joe'550 -/spiracle/Get_Union?id='1' or id like 1550 -/spiracle/Get_Union?id='1' or 'name' like'Joe'550 -/spiracle/Get_Union?id='1' OR 120=(SELECT COUNT(*) FROM users)550 -/spiracle/Get_Union?id='1' AND 'users.cvv' IS NULL550 -/spiracle/Get_Union?id='1' AND 'DOB' IS NULL550 -/spiracle/Get_Union?id='1' AND 'cvv' IS NULL550 -/spiracle/Get_Union?id='1' OR 'x'='x'550 -/spiracle/Get_Union?id='1' or 1=1 or 'x'='y'550 -/spiracle/Get_Union?id='1' or 1=1 or ''=''--550 -/spiracle/Get_Union?id=' or 1=1 or ''=''--550 -/spiracle/Get_Union?id='("hi")' or ('a'='a')--550 -/spiracle/Get_Union?id='a' or 'a' = 'a'550 -/spiracle/Get_Union?id='joe' or '1'='1'550 -/spiracle/Get_Union?id='hi' or 1=1--550 -/spiracle/Get_Union?id='hi' or 'a'='a'--550 -/spiracle/Get_Union?id='a' or 1=1--550 -/spiracle/Get_Union?id=''%20or%20'x'='x'--550 -/spiracle/Get_Union?id='' or 0=0 --550 -/spiracle/Get_Union?id='a' or 3=3--550 -/spiracle/Get_Union?id='Joe' or 0=0 --550 -/spiracle/Get_Union?id='Joe' or 'a'='a'--550 -/spiracle/Get_Union?id=1 or 'a'='a'550 -/spiracle/Get_Union?id=1 or '1'='1'550 -/spiracle/Get_Union?id=' or 1=1--550 -/spiracle/Get_Union?id=%27%20or%201=1--550 -/spiracle/Get_Union?id=' %20or%201=1--550 -/spiracle/Get_Union?id=' or 3=3--550 -/spiracle/MySql_Get_Implicit_Join?id='2' OR 'name' LIKE 'Joe'550 -/spiracle/MySql_Get_Implicit_Join?id='1' or 'name' like 'Joe'550 -/spiracle/MySql_Get_Implicit_Join?id='1' or users.id like 1550 -/spiracle/MySql_Get_Implicit_Join?id='1' or 'users.name' like'Joe'550 -/spiracle/MySql_Get_Implicit_Join?id='1' OR 120=(SELECT COUNT(*) FROM users)550 -/spiracle/MySql_Get_Implicit_Join?id='1' AND 'users.cvv' IS NULL550 -/spiracle/MySql_Get_Implicit_Join?id='1' AND 'DOB' IS NULL550 -/spiracle/MySql_Get_Implicit_Join?id='1' AND 'cvv' IS NULL550 -/spiracle/MySql_Get_Implicit_Join?id='1' OR 'x'='x'550 -/spiracle/MySql_Get_Implicit_Join?id='1' or 1=1 or 'x'='y'550 -/spiracle/MySql_Get_Implicit_Join?id='1' or 1=1 or ''=''--550 -/spiracle/MySql_Get_Implicit_Join?id='' or 1=1 or ''=''--550 -/spiracle/MySql_Get_Implicit_Join?id=('hi') or ('a'='a')550 -/spiracle/MySql_Get_Implicit_Join?id='a' or 'a' = 'a'550 -/spiracle/MySql_Get_Implicit_Join?id='joe' or '1'='1'550 -/spiracle/MySql_Get_Implicit_Join?id='hi' or 1=1--550 -/spiracle/MySql_Get_Implicit_Join?id='hi' or 'a'='a'--550 -/spiracle/MySql_Get_Implicit_Join?id='a' or 1=1--550 -/spiracle/MySql_Get_Implicit_Join?id=''%20or%20'x'='x'--550 -/spiracle/MySql_Get_Implicit_Join?id='' or 0=0 --550 -/spiracle/MySql_Get_Implicit_Join?id='a' or 3=3--550 -/spiracle/MySql_Get_Implicit_Join?id='Joe' or 0=0 --550 -/spiracle/MySql_Get_Implicit_Join?id='Joe' or 'a'='a'--550 -/spiracle/MySql_Get_Implicit_Join?id=1 or 'a'='a'550 -/spiracle/MySql_Get_Implicit_Join?id=1 or '1'='1'550 -/spiracle/MySql_Get_Implicit_Join?id=1 or '1'='1'550 -/spiracle/MySql_Get_Implicit_Join?id='' or 1=1--550 -/spiracle/MySql_Get_Implicit_Join?id='' %20or%201=1--550 -/spiracle/MySql_Get_Implicit_Join?id='' or 3=3--550 -/spiracle/MySql_Get_Implicit_Join?id=%27%27%20or%201=1--550 -/spiracle/MySql_Implicit_Join_Namespace?id='2' OR 'name' LIKE 'Joe'550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' or 'name' like 'Joe'550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' or users.id like 1550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' or 'users.name' like'Joe'550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' OR 120=(SELECT COUNT(*) FROM users)550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' AND 'users.cvv' IS NULL550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' AND 'DOB' IS NULL550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' AND 'cvv' IS NULL550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' OR 'x'='x'550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' or 1=1 or 'x'='y'550 -/spiracle/MySql_Implicit_Join_Namespace?id='1' or 1=1 or ''=''--550 -/spiracle/MySql_Implicit_Join_Namespace?id='' or 1=1 or ''=''--550 -/spiracle/MySql_Implicit_Join_Namespace?id=('hi') or ('a'='a')550 -/spiracle/MySql_Implicit_Join_Namespace?id='a' or 'a' = 'a'550 -/spiracle/MySql_Implicit_Join_Namespace?id='joe' or '1'='1'550 -/spiracle/MySql_Implicit_Join_Namespace?id='hi' or 1=1--550 -/spiracle/MySql_Implicit_Join_Namespace?id='hi' or 'a'='a'--550 -/spiracle/MySql_Implicit_Join_Namespace?id='a' or 1=1--550 -/spiracle/MySql_Implicit_Join_Namespace?id=''%20or%20'x'='x'--550 -/spiracle/MySql_Implicit_Join_Namespace?id='' or 0=0 --550 -/spiracle/MySql_Implicit_Join_Namespace?id='a' or 3=3--550 -/spiracle/MySql_Implicit_Join_Namespace?id='Joe' or 0=0 --550 -/spiracle/MySql_Implicit_Join_Namespace?id='Joe' or 'a'='a'--550 -/spiracle/MySql_Implicit_Join_Namespace?id=1 or 'a'='a'550 -/spiracle/MySql_Implicit_Join_Namespace?id=1 or '1'='1'550 -/spiracle/MySql_Implicit_Join_Namespace?id=1 or '1'='1'550 -/spiracle/MySql_Implicit_Join_Namespace?id='' or 1=1--550 -/spiracle/MySql_Implicit_Join_Namespace?id='' %20or%201=1--550 -/spiracle/MySql_Implicit_Join_Namespace?id='' or 3=3--550 -/spiracle/MySql_Implicit_Join_Namespace?id=%27%27%20or%201=1--550 diff --git a/tests/oracle.txt b/tests/oracle.txt deleted file mode 100644 index 276629d..0000000 --- a/tests/oracle.txt +++ /dev/null @@ -1,301 +0,0 @@ -/spiracle/Get_int?id=2' OR name LIKE '%25550 -/spiracle/Get_int?id=1' or name like '%25550 -/spiracle/Get_int?id=1' or id like '%25550 -/spiracle/Get_int?id=' or name like '%25550 -/spiracle/Get_int?id=1' OR 120=(SELECT COUNT(*) FROM users) --550 -/spiracle/Get_int?id=1' AND users.cvv IS NULL --550 -/spiracle/Get_int?id=1' AND DOB IS NULL --550 -/spiracle/Get_int?id=1' AND cvv IS NULL --550 -/spiracle/Get_int?id=1' OR 'x'='x'--550 -/spiracle/Get_int?id=1' or 1=1 or 'x'='y'--550 -/spiracle/Get_int?id=1' UNION ALL SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_int?id=1' or 1=1 or ''=''--550 -/spiracle/Get_int?id=' or 1=1 or ''=''--550 -/spiracle/Get_int?id=("hi")' or ('a'='a')--550 -/spiracle/Get_int?id=a' or 'a' = 'a'--550 -/spiracle/Get_int?id=joe' or '1'='1'--550 -/spiracle/Get_int?id=1' UNION SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_int?id=hi' or 1=1--550 -/spiracle/Get_int?id=hi' or 'a'='a'--550 -/spiracle/Get_int?id=a' or 1=1--550 -/spiracle/Get_int?id=%20or%20'x'='x'--550 -/spiracle/Get_int?id=' or 0=0 --550 -/spiracle/Get_int?id=a' or 3=3--550 -/spiracle/Get_int?id=Joe' or 0=0 --550 -/spiracle/Get_int?id=Joe' or 'a'='a'--550 -/spiracle/Get_int?id=1' or ''=''--550 -/spiracle/Get_int?id=' or 1=1--550 -/spiracle/Get_int?id='%20or%201=1--550 -/spiracle/Get_int?id=%27%20or%201=1--550 -/spiracle/Get_int?id=' or 3=3--550 -/spiracle/Get_int_no_quote?id=2 OR name LIKE '%25'550 -/spiracle/Get_int_no_quote?id=1 or name like '%25'550 -/spiracle/Get_int_no_quote?id=1 or id like '%25'550 -/spiracle/Get_int_no_quote?id=1 or name like '%25'550 -/spiracle/Get_int_no_quote?id=1 OR 120=(SELECT COUNT(*) FROM users) --550 -/spiracle/Get_int_no_quote?id=1 AND users.cvv IS NULL --550 -/spiracle/Get_int_no_quote?id=1 AND DOB IS NULL --550 -/spiracle/Get_int_no_quote?id=1 AND cvv IS NULL --550 -/spiracle/Get_int_no_quote?id=1 OR 'x'='x'550 -/spiracle/Get_int_no_quote?id=1 or 1=1 or 'x'='y'550 -/spiracle/Get_int_no_quote?id=1 UNION ALL SELECT * from users where id=1 or 1=1550 -/spiracle/Get_int_no_quote?id=1 or 1=1 or ''=''550 -/spiracle/Get_int_no_quote?id='' or 1=1 or ''=''550 -/spiracle/Get_int_no_quote?id=('hi') or ('a'='a')550 -/spiracle/Get_int_no_quote?id='a' or 'a' = 'a'550 -/spiracle/Get_int_no_quote?id=1 UNION SELECT * from users where id=1 or 1=1550 -/spiracle/Get_int_no_quote?id='joe' or '1'='1'--550 -/spiracle/Get_int_no_quote?id=(%27%27)%20or%20(%27x%27=%27x%27)550 -/spiracle/Get_int_no_quote?id=('') or ('a'='a')550 -/spiracle/Get_int_no_quote?id='hi' or 1=1--550 -/spiracle/Get_int_no_quote?id='hi' or 'a'='a'550 -/spiracle/Get_int_no_quote?id='a' or 1=1--550 -/spiracle/Get_int_no_quote?id=''%20or%20'x'='x'550 -/spiracle/Get_int_no_quote?id='' or 0=0 --550 -/spiracle/Get_int_no_quote?id='a' or 3=3--550 -/spiracle/Get_int_no_quote?id='Joe' or 0=0 #550 -/spiracle/Get_int_no_quote?id='Joe' or 'a'='a'550 -/spiracle/Get_int_no_quote?id=1%20or%20''=''550 -/spiracle/Get_int_no_quote?id=1 or ''=''550 -/spiracle/Get_int_no_quote?id='Joe' or 1=1550 -/spiracle/Get_int_no_quote?id='Joe' %20or%201=1550 -/spiracle/Get_int_no_quote?id='Joe %27%20or%201=1550 -/spiracle/Get_int_no_quote?id='Joe' or 3=3550 -/spiracle/Get_int_partialunion?id=2' OR name LIKE '%25550 -/spiracle/Get_int_partialunion?id=1' or name like '%25550 -/spiracle/Get_int_partialunion?id=1' or id like '%25550 -/spiracle/Get_int_partialunion?id=' or name like '%25550 -/spiracle/Get_int_partialunion?id=1' OR 120=(SELECT COUNT(*) FROM users) --550 -/spiracle/Get_int_partialunion?id=1' AND users.cvv IS NULL --550 -/spiracle/Get_int_partialunion?id=1' AND DOB IS NULL --550 -/spiracle/Get_int_partialunion?id=1' AND cvv IS NULL --550 -/spiracle/Get_int_partialunion?id=1' OR 'x'='x'--550 -/spiracle/Get_int_partialunion?id=1' or 1=1 or 'x'='y'--550 -/spiracle/Get_int_partialunion?id=1' UNION ALL SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_int_partialunion?id=1' or 1=1 or ''=''--550 -/spiracle/Get_int_partialunion?id=' or 1=1 or ''=''--550 -/spiracle/Get_int_partialunion?id=("hi")' or ('a'='a')--550 -/spiracle/Get_int_partialunion?id=a' or 'a' = 'a'--550 -/spiracle/Get_int_partialunion?id=joe' or '1'='1'--550 -/spiracle/Get_int_partialunion?id=1' UNION SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_int_partialunion?id=hi' or 1=1--550 -/spiracle/Get_int_partialunion?id=hi' or 'a'='a'--550 -/spiracle/Get_int_partialunion?id=a' or 1=1--550 -/spiracle/Get_int_partialunion?id='%20or%20'x'='x'--550 -/spiracle/Get_int_partialunion?id=' or 0=0 --550 -/spiracle/Get_int_partialunion?id=a' or 3=3--550 -/spiracle/Get_int_partialunion?id=Joe' or 0=0 --550 -/spiracle/Get_int_partialunion?id=Joe' or 'a'='a'--550 -/spiracle/Get_int_partialunion?id=1' or ''=''--550 -/spiracle/Get_int_partialunion?id=' or 1=1--550 -/spiracle/Get_int_partialunion?id=' %20or%201=1--550 -/spiracle/Get_int_partialunion?id=%27%20or%201=1--550 -/spiracle/Get_int_partialunion?id=' or 3=3--550 -/spiracle/Get_int_groupby?id=name union all select null, to_char(dob) from users550 -/spiracle/Get_int_groupby?id=name union select null, to_char(dob) from users550 -/spiracle/Get_int_nooutput?id=2' OR name LIKE '%25550 -/spiracle/Get_int_nooutput?id=1' or name like '%25550 -/spiracle/Get_int_nooutput?id=1' or id like '%25550 -/spiracle/Get_int_nooutput?id=' or name like '%25550 -/spiracle/Get_int_nooutput?id=1' OR 120=(SELECT COUNT(*) FROM users) --550 -/spiracle/Get_int_nooutput?id=1' AND users.cvv IS NULL --550 -/spiracle/Get_int_nooutput?id=1' AND DOB IS NULL --550 -/spiracle/Get_int_nooutput?id=1' AND cvv IS NULL --550 -/spiracle/Get_int_nooutput?id=1' OR 'x'='x'--550 -/spiracle/Get_int_nooutput?id=1' or 1=1 or 'x'='y'--550 -/spiracle/Get_int_nooutput?id=1' UNION ALL SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_int_nooutput?id=1' or 1=1 or ''=''--550 -/spiracle/Get_int_nooutput?id=' or 1=1 or ''=''--550 -/spiracle/Get_int_nooutput?id=("hi")' or ('a'='a')--550 -/spiracle/Get_int_nooutput?id=a' or 'a' = 'a'--550 -/spiracle/Get_int_nooutput?id=joe' or '1'='1'--550 -/spiracle/Get_int_nooutput?id=1' UNION SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_int_nooutput?id=hi' or 1=1--550 -/spiracle/Get_int_nooutput?id=hi' or 'a'='a'--550 -/spiracle/Get_int_nooutput?id=a' or 1=1--550 -/spiracle/Get_int_nooutput?id='%20or%20'x'='x'--550 -/spiracle/Get_int_nooutput?id=' or 0=0 --550 -/spiracle/Get_int_nooutput?id=a' or 3=3--550 -/spiracle/Get_int_nooutput?id=Joe' or 0=0 --550 -/spiracle/Get_int_nooutput?id=Joe' or 'a'='a'--550 -/spiracle/Get_int_nooutput?id=1' or ''=''--550 -/spiracle/Get_int_nooutput?id=' or 1=1--550 -/spiracle/Get_int_nooutput?id=1%27%20or%201=1--550 -/spiracle/Get_int_nooutput?id=' %20or%201=1--550 -/spiracle/Get_int_nooutput?id=' or 3=3--550 -/spiracle/Get_int_having?id=1 union select to_char(dob) from users550 -/spiracle/Get_int_having?id=1 union all select to_char(dob) from users550 -/spiracle/Get_int_inline?id=select * from users where name = 'x' OR name LIKE 'Joe'550 -/spiracle/Get_int_inline?id=select * from users where name = '' or name like ''550 -/spiracle/Get_int_inline?id=select * from users where id = '' or id like ''550 -/spiracle/Get_int_inline?id=select * from users where name = '' or name like ''550 -/spiracle/Get_int_inline?id=select * from users where name ='x' AND users.cvv IS NULL --550 -/spiracle/Get_int_inline?id=select * from users where name = 'x' OR 120=(SELECT COUNT(*) FROM users) --550 -/spiracle/Get_int_inline?id=select * from users where name ='Joe' AND DOB IS NULL --550 -/spiracle/Get_int_inline?id=select * from users where name ='Joe' AND cvv IS NULL --550 -/spiracle/Get_int_inline?id=select * from users where name ='anything' OR 'x'='x'550 -/spiracle/Get_int_inline?id=select * from users where name = 'x' or 1=1 or 'x'='y'550 -/spiracle/Get_int_inline?id=select * from users where name ='Joe' UNION ALL SELECT * from users where id=1 or 1=1550 -/spiracle/Get_int_inline?id=select * from users where name = '' or 1=1 or ''=''550 -/spiracle/Get_int_inline?id=select * from users where name = ('hi') or ('a'='a')550 -/spiracle/Get_int_inline?id=select * from users where name = 'a' or 'a' = 'a'550 -/spiracle/Get_int_inline?id=select * from users where name = 'joe' or '1'='1'--550 -/spiracle/Get_int_inline?id=select * from users where name ='Joe' UNION SELECT * from users where id=1 or 1=1550 -/spiracle/Get_int_inline?id=select * from users where name = (%27%27)%20or%20(%27x%27=%27x%27)550 -/spiracle/Get_int_inline?id=select * from users where name=('') or ('a'='a')550 -/spiracle/Get_int_inline?id=select * from users where name='hi' or 1=1--550 -/spiracle/Get_int_inline?id=select * from users where name='hi' or 'a'='a'550 -/spiracle/Get_int_inline?id=select * from users where name='a' or 1=1--550 -/spiracle/Get_int_inline?id=select * from users where name=''%20or%20'x'='x'550 -/spiracle/Get_int_inline?id=select * from users where name='' or 0=0 --550 -/spiracle/Get_int_inline?id=select * from users where name='a' or 3=3--550 -/spiracle/Get_int_inline?id=select * from users where name='Joe' or 0=0 #550 -/spiracle/Get_int_inline?id=select * from users where name='Joe' or 'a'='a'550 -/spiracle/Get_int_inline?id=select * from users where name='Joe'%20or%20''=''550 -/spiracle/Get_int_inline?id=select * from users where name='Joe' or 1=1550 -/spiracle/Get_int_inline?id=select * from users where name='Joe' or ''=''550 -/spiracle/Get_int_inline?id=select * from users where name='Joe' %20or%201=1550 -/spiracle/Get_int_inline?id=select * from users where name='Joe %27%20or%201=1550 -/spiracle/Get_int_inline?id=select * from users where name='Joe' or 3=3550 -/spiracle/Get_string?name=x' or name like '%25'--550 -/spiracle/Get_string?name=t'exec master..xp_cmdshell 'nslookup%20www%2egoogle%2ecom'--550 -/spiracle/Get_string?name='%3b%20exec%20master%2e%2exp_cmdshell%20'ping%20172%2e10%2e1%2e255'--550 -/spiracle/Get_string?name=x' AND 100=(SELECT COUNT(*) FROM address) --550 -/spiracle/Get_string?name=x' OR cvv IS NULL--550 -/spiracle/Get_string?name=anything' OR 'x'='x550 -/spiracle/Get_string?name=x' or 1=1 or 'x'='y550 -/spiracle/Get_string?name=' UNION ALL SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_string?name='or 1=1 or ''='550 -/spiracle/Get_string?name=a' or 'a' = 'a550 -/spiracle/Get_string?name=' or '1'='1'--550 -/spiracle/Get_string?name=' UNION SELECT * from users--550 -/spiracle/Get_string?name=a' or 1=1--550 -/spiracle/Get_string?name=%27%20or%20%27x%27=%27x550 -/spiracle/Get_string?name='or 0=0 --550 -/spiracle/Get_string?name=a' or 3=3--550 -/spiracle/Get_string?name=' or 1=1--550 -/spiracle/Get_string?name=' or 1 --'550 -/spiracle/Get_string?name=' or 'a'='a'--550 -/spiracle/Get_string?name=%27%20or%201=1--550 -/spiracle/Get_string?name=x' or 3=3--550 -/spiracle/Get_string_no_quote?name='2' OR name LIKE 'Joe'550 -/spiracle/Get_string_no_quote?name='Jon' or name like 'Joe'550 -/spiracle/Get_string_no_quote?name='1' or id like '2'550 -/spiracle/Get_string_no_quote?name='1' or name like 'Joe'550 -/spiracle/Get_string_no_quote?name='Joe' OR 120=(SELECT COUNT(*) FROM users) --550 -/spiracle/Get_string_no_quote?name='1' or name like 'Sammy'550 -/spiracle/Get_string_no_quote?name='1' AND users.cvv IS NULL --550 -/spiracle/Get_string_no_quote?name='1' AND DOB IS NULL --550 -/spiracle/Get_string_no_quote?name='1' AND cvv IS NULL --550 -/spiracle/Get_string_no_quote?name='1' OR 'x'='x'550 -/spiracle/Get_string_no_quote?name='1' or 1=1 or 'x'='y'--550 -/spiracle/Get_string_no_quote?name='1' UNION ALL SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_string_no_quote?name='' or 1=1 or ''=''--550 -/spiracle/Get_string_no_quote?name='1' or 1=1 or ''=''--550 -/spiracle/Get_string_no_quote?name=(1) or ('a'='a')--550 -/spiracle/Get_string_no_quote?name=1 or 'a' = 'a'--550 -/spiracle/Get_string_no_quote?name=1 or '1'='1'--550 -/spiracle/Get_string_no_quote?name='Joe' UNION SELECT * from users where id=1 or 1=1--550 -/spiracle/Get_string_no_quote?name=('') or ('a'='a')--550 -/spiracle/Get_string_no_quote?name=('') or ('x'='x')--550 -/spiracle/Get_string_no_quote?name='' or 'a'='a'--550 -/spiracle/Get_string_no_quote?name='' or 1=1--550 -/spiracle/Get_string_no_quote?name='a' or 1=1--550 -/spiracle/Get_string_no_quote?name=%27%27%20or%20%27x%27%3D%27x%27--550 -/spiracle/Get_string_no_quote?name='a' or 3=3--550 -/spiracle/Get_string_no_quote?name='' or 0=0 --550 -/spiracle/Get_string_no_quote?name='Joe' or 0=0 #550 -/spiracle/Get_string_no_quote?name='Joe' or 'a'='a'--550 -/spiracle/Get_string_no_quote?name='Joe' or 'a'='a'550 -/spiracle/Get_string_no_quote?name='1' or ''=''--550 -/spiracle/Get_string_no_quote?name='Joe' %20or%201=1--550 -/spiracle/Get_string_no_quote?name='Joe' or 1=1--550 -/spiracle/Get_string_no_quote?name='Joe %27%20or%201=1--550 -/spiracle/Get_string_no_quote?name='Joe' or 3=3--550 -/spiracle/Get_Implicit_Join?id='2' OR 'name' LIKE 'Joe'550 -/spiracle/Get_Implicit_Join?id='1' or 'name' like 'Joe'550 -/spiracle/Get_Implicit_Join?id='1' or users.id like 1550 -/spiracle/Get_Implicit_Join?id='1' or 'users.name' like'Joe'550 -/spiracle/Get_Implicit_Join?id='1' OR 120=(SELECT COUNT(*) FROM users)550 -/spiracle/Get_Implicit_Join?id='1' AND 'users.cvv' IS NULL550 -/spiracle/Get_Implicit_Join?id='1' AND 'DOB' IS NULL550 -/spiracle/Get_Implicit_Join?id='1' AND 'cvv' IS NULL550 -/spiracle/Get_Implicit_Join?id='1' OR 'x'='x'550 -/spiracle/Get_Implicit_Join?id='1' or 1=1 or 'x'='y'550 -/spiracle/Get_Implicit_Join?id='1' or 1=1 or ''=''--550 -/spiracle/Get_Implicit_Join?id='' or 1=1 or ''=''--550 -/spiracle/Get_Implicit_Join?id=('hi') or ('a'='a')550 -/spiracle/Get_Implicit_Join?id='a' or 'a' = 'a'550 -/spiracle/Get_Implicit_Join?id='joe' or '1'='1'550 -/spiracle/Get_Implicit_Join?id='hi' or 1=1--550 -/spiracle/Get_Implicit_Join?id='hi' or 'a'='a'--550 -/spiracle/Get_Implicit_Join?id='a' or 1=1--550 -/spiracle/Get_Implicit_Join?id=''%20or%20'x'='x'--550 -/spiracle/Get_Implicit_Join?id='' or 0=0 --550 -/spiracle/Get_Implicit_Join?id='a' or 3=3--550 -/spiracle/Get_Implicit_Join?id='Joe' or 0=0 --550 -/spiracle/Get_Implicit_Join?id='Joe' or 'a'='a'--550 -/spiracle/Get_Implicit_Join?id=1 or 'a'='a'550 -/spiracle/Get_Implicit_Join?id=1 or '1'='1'550 -/spiracle/Get_Implicit_Join?id='' or 1=1--550 -/spiracle/Get_Implicit_Join?id='' %20or%201=1--550 -/spiracle/Get_Implicit_Join?id='' or 3=3--550 -/spiracle/Get_Implicit_Join?id=%27%27%20or%201=1--550 -/spiracle/Get_Full_Outer_Join?id='2' OR 'name' LIKE 'Joe'550 -/spiracle/Get_Full_Outer_Join?id='1' or 'name' like 'Joe'550 -/spiracle/Get_Full_Outer_Join?id='1' or users.id like 1550 -/spiracle/Get_Full_Outer_Join?id='1' or 'users.name' like'Joe'550 -/spiracle/Get_Full_Outer_Join?id='1' OR 120=(SELECT COUNT(*) FROM users)550 -/spiracle/Get_Full_Outer_Join?id='1' AND 'users.cvv' IS NULL550 -/spiracle/Get_Full_Outer_Join?id='1' AND 'DOB' IS NULL550 -/spiracle/Get_Full_Outer_Join?id='1' AND 'cvv' IS NULL550 -/spiracle/Get_Full_Outer_Join?id='1' OR 'x'='x'550 -/spiracle/Get_Full_Outer_Join?id='1' or 1=1 or 'x'='y'550 -/spiracle/Get_Full_Outer_Join?id='1' or 1=1 or ''=''--550 -/spiracle/Get_Full_Outer_Join?id='' or 1=1 or ''=''--550 -/spiracle/Get_Full_Outer_Join?id=('hi') or ('a'='a')550 -/spiracle/Get_Full_Outer_Join?id='a' or 'a' = 'a'550 -/spiracle/Get_Full_Outer_Join?id='joe' or '1'='1'550 -/spiracle/Get_Full_Outer_Join?id='hi' or 'a'='a'--550 -/spiracle/Get_Full_Outer_Join?id='hi' or 1=1--550 -/spiracle/Get_Full_Outer_Join?id='a' or 1=1--550 -/spiracle/Get_Full_Outer_Join?id='' or 0=0 --550 -/spiracle/Get_Full_Outer_Join?id=''%20or%20'x'='x'--550 -/spiracle/Get_Full_Outer_Join?id='a' or 3=3--550 -/spiracle/Get_Full_Outer_Join?id='Joe' or 0=0 --550 -/spiracle/Get_Full_Outer_Join?id='Joe' or 'a'='a'--550 -/spiracle/Get_Full_Outer_Join?id=1 or 'a'='a'550 -/spiracle/Get_Full_Outer_Join?id=1 or '1'='1'550 -/spiracle/Get_Full_Outer_Join?id='' or 1=1--550 -/spiracle/Get_Full_Outer_Join?id='' %20or%201=1--550 -/spiracle/Get_Full_Outer_Join?id=%27%27%20or%201=1--550 -/spiracle/Get_Full_Outer_Join?id='' or 3=3--550 -/spiracle/Get_Union?id='2' OR 'name' LIKE 'Joe'550 -/spiracle/Get_Union?id='1' or 'name' like 'Joe'550 -/spiracle/Get_Union?id='1' or id like 1550 -/spiracle/Get_Union?id='1' or 'name' like'Joe'550 -/spiracle/Get_Union?id='1' OR 120=(SELECT COUNT(*) FROM users)550 -/spiracle/Get_Union?id='1' AND 'users.cvv' IS NULL550 -/spiracle/Get_Union?id='1' AND 'DOB' IS NULL550 -/spiracle/Get_Union?id='1' AND 'cvv' IS NULL550 -/spiracle/Get_Union?id='1' OR 'x'='x'550 -/spiracle/Get_Union?id='1' or 1=1 or 'x'='y'550 -/spiracle/Get_Union?id='1' or 1=1 or ''=''--550 -/spiracle/Get_Union?id=' or 1=1 or ''=''--550 -/spiracle/Get_Union?id='("hi")' or ('a'='a')--550 -/spiracle/Get_Union?id='a' or 'a' = 'a'550 -/spiracle/Get_Union?id='joe' or '1'='1'550 -/spiracle/Get_Union?id='hi' or 1=1--550 -/spiracle/Get_Union?id='hi' or 'a'='a'--550 -/spiracle/Get_Union?id='a' or 1=1--550 -/spiracle/Get_Union?id=''%20or%20'x'='x'--550 -/spiracle/Get_Union?id='' or 0=0 --550 -/spiracle/Get_Union?id='a' or 3=3--550 -/spiracle/Get_Union?id='Joe' or 0=0 --550 -/spiracle/Get_Union?id='Joe' or 'a'='a'--550 -/spiracle/Get_Union?id=1 or 'a'='a'550 -/spiracle/Get_Union?id=1 or '1'='1'550 -/spiracle/Get_Union?id=' or 1=1--550 -/spiracle/Get_Union?id=%27%20or%201=1--550 -/spiracle/Get_Union?id=' %20or%201=1--550 -/spiracle/Get_Union?id=' or 3=3--550 From 76740cd7c5b5c860ac50fa8499e7796441f4dca9 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Tue, 9 Jun 2026 08:12:38 +0100 Subject: [PATCH 10/13] docs: convert README to Markdown, document Java 6+ and RASP - Convert README.adoc -> README.md (GitHub-flavored Markdown) - master now targets Java 6+ (supported version.jdk: 1.6/1.7/1.8); keep the RASP suite (the agent supports Java 6-25) - Reconcile the Docker driver/build notes with the actual image --- README.adoc | 271 ----------------------------------------------- README.md | 261 +++++++++++++++++++++++++++++++++++++++++++++ docker/README.md | 4 +- 3 files changed, 263 insertions(+), 273 deletions(-) delete mode 100644 README.adoc create mode 100644 README.md diff --git a/README.adoc b/README.adoc deleted file mode 100644 index 7fe5825..0000000 --- a/README.adoc +++ /dev/null @@ -1,271 +0,0 @@ -= Spiracle - -Spiracle is an insecure web application used to test system security controls. - -It can be used to read/write arbitrary files and open network connections. -The application is also vulnerable to numerous other vulnerabilities such as: - -* SQL Injection (CWE-89) -* XSS (CWE-79) -* CSRF (CWE-352) -* Path Traversal (CWE-22) -* Deserialization (CWE-502) -* and many more... - -CAUTION: Due to its insecure design, this application should NOT be deployed on an unsecured network or system. Run on localhost or throwaway networks only. - -This application has been tested on the following application servers: - -* Apache Tomcat 7.x -* IBM WebSphere Liberty Core 8.5.5.3 - -Your mileage may vary with other application servers. - -== Docker (quickstart) - -The fastest way to run Spiracle. No local Tomcat or database install needed. - -Pick a database and run its compose file from the repo root: - ----- -# MySQL (smallest image — recommended for first run) -$ docker compose -f docker-compose.mysql.yml up --build - -# Microsoft SQL Server (~1.5 GB image) -$ docker compose -f docker-compose.mssql.yml up --build - -# Oracle XE (~2–4 GB image; first pull takes several minutes) -$ docker compose -f docker-compose.oracle.yml up --build ----- - -Once healthy, browse to: http://localhost:8080/spiracle/ - -Tear down (removes volumes): - ----- -$ docker compose -f docker-compose.mysql.yml down -v ----- - -The multi-stage `Dockerfile` builds the WAR with JDK 8 / Maven and deploys it on Tomcat 9. MySQL, MSSQL, and Oracle JDBC drivers are bundled in the image; `docker/entrypoint.sh` rewrites `conf/Spiracle.properties` from environment variables at startup. - -Full Docker reference: link:docker/README.md[docker/README.md] - -== Installation - -* Download pre-built `spiracle.war` file from the releases page: https://github.com/waratek/spiracle/releases - -=== Tomcat - -* Copy the war file to the `$CATALINA_HOME/webapps/` directory. - -=== Liberty Core - -* Ensure that the application war file is extracted to your server's apps directory: -+ ----- -$ mkdir ./wlp/usr/servers/defaultServer/apps/spiracle -$ cd ./wlp/usr/servers/defaultServer/apps/spiracle -$ jar xvf /path/to/downloaded/spiracle.war ----- - -* Modify `server.xml` as follows: -+ -[source,xml] ----- - - - - - jsp-2.2 - Servlet-3.0 - - - - - - - httpPort="9080" - httpsPort="9443"/> - ----- -+ -<1> Enable `Servlet-3.0` as a feature -<2> Add a `webApplication` tag referencing Spiracle -<3> Change `httpSession` parameter length -<4> Add a `host` attribute - -=== Database setup - -If you would like to run the SQL injection tests, the database should be populated as follows. Data files are available in the web applications `spiracle/conf/` directory after the `spiracle.war` file has been deployed and exploded. - -NOTE: When using Docker, database initialisation is handled automatically by the compose stack. Manual setup is only needed for bare-metal deployments. - -==== Oracle - -. Ensure that the link:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html[Oracle Database JDBC Driver] (ojdbc6.jar) is installed in the applications `WEB-INF/lib/` directory after the `spiracle.war` file is exploded on first run. -. Import the data: -+ ----- -$ sqlplus SYS/password@//127.0.0.1:1521/XE AS SYSDBA < setupdb_oracle.sql ----- -+ -NOTE: This will create a user `test` with password `test`. You should adjust usernames and/or connection URLs dependent on your environment -+ -. Configuration parameters for the Oracle JDBC connection are defined in `conf/Spiracle.properties`: -+ ----- -c3p0.classname=oracle.jdbc.driver.OracleDriver -c3p0.url=jdbc:oracle:thin:@localhost:1521:XE -c3p0.username=test -c3p0.password=test ----- - -NOTE: It may be necessary to restart your application server after deploying the `ojdbc6.jar` or updating database configuration. - -==== MySQL -. Import the data: -+ ----- -$ mysql -u root -p test < setupdb_mysql.sql ----- -+ -NOTE: This will create a user `test` with password `test`. You should adjust usernames and/or connection URLs dependent on your environment -+ -. Configuration parameters for the MySQL JDBC connection are defined in `conf/Spiracle.properties`: -+ ----- -c3p0.classname=com.mysql.jdbc.Driver -c3p0.url=jdbc:jdbc:mysql://localhost:3306/test -c3p0.username=test -c3p0.password=test ----- - -== Running - -After deployment, the Spiracle application will be available at: - ----- -http://ip:port/spiracle/ ----- - -Properties file can be overridden when submitting the request by appending the new value to the URL: - ----- -&connectionType=c3p0.mysql ----- - -== Testing - -Spiracle ships with a link:tests/hurl/README.md[Hurl] test harness under `tests/hurl/`. Tests are endpoint-based and run against any deployment (Docker or bare-metal). - -=== Suites - -[cols="1,1,3",options="header"] -|=== -|Suite |Agent required |What it covers - -|`smoke/` -|No -|App root responds 200; benign query returns data; unblocked SQLi widens result set (confirming injections succeed without agent) - -|`functional/` -|No -|SendRedirect, SQL queries, reflected XSS via `customTag.jsp`, path traversal, 404/empty-result/no-param negative cases - -|`rasp/` -|Yes (Waratek RASP) -|440-case SQL injection matrix (139 MySQL, 301 Oracle); asserts status `550`, which is only emitted when the Waratek agent intercepts the query -|=== - -=== Quick run (plain Docker stack) - ----- -# Bring up MySQL stack -$ docker compose -f docker-compose.mysql.yml up -d - -# Smoke -$ ./tests/hurl/run.sh smoke localhost 8080 - -# Functional -$ ./tests/hurl/run.sh functional localhost 8080 ----- - -The `rasp/` suite will fail on a plain deployment — expected. Run it only with the Waratek agent attached to Tomcat. - -Full harness reference: link:tests/hurl/README.md[tests/hurl/README.md] - -== Building - -Prerequisites: - -* Java 5–8 (see toolchain note below) -* Apache Maven -* link:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html[Oracle Database JDBC Driver] (ojdbc6.jar) - -If you wish to use the database features, ensure that the Oracle database JDBC driver file `ojdbc6.jar` is available under `./src/main/webapp/WEB-INF/lib` - -=== Build flags - -Two flags parameterise the build: - -`-Dversion.jdk=`:: Sets the Java source and target compiler level. Supported values on `master`: `1.5`, `1.6`, `1.7`, `1.8`. -`-Dversion.webxml=<25|30>`:: Selects the Servlet descriptor version (2.5 or 3.0). - -Representative invocations: - ----- -# Java 8, Servlet 3.0 (recommended) -$ mvn install -Dversion.jdk=1.8 -Dversion.webxml=30 - -# Java 6, Servlet 2.5 -$ mvn install -Dversion.jdk=1.6 -Dversion.webxml=25 ----- - -To clean the build infrastructure, run: - - $ mvn clean - -The WAR file will be output to: - - ./target/spiracle.war - -=== Toolchain - -The repo carries a `mise.toml` pinning Temurin 8 and Maven 3.9. With link:https://mise.jdx.dev[mise] installed: - ----- -$ mise install # installs pinned JDK + Maven -$ mvn install -Dversion.jdk=1.8 -Dversion.webxml=30 ----- - -The Docker image pins its own JDK via its base image — no local JDK is needed when building through Docker. - -=== Branch model - -`master`:: Modern, parameterised source tree. Java 5–8 source level. Use `-Dversion.jdk` to select. -`java4`:: Java-1.4-source-compatible variant. Annotations replaced by `web.xml` registration; uses a legacy dependency set. Check out this branch if you need Java 4 compatibility. - -== Recent fixes - -* *#8* — `Content-Type: text/plain` is now set on the `SendRedirect` fallback response (no-parameter case). -* *#33* — `setupdb_mysql.sql` is idempotent; uses `IF [NOT] EXISTS` guards so re-running does not error. -* *#103* — Oracle connection NPE fixed in `CreateC3p0Connection`; was reading non-existent property keys from `Spiracle.properties`. - -== License - ----- -Copyright 2018 Waratek Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. ----- diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e06f9e --- /dev/null +++ b/README.md @@ -0,0 +1,261 @@ +# Spiracle + +Spiracle is an insecure web application used to test system security controls. + +It can be used to read/write arbitrary files and open network connections. +The application is also vulnerable to numerous other vulnerabilities such as: + +- SQL Injection (CWE-89) +- XSS (CWE-79) +- CSRF (CWE-352) +- Path Traversal (CWE-22) +- Deserialization (CWE-502) +- and many more... + +> **⚠️ Caution:** Due to its insecure design, this application should NOT be deployed on an unsecured network or system. Run on localhost or throwaway networks only. + +This application has been tested on the following application servers: + +- Apache Tomcat 7.x +- IBM WebSphere Liberty Core 8.5.5.3 + +Your mileage may vary with other application servers. + +## Docker (quickstart) + +The fastest way to run Spiracle. No local Tomcat or database install needed. + +Pick a database and run its compose file from the repo root: + +```sh +# MySQL (smallest image — recommended for first run) +$ docker compose -f docker-compose.mysql.yml up --build + +# Microsoft SQL Server (~1.5 GB image) +$ docker compose -f docker-compose.mssql.yml up --build + +# Oracle XE (~2–4 GB image; first pull takes several minutes) +$ docker compose -f docker-compose.oracle.yml up --build +``` + +Once healthy, browse to: http://localhost:8080/spiracle/ + +Tear down (removes volumes): + +```sh +$ docker compose -f docker-compose.mysql.yml down -v +``` + +The multi-stage `Dockerfile` builds the WAR with JDK 8 / Maven (`-Dversion.jdk=1.6 -Dversion.webxml=30`) and deploys it on Tomcat 9. `docker/entrypoint.sh` rewrites `conf/Spiracle.properties` from environment variables at startup. + +> **Note — JDBC drivers:** No JDBC driver is bundled in the WAR. The Docker image places the drivers in Tomcat's `lib/`: MySQL Connector/J 5.1.49 (legacy `com.mysql.jdbc.Driver` classname), MSSQL `mssql-jdbc` 12.4.2.jre8, and Oracle `ojdbc8` 21.13. The MySQL stack runs `mysql:8.0` (`--default-authentication-plugin=mysql_native_password`, which Connector/J 5.1.49 can negotiate). + +Full Docker reference: [docker/README.md](docker/README.md) + +## Installation + +- Download pre-built `spiracle.war` file from the releases page: https://github.com/waratek/spiracle/releases + +### Tomcat + +- Copy the war file to the `$CATALINA_HOME/webapps/` directory. + +### Liberty Core + +- Ensure that the application war file is extracted to your server's apps directory: + + ```sh + $ mkdir ./wlp/usr/servers/defaultServer/apps/spiracle + $ cd ./wlp/usr/servers/defaultServer/apps/spiracle + $ jar xvf /path/to/downloaded/spiracle.war + ``` + +- Modify `server.xml` as follows: + + ```xml + + + + + jsp-2.2 + Servlet-3.0 + + + + + + + httpPort="9080" + httpsPort="9443"/> + + ``` + +### Database setup + +If you would like to run the SQL injection tests, the database should be populated as follows. Data files are available in the web applications `spiracle/conf/` directory after the `spiracle.war` file has been deployed and exploded. + +> **Note:** When using Docker, database initialisation is handled automatically by the compose stack. Manual setup is only needed for bare-metal deployments. + +#### Oracle + +1. Ensure that the [Oracle Database JDBC Driver](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html) (ojdbc6.jar) is installed in the applications `WEB-INF/lib/` directory after the `spiracle.war` file is exploded on first run. +2. Import the data: + + ```sh + $ sqlplus SYS/password@//127.0.0.1:1521/XE AS SYSDBA < setupdb_oracle.sql + ``` + + > **Note:** This will create a user `test` with password `test`. You should adjust usernames and/or connection URLs dependent on your environment + +3. Configuration parameters for the Oracle JDBC connection are defined in `conf/Spiracle.properties`: + + ``` + c3p0.classname=oracle.jdbc.driver.OracleDriver + c3p0.url=jdbc:oracle:thin:@localhost:1521:XE + c3p0.username=test + c3p0.password=test + ``` + +> **Note:** It may be necessary to restart your application server after deploying the `ojdbc6.jar` or updating database configuration. + +#### MySQL + +1. Import the data: + + ```sh + $ mysql -u root -p test < setupdb_mysql.sql + ``` + + > **Note:** This will create a user `test` with password `test`. You should adjust usernames and/or connection URLs dependent on your environment + +2. Configuration parameters for the MySQL JDBC connection are defined in `conf/Spiracle.properties`: + + ``` + c3p0.classname=com.mysql.jdbc.Driver + c3p0.url=jdbc:jdbc:mysql://localhost:3306/test + c3p0.username=test + c3p0.password=test + ``` + +## Running + +After deployment, the Spiracle application will be available at: + +``` +http://ip:port/spiracle/ +``` + +Properties file can be overridden when submitting the request by appending the new value to the URL: + +``` +&connectionType=c3p0.mysql +``` + +## Testing + +Spiracle ships with a [Hurl](tests/hurl/README.md) test harness under `tests/hurl/`. Tests are endpoint-based and run against any deployment (Docker or bare-metal). + +### Suites + +| Suite | Agent required | What it covers | +|---|---|---| +| `smoke/` | No | App root responds 200; benign query returns data; unblocked SQLi widens result set (confirming injections succeed without agent) | +| `functional/` | No | SendRedirect, SQL queries, reflected XSS via `customTag.jsp`, path traversal, 404/empty-result/no-param negative cases | +| `rasp/` | Yes (Waratek RASP) | 579-case SQL injection matrix (139 MySQL, 301 Oracle, 139 MSSQL); asserts status `550`, which is only emitted when the Waratek agent intercepts the query | + +> **Note:** The `rasp/` suite requires the Waratek RASP agent attached to Tomcat. The agent supports Java 6 through 25, which is why this matrix lives on `master` (Java 6+) and not on the `java4`/`java5` branches. + +To build and run the rasp suite across the whole Java 6–25 range from a single parameterised `Dockerfile`, use `scripts/rasp-matrix.sh` (e.g. `./scripts/rasp-matrix.sh test 17 mysql` or `./scripts/rasp-matrix.sh sweep "8 11 17 21 25" mysql`). See [docs/multi-version-testing.md](docs/multi-version-testing.md) for the design and the version matrix. + +### Quick run (plain Docker stack) + +```sh +# Bring up MySQL stack +$ docker compose -f docker-compose.mysql.yml up -d + +# Smoke +$ ./tests/hurl/run.sh smoke localhost 8080 + +# Functional +$ ./tests/hurl/run.sh functional localhost 8080 +``` + +The `rasp/` suite will fail on a plain deployment — expected. Run it only with the Waratek agent attached to Tomcat. + +Full harness reference: [tests/hurl/README.md](tests/hurl/README.md) + +## Building + +Prerequisites: + +- Java 8 toolchain (compiles `-source/-target` from `1.6` upward) +- Apache Maven +- [Oracle Database JDBC Driver](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html) (ojdbc6.jar) + +If you wish to use the database features, ensure that the Oracle database JDBC driver file `ojdbc6.jar` is available under `./src/main/webapp/WEB-INF/lib` + +### Build flags + +Two flags parameterise the build: + +- **`-Dversion.jdk=`** — Sets the Java source and target compiler level. Supported values on `master`: `1.6`, `1.7`, `1.8`. (Java 6 is the floor: the Waratek RASP agent supports Java 6 through 25, so the protected matrix targets Java 6+.) +- **`-Dversion.webxml=<25|30>`** — Selects the Servlet descriptor version. `30` (Servlet 3.0) registers servlets from the `@WebServlet` annotations; `25` (Servlet 2.5) registers them explicitly from `web-25.xml`. + +> **Note:** `version.jdk` has no default; `mvn install` without `-Dversion.jdk` fails because the literal `${version.jdk}` is passed to the compiler. Always pass the flag. + +Representative invocations: + +```sh +# Java 8, Servlet 3.0 (recommended) +$ mvn install -Dversion.jdk=1.8 -Dversion.webxml=30 + +# Java 6, Servlet 3.0 (minimum supported level) +$ mvn install -Dversion.jdk=1.6 -Dversion.webxml=30 +``` + +To clean the build infrastructure, run: + +```sh +$ mvn clean +``` + +The WAR file will be output to: + +``` +./target/spiracle.war +``` + +### Toolchain + +This branch builds under a Java 8 toolchain. The Docker image pins its own JDK via its base image (`maven:3.9-eclipse-temurin-8`) — no local JDK is needed when building through Docker. + +### Branch model + +- **`master` (this branch)** — Modern, parameterised source tree. Java 6+ source level (`-Dversion.jdk=1.6|1.7|1.8`). The only branch the Waratek RASP suite runs against, since the agent supports Java 6 through 25. +- **`java5`** — Java 5 source-compatible variant (same source tree, built at `-Dversion.jdk=1.5`). No RASP suite — the agent does not support Java 5. +- **`java4`** — Java-1.4-source-compatible variant. Annotations replaced by `web.xml` registration; uses a legacy dependency set. Check out this branch if you need Java 4 compatibility. + +## Recent fixes + +- **#8** — `Content-Type: text/plain` is now set on the `SendRedirect` fallback response (no-parameter case). +- **#33** — `setupdb_mysql.sql` is idempotent; uses `IF [NOT] EXISTS` guards so re-running does not error. +- **#103** — Oracle connection NPE fixed in `CreateC3p0Connection`; was reading non-existent property keys from `Spiracle.properties`. + +## License + +``` +Copyright 2018 Waratek Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/docker/README.md b/docker/README.md index 02461aa..0e0fb71 100644 --- a/docker/README.md +++ b/docker/README.md @@ -54,8 +54,8 @@ docker compose -f docker-compose.oracle.yml down -v ## How it works -- A multi-stage Dockerfile builds the WAR with JDK 8 / Maven, then deploys it on Tomcat 9. -- MySQL (Connector/J 5.1.49), MSSQL (mssql-jdbc jre8) and Oracle (ojdbc8) JDBC drivers are bundled in the image. +- A multi-stage Dockerfile builds the WAR with JDK 8 / Maven (`-Dversion.jdk=1.6 -Dversion.webxml=30`), then deploys it on Tomcat 9. +- No JDBC driver is bundled in the WAR; the drivers live in Tomcat's `lib/`: MySQL Connector/J 5.1.49, MSSQL mssql-jdbc 12.4.2.jre8, and Oracle ojdbc8 21.13. The MySQL stack runs `mysql:8.0` with `--default-authentication-plugin=mysql_native_password`, which Connector/J 5.1.49 can negotiate. - `docker/entrypoint.sh` rewrites `conf/Spiracle.properties` from environment variables before Tomcat starts. The committed `Spiracle.properties` is never modified. ## Environment variables (app service) From 90c82b1203e8bb9f5ae40898f1f5cdd3efd3f097 Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Tue, 9 Jun 2026 08:20:15 +0100 Subject: [PATCH 11/13] build: parameterise the image to build/run across Java 6-25 - Single Dockerfile driven by build args (BUILD_JDK, VERSION_JDK, RUNTIME_BASE, TOMCAT_*, MAVEN_OPTS); Tomcat assembled in the build stage and COPYd in so the runtime stage installs nothing (works on minimal/legacy JRE bases) - Wire env-driven build.args into the db compose files - Add docker-compose.rasp-agent.yml (agent overlay), scripts/rasp-matrix.sh (version matrix + build/test/sweep/full), and docs/multi-version-testing.md --- Dockerfile | 115 +++++++++------ docker-compose.mssql.yml | 10 ++ docker-compose.mysql.yml | 10 ++ docker-compose.oracle.yml | 10 ++ docker-compose.rasp-agent.yml | 23 +++ docs/multi-version-testing.md | 174 +++++++++++++++++++++++ scripts/rasp-matrix.sh | 258 ++++++++++++++++++++++++++++++++++ 7 files changed, 560 insertions(+), 40 deletions(-) create mode 100644 docker-compose.rasp-agent.yml create mode 100644 docs/multi-version-testing.md create mode 100755 scripts/rasp-matrix.sh diff --git a/Dockerfile b/Dockerfile index e0c7a68..909c212 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,51 +1,86 @@ -# Stage 1: build the WAR using Java 8 + Maven -FROM maven:3.9-eclipse-temurin-8 AS build +# ============================================================================= +# Parameterised multi-version build for Spiracle (Java 6 .. 25). +# +# A SINGLE Dockerfile builds and runs the app on any Java version by varying +# these build args. Defaults reproduce the historical Java 8 / Tomcat 9 image. +# +# BUILD_JDK JDK image used to COMPILE the WAR and assemble Tomcat +# (8|11|17|21|25). No single JDK can target every source +# level, so it is picked per band. +# VERSION_JDK -Dversion.jdk source/target level (1.6|1.7|1.8|9..25). +# VERSION_WEBXML Servlet descriptor (25|30). +# MAVEN_OPTS Extra JVM flags for Maven (the matrix sets --add-opens for +# build JDK >= 16, which the legacy plugins need). +# TOMCAT_MAJOR Tomcat major line (9 for Java 8+; 7/8 for the Java 6/7 tail). +# TOMCAT_VERSION Apache Tomcat patch release (from the dist archive). +# RUNTIME_BASE JRE image the agent actually instruments (e.g. +# eclipse-temurin:17-jre). This is the "run on Java N" knob. +# +# All network/extraction work happens in the BUILD stage (modern image, working +# apt/curl/jar). The runtime stage only COPYs a self-contained Tomcat, so it +# needs nothing but a JRE + shell — it works on minimal or legacy (Java 6/7) +# bases whose package repos are dead. See docs/multi-version-testing.md. +# ============================================================================= + +# ARGs consumed by a FROM must be declared in the global scope (before the +# first FROM). BUILD_JDK selects the build image; RUNTIME_BASE the runtime image. +ARG BUILD_JDK=8 +ARG RUNTIME_BASE=eclipse-temurin:8-jre + +# --------------------------------------------------------------------------- +# Stage 1: build the WAR and assemble a self-contained Tomcat with the WAR +# exploded and the JDBC drivers in place. BUILD_JDK must support the requested +# VERSION_JDK source level (JDK 8 -> 1.6/1.7/1.8; JDK 25 -> 8..25). +# --------------------------------------------------------------------------- +FROM maven:3.9-eclipse-temurin-${BUILD_JDK} AS build + +ARG VERSION_JDK=1.8 +ARG VERSION_WEBXML=30 +# The pinned legacy Maven plugins (war-plugin 2.4 …) do deep reflection that +# JDK 16+ blocks by default; the matrix sets MAVEN_OPTS to the needed +# --add-opens for build JDK >= 16 (empty on JDK 8/11). +ARG MAVEN_OPTS="" +ENV MAVEN_OPTS=${MAVEN_OPTS} +ARG TOMCAT_MAJOR=9 +ARG TOMCAT_VERSION=9.0.118 WORKDIR /build COPY pom.xml . COPY src ./src -RUN mvn install -Dversion.webxml=30 -DskipTests -q +RUN mvn install -Dversion.jdk=${VERSION_JDK} -Dversion.webxml=${VERSION_WEBXML} -DskipTests -q + +# Assemble Tomcat under /opt/tomcat: download (archive.apache.org keeps every +# patch; dlcdn only the latest), drop default webapps, explode the WAR with +# `jar`, and fetch the JDBC drivers into lib/. NOTE: ojdbc8 / mssql-jdbc.jre8 +# need Java 8+ and won't load on the Java 6/7 tail (logged, non-fatal); MySQL +# Connector/J 5.1.49 works on every level. +RUN set -eux; \ + curl -fsSL "https://archive.apache.org/dist/tomcat/tomcat-${TOMCAT_MAJOR}/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" -o /tmp/tomcat.tgz; \ + mkdir -p /opt/tomcat; \ + tar xzf /tmp/tomcat.tgz -C /opt/tomcat --strip-components=1; \ + rm /tmp/tomcat.tgz; \ + rm -rf /opt/tomcat/webapps/*; \ + mkdir -p /opt/tomcat/webapps/spiracle; \ + (cd /opt/tomcat/webapps/spiracle && jar xf /build/target/spiracle.war); \ + curl -fsSL "https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar" \ + -o /opt/tomcat/lib/mysql-connector-java-5.1.49.jar; \ + curl -fsSL "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.4.2.jre8/mssql-jdbc-12.4.2.jre8.jar" \ + -o /opt/tomcat/lib/mssql-jdbc-12.4.2.jre8.jar; \ + curl -fsSL "https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/21.13.0.0/ojdbc8-21.13.0.0.jar" \ + -o /opt/tomcat/lib/ojdbc8-21.13.0.0.jar # --------------------------------------------------------------------------- -# Stage 2: runtime — Tomcat 9 + JRE 8 +# Stage 2: runtime. Just a JRE + the assembled Tomcat — no package installs, +# so any JRE base works (Temurin 8..25, or a legacy Azul Zulu 6/7 for the tail). # --------------------------------------------------------------------------- -FROM tomcat:9-jre8-temurin AS runtime - -# Remove default webapps -RUN rm -rf "$CATALINA_HOME/webapps/ROOT" \ - "$CATALINA_HOME/webapps/docs" \ - "$CATALINA_HOME/webapps/examples" \ - "$CATALINA_HOME/webapps/host-manager" \ - "$CATALINA_HOME/webapps/manager" - -# Copy and pre-explode the WAR so the entrypoint can edit conf/ on disk -COPY --from=build /build/target/spiracle.war /tmp/spiracle.war -RUN apt-get update -qq && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/* \ - && mkdir -p "$CATALINA_HOME/webapps/spiracle" \ - && unzip -q /tmp/spiracle.war -d "$CATALINA_HOME/webapps/spiracle" \ - && rm /tmp/spiracle.war - -# ---- JDBC drivers (downloaded at image build time from Maven Central) ---- - -# MySQL Connector/J 5.1.49 — has com.mysql.jdbc.Driver (legacy classname) -RUN curl -fsSL \ - "https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar" \ - -o "$CATALINA_HOME/lib/mysql-connector-java-5.1.49.jar" - -# MSSQL JDBC — jre8 classifier -RUN curl -fsSL \ - "https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/12.4.2.jre8/mssql-jdbc-12.4.2.jre8.jar" \ - -o "$CATALINA_HOME/lib/mssql-jdbc-12.4.2.jre8.jar" - -# Oracle ojdbc8 — Java 8 compatible -RUN curl -fsSL \ - "https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/21.13.0.0/ojdbc8-21.13.0.0.jar" \ - -o "$CATALINA_HOME/lib/ojdbc8-21.13.0.0.jar" - -# ---- entrypoint ---- -COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh -RUN chmod +x /usr/local/bin/entrypoint.sh +FROM ${RUNTIME_BASE} AS runtime + +ENV CATALINA_HOME=/usr/local/tomcat +ENV PATH="${CATALINA_HOME}/bin:${PATH}" + +COPY --from=build /opt/tomcat ${CATALINA_HOME} +COPY --chmod=0755 docker/entrypoint.sh /usr/local/bin/entrypoint.sh EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/docker-compose.mssql.yml b/docker-compose.mssql.yml index a59bd62..edd7b74 100644 --- a/docker-compose.mssql.yml +++ b/docker-compose.mssql.yml @@ -40,6 +40,16 @@ services: build: context: . dockerfile: Dockerfile + args: + # Defaults reproduce the historical Java 8 image. Override via the + # environment (see scripts/rasp-matrix.sh) to build for Java 6..25. + BUILD_JDK: "${BUILD_JDK:-8}" + VERSION_JDK: "${VERSION_JDK:-1.8}" + VERSION_WEBXML: "${VERSION_WEBXML:-30}" + RUNTIME_BASE: "${RUNTIME_BASE:-eclipse-temurin:8-jre}" + TOMCAT_MAJOR: "${TOMCAT_MAJOR:-9}" + TOMCAT_VERSION: "${TOMCAT_VERSION:-9.0.118}" + MAVEN_OPTS: "${MAVEN_OPTS:-}" ports: - "8080:8080" environment: diff --git a/docker-compose.mysql.yml b/docker-compose.mysql.yml index e3b259a..4d3c502 100644 --- a/docker-compose.mysql.yml +++ b/docker-compose.mysql.yml @@ -23,6 +23,16 @@ services: build: context: . dockerfile: Dockerfile + args: + # Defaults reproduce the historical Java 8 image. Override via the + # environment (see scripts/rasp-matrix.sh) to build for Java 6..25. + BUILD_JDK: "${BUILD_JDK:-8}" + VERSION_JDK: "${VERSION_JDK:-1.8}" + VERSION_WEBXML: "${VERSION_WEBXML:-30}" + RUNTIME_BASE: "${RUNTIME_BASE:-eclipse-temurin:8-jre}" + TOMCAT_MAJOR: "${TOMCAT_MAJOR:-9}" + TOMCAT_VERSION: "${TOMCAT_VERSION:-9.0.118}" + MAVEN_OPTS: "${MAVEN_OPTS:-}" ports: - "8080:8080" environment: diff --git a/docker-compose.oracle.yml b/docker-compose.oracle.yml index a845e43..761025e 100644 --- a/docker-compose.oracle.yml +++ b/docker-compose.oracle.yml @@ -18,6 +18,16 @@ services: build: context: . dockerfile: Dockerfile + args: + # Defaults reproduce the historical Java 8 image. Override via the + # environment (see scripts/rasp-matrix.sh) to build for Java 6..25. + BUILD_JDK: "${BUILD_JDK:-8}" + VERSION_JDK: "${VERSION_JDK:-1.8}" + VERSION_WEBXML: "${VERSION_WEBXML:-30}" + RUNTIME_BASE: "${RUNTIME_BASE:-eclipse-temurin:8-jre}" + TOMCAT_MAJOR: "${TOMCAT_MAJOR:-9}" + TOMCAT_VERSION: "${TOMCAT_VERSION:-9.0.118}" + MAVEN_OPTS: "${MAVEN_OPTS:-}" ports: - "8080:8080" environment: diff --git a/docker-compose.rasp-agent.yml b/docker-compose.rasp-agent.yml new file mode 100644 index 0000000..4ee5b51 --- /dev/null +++ b/docker-compose.rasp-agent.yml @@ -0,0 +1,23 @@ +# ============================================================================= +# Overlay compose that attaches the Waratek RASP agent to the app container. +# +# The agent binary is NOT shipped in this repo. Point AGENT_DIR at a prepared +# agent directory (waratek.jar, core.jar, compiler-8.jar, a standalone +# waratek.properties and a SQL-injection rules.armr). scripts/rasp-matrix.sh +# prepares that directory for you (`setup-agent`). +# +# Usage (layered on top of a database compose file): +# +# AGENT_DIR=/abs/path/to/agent docker compose \ +# -f docker-compose.mysql.yml -f docker-compose.rasp-agent.yml up -d +# +# The agent supports Java 6..25, so this overlay is version-independent — it +# works against any image the parameterised Dockerfile produces. +# ============================================================================= +services: + app: + environment: + # catalina.sh honours CATALINA_OPTS; this attaches the agent at JVM start. + CATALINA_OPTS: "-javaagent:/opt/waratek/waratek.jar -Dcom.waratek.WaratekProperties=/opt/waratek/waratek.properties" + volumes: + - "${AGENT_DIR:?set AGENT_DIR to a prepared Waratek agent directory}:/opt/waratek:rw" diff --git a/docs/multi-version-testing.md b/docs/multi-version-testing.md new file mode 100644 index 0000000..877d34a --- /dev/null +++ b/docs/multi-version-testing.md @@ -0,0 +1,174 @@ +# Multi-version testing (Java 6 – 25) from one Dockerfile + +The Waratek RASP agent supports **Java 6 through 25**. To prove the +SQL-injection matrix (`tests/hurl/rasp`, 440 cases) blocks on every one of those +runtimes, Spiracle builds and runs under any Java version **without a Dockerfile +per version**. A single parameterised `Dockerfile` is driven by five build args; +a small matrix in `scripts/rasp-matrix.sh` maps a Java version onto those args. + +``` +scripts/rasp-matrix.sh ── matrix: Java 6..25 → build args + │ + ├─ docker-compose..yml (build.args from env) + ├─ docker-compose.rasp-agent.yml (attaches the Waratek agent) + └─ Dockerfile (ARG-parameterised, one file) +``` + +## The two axes + +"Run on Java N" is ambiguous — two things vary independently, and the matrix +sets both: + +| Axis | Build arg(s) | What it controls | +|---|---|---| +| **Runtime JRE** | `RUNTIME_BASE` | the JVM the agent actually instruments — the real "run on Java N" | +| **Source/target level** | `VERSION_JDK` + `BUILD_JDK` | the bytecode level the WAR is compiled to | + +`VERSION_JDK` is fed to Maven as `-Dversion.jdk` (the pom's +`maven.compiler.source`/`target`). Its spelling flips at Java 9: `1.6`, `1.7`, +`1.8`, then bare `9` … `25`. + +## The build args + +| Arg | Default | Meaning | +|---|---|---| +| `BUILD_JDK` | `8` | JDK image used to **compile** (`maven:3.9-eclipse-temurin-${BUILD_JDK}`) | +| `VERSION_JDK` | `1.8` | `-Dversion.jdk` source/target level | +| `VERSION_WEBXML` | `30` | Servlet descriptor (25 or 30) | +| `RUNTIME_BASE` | `eclipse-temurin:8-jre` | JRE image the app runs on | +| `TOMCAT_MAJOR` | `9` | Tomcat major line | +| `TOMCAT_VERSION` | `9.0.118` | Tomcat patch (installed from `archive.apache.org`) | + +The defaults reproduce the historical Java 8 / Tomcat 9 image, so a plain +`docker compose build` is unchanged. + +### Why the runtime is "JRE base + assembled Tomcat" + +Docker Hub only publishes `tomcat:9-jreN` tags for **LTS** JREs (8, 11, 17, 21). +Instead, the **build stage** downloads Tomcat from the Apache archive +(`archive.apache.org`, which keeps every patch — `dlcdn` keeps only the latest), +explodes the WAR into it with `jar`, and drops in the JDBC drivers. The runtime +stage then just `COPY`s that self-contained Tomcat onto a plain JRE image and +installs **nothing** — no apt/curl/unzip. This makes the runtime uniform for +every Java the base ships (Temurin 8–25) *and* works on legacy bases whose +package repos are dead (the Azul Zulu 6/7 images used for the tail). + +## The version matrix + +`scripts/rasp-matrix.sh matrix ` resolves a version. Bands: + +| Java | `VERSION_JDK` | `BUILD_JDK` | `RUNTIME_BASE` | Tomcat | +|---|---|---|---|---| +| 6 | `1.6` | 8 | `azul/zulu-openjdk:6` | 7.0.x | +| 7 | `1.7` | 8 | `azul/zulu-openjdk:7` | 8.5.x | +| 8 | `1.8` | 8 | `eclipse-temurin:8-jre` | 9.0.x | +| 9–10 | `9`/`10` | 11 | `eclipse-temurin:11-jre` | 9.0.x | +| 11 | `11` | 11 | `eclipse-temurin:11-jre` | 9.0.x | +| 12–16 | `12`…`16` | 17 | `eclipse-temurin:17-jre` | 9.0.x | +| 17 | `17` | 17 | `eclipse-temurin:17-jre` | 9.0.x | +| 18–20 | `18`…`20` | 21 | `eclipse-temurin:21-jre` | 9.0.x | +| 21 | `21` | 21 | `eclipse-temurin:21-jre` | 9.0.x | +| 22–24 | `22`…`24` | 25 | `eclipse-temurin:${N}-jre` | 9.0.x | +| 25 | `25` | 25 | `eclipse-temurin:25-jre` | 9.0.x | + +**Why a `BUILD_JDK` band and not one JDK?** `javac` drops old `-source/-target` +support over time (target 1.6 needs JDK ≤ 11; target 25 needs JDK 25). Five LTS +build JDKs cover the whole range. The non-LTS rows compile at their exact level +but **run** on the nearest LTS JRE that hosts that bytecode (e.g. Java 13 → built +with JDK 17, runs on `temurin:17-jre`) because Temurin has no `13-jre` image. Set +`RUNTIME_BASE` yourself (BellSoft Liberica, SapMachine, …) if you need the exact +non-LTS JRE. + +## Caveats / edges + +- **Java 6 & 7 (the tail)** — Tomcat 9 and Temurin both floor at Java 8, so the + tail runs **Tomcat 7/8 on an Azul Zulu JRE 6/7 base** (Azul still publishes + these; Temurin/Docker-official do not). `ojdbc8`/`mssql-jdbc.jre8` need Java 8+ + and won't load there, so **only MySQL works on 6/7**. **Both 6 and 7 are + verified end-to-end** — build + agent attach + rasp/mysql 139/139 → 550 — after + three fixes that the tail surfaced: + 1. **Build** — `zulu:6`'s base OS has dead apt repos, so installing packages + in the runtime stage failed. The Dockerfile now assembles Tomcat entirely + in the **build stage** and `COPY`s it in; the runtime stage installs + nothing, so any JRE base works. + 2. **Deploy** — `SpiracleInit` eagerly `Class.forName`s every JDBC driver but + caught only `ClassNotFoundException`. The Java-8 drivers throw + `UnsupportedClassVersionError` (a `LinkageError`) on JRE 6/7, which made + Tomcat 8.5 fail the context ("One or more listeners failed to start"; + Tomcat 9 tolerated it). Broadened to `catch (Throwable)`. + 3. **Connect** — JRE 6 speaks only TLS 1.0, but `mysql:8.0` requires TLS 1.2+, + so the SSL handshake died (`SSLException: protocol_version`). The mysql URL + now sets `useSSL=false` (fine for the local throwaway DB; harmless on 8–25). + The agent attaches on JRE 6/7 because `compiler-8.jar` covers the 6–8 tier + (the bundle ships `compiler-{8,17,21,25}.jar`). +- **Non-LTS old versions (9, 10, 12–16, 18–20)** have no Temurin JRE image; the + matrix runs them on the nearest LTS JRE. Override `RUNTIME_BASE` for an exact + JRE. +- **`tomcat:9-jre25`** may not be published; the tarball-install path sidesteps + this — only `eclipse-temurin:25-jre` must exist (it does). +- **rasp suite DB coverage** — `tests/hurl/rasp/` ships matrices for **MySQL + (139) and Oracle (301)** only; there is no MSSQL rasp suite. The agent rule is + `vendor(any)`, so MSSQL injection would be blocked too, but the MSSQL Docker + stack (`docker-compose.mssql.yml`) currently fails to seed in this environment + (`db-init`: SA login fails → `spiracle` DB not created) — a pre-existing stack + issue independent of this work. + +## The agent + +The RASP suite needs the Waratek agent attached. The agent binary is **not** in +this repo. `scripts/rasp-matrix.sh setup-agent` copies a prepared agent +(`AGENT_SRC`, default `../agent-test`) to `AGENT_DIR` (default `../agent-run`) +and writes: + +- a **standalone** `waratek.properties` (`com.waratek.ControllerPresent=false`) + so the agent runs headless with local rules — no Portal/controller needed; +- a **SQL-injection PROTECT** `rules.armr`: + + ``` + sql("Block SQL injection from HTTP, any vendor"): + vendor(any) + input(http) + injection(successful-attempt, failed-attempt) + protect(message: "...", severity: Very-High) + endsql + ``` + + The block message is hardcoded in the agent and surfaced as a + `java.sql.SQLException` only on a `protect` action; Spiracle's + `SelectUtil.verifySQLException` maps that exact message to **HTTP 550**, which + the rasp suite asserts. + +`docker-compose.rasp-agent.yml` mounts `AGENT_DIR` at `/opt/waratek` and sets +`CATALINA_OPTS=-javaagent:/opt/waratek/waratek.jar …`. + +## Usage + +```sh +# Inspect the resolved args for a version +./scripts/rasp-matrix.sh matrix 17 + +# Build only +./scripts/rasp-matrix.sh build 21 mysql + +# Build + attach agent + run the rasp suite + tear down +./scripts/rasp-matrix.sh test 17 mysql +./scripts/rasp-matrix.sh test 21 oracle + +# Sweep several versions and print a PASS/FAIL summary +./scripts/rasp-matrix.sh sweep "8 11 17 21 25" mysql + +# No agent: smoke + functional on a plain deployment +./scripts/rasp-matrix.sh plain 17 mysql +``` + +Environment overrides: `AGENT_SRC`, `AGENT_DIR`, `HURL_LIBS` (dir holding +`libxml2.so.2` etc. for the `hurl` binary), `KEEP_UP=1` (leave the stack up). + +## Files + +| File | Role | +|---|---| +| `Dockerfile` | one parameterised build (5 ARGs) | +| `docker-compose.{mysql,oracle,mssql}.yml` | `build.args` wired to env vars | +| `docker-compose.rasp-agent.yml` | overlay that attaches the agent | +| `scripts/rasp-matrix.sh` | matrix + build/test/sweep driver | diff --git a/scripts/rasp-matrix.sh b/scripts/rasp-matrix.sh new file mode 100755 index 0000000..5360767 --- /dev/null +++ b/scripts/rasp-matrix.sh @@ -0,0 +1,258 @@ +#!/usr/bin/env bash +# ============================================================================= +# rasp-matrix.sh — build & RASP-test Spiracle across Java 6..25 from ONE +# Dockerfile, by resolving each Java version to a set of Docker build args. +# +# The Waratek RASP agent supports Java 6 through 25; this script lets you run +# the 440-case SQL-injection matrix (tests/hurl/rasp) against the app on any +# of those runtimes without maintaining a Dockerfile per version. +# +# COMMANDS +# matrix Print the resolved build args for a Java version. +# setup-agent Prepare the agent dir (copy + standalone config). +# build [db] Build the image for (db: mysql|oracle|mssql). +# test [db] Build, start db+app+agent, run the rasp suite, tear down. +# sweep "" [db] test each Java version in turn; print a summary. +# plain [db] Like test but NO agent (runs smoke+functional instead). +# +# ENV OVERRIDES +# AGENT_SRC source agent dir to copy (default: ../agent-test) +# AGENT_DIR prepared agent dir (mounted) (default: ../agent-run) +# HURL_LIBS dir with libxml2.so.2 etc. (default: ~/.local/hurl-libs) +# KEEP_UP=1 do not tear the stack down after test +# +# EXAMPLES +# ./scripts/rasp-matrix.sh test 17 mysql +# ./scripts/rasp-matrix.sh sweep "8 11 17 21" mysql +# ./scripts/rasp-matrix.sh sweep "8 11 17 21" oracle +# +# See docs/multi-version-testing.md for the design and the matrix rationale. +# ============================================================================= +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO" + +AGENT_SRC="${AGENT_SRC:-$REPO/../agent-test}" +AGENT_DIR="${AGENT_DIR:-$REPO/../agent-run}" +HURL_LIBS="${HURL_LIBS:-$HOME/.local/hurl-libs}" + +# --------------------------------------------------------------------------- +# Version matrix: Java level -> (BUILD_JDK, VERSION_JDK, RUNTIME_BASE, +# TOMCAT_MAJOR, TOMCAT_VERSION). Emitted as KEY=VALUE lines. +# +# * BUILD_JDK bands: one JDK cannot target every source level, so pick the +# oldest LTS JDK that still supports the target (8 -> 6/7/8, 11 -> 9/10/11, +# 17 -> 12..17, 21 -> 18..21, 25 -> 22..25). +# * VERSION_JDK: 6/7/8 use the legacy 1.x spelling; 9+ are bare numbers. +# * RUNTIME_BASE: a plain JRE image; Tomcat 9 is installed from the archive. +# Eclipse Temurin publishes JRE images for 8/11/17/21 and recent (22..25). +# Non-LTS old versions (9,10,12..16,18..20) have no Temurin JRE image — they +# are marked best-effort and will need a different vendor base (set +# RUNTIME_BASE yourself, e.g. a BellSoft Liberica or SapMachine image). +# * Java 6/7: Tomcat 9 needs Java 8+, so the tail drops to Tomcat 7/8 on a +# legacy JRE 6/7 image you must supply (RUNTIME_BASE), and ojdbc8/mssql-jdbc +# (Java 8+) won't load — only MySQL works there. +# --------------------------------------------------------------------------- +matrix() { + local j="$1" build vjdk base tmaj tver note="" + case "$j" in + 6) build=8; vjdk=1.6; base="azul/zulu-openjdk:6"; tmaj=7; tver=7.0.109; note="tail: Azul Zulu JRE6 + Tomcat7; MySQL only (ojdbc8/mssql-jdbc need Java 8+)";; + 7) build=8; vjdk=1.7; base="azul/zulu-openjdk:7"; tmaj=8; tver=8.5.100; note="tail: Azul Zulu JRE7 + Tomcat8.5; MySQL only (ojdbc8/mssql-jdbc need Java 8+)";; + 8) build=8; vjdk=1.8; base="eclipse-temurin:8-jre"; tmaj=9; tver=9.0.118;; + 9) build=11; vjdk=9; base="eclipse-temurin:11-jre"; tmaj=9; tver=9.0.118; note="non-LTS: runs on JRE11 (no temurin:9-jre)";; + 10) build=11; vjdk=10; base="eclipse-temurin:11-jre"; tmaj=9; tver=9.0.118; note="non-LTS: runs on JRE11";; + 11) build=11; vjdk=11; base="eclipse-temurin:11-jre"; tmaj=9; tver=9.0.118;; + 12|13|14|15|16) build=17; vjdk="$j"; base="eclipse-temurin:17-jre"; tmaj=9; tver=9.0.118; note="non-LTS: runs on JRE17";; + 17) build=17; vjdk=17; base="eclipse-temurin:17-jre"; tmaj=9; tver=9.0.118;; + 18|19|20) build=21; vjdk="$j"; base="eclipse-temurin:21-jre"; tmaj=9; tver=9.0.118; note="non-LTS: runs on JRE21";; + 21) build=21; vjdk=21; base="eclipse-temurin:21-jre"; tmaj=9; tver=9.0.118;; + 22|23|24) build=25; vjdk="$j"; base="eclipse-temurin:${j}-jre"; tmaj=9; tver=9.0.118; note="non-LTS: temurin:${j}-jre may be unavailable";; + 25) build=25; vjdk=25; base="eclipse-temurin:25-jre"; tmaj=9; tver=9.0.118;; + *) echo "ERROR: unsupported Java version '$j' (expected 6..25)" >&2; return 1;; + esac + # JDK 16+ strongly encapsulates the JDK internals the legacy Maven plugins + # reflect into; open them so the build runs. Harmless to omit on 8/11. + local mopts="" + if [ "$build" -ge 16 ] 2>/dev/null; then + mopts="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED" + fi + cat <&2 || true +} + +# --------------------------------------------------------------------------- +# Agent preparation: copy the pristine agent, write a standalone (no-controller) +# waratek.properties and a SQL-injection PROTECT rule that makes blocked queries +# return HTTP 550 (the message is hardcoded in the agent; `protect` triggers it). +# --------------------------------------------------------------------------- +setup_agent() { + [ -f "$AGENT_SRC/waratek.jar" ] || { echo "ERROR: no waratek.jar under AGENT_SRC=$AGENT_SRC" >&2; return 1; } + echo "Preparing agent: $AGENT_SRC -> $AGENT_DIR" + rm -rf "$AGENT_DIR"; mkdir -p "$AGENT_DIR" + cp -a "$AGENT_SRC/." "$AGENT_DIR/" + rm -f "$AGENT_DIR/instance.waratek.properties.lock" "$AGENT_DIR/rules.log" "$AGENT_DIR/events.log" + + cat > "$AGENT_DIR/waratek.properties" <<'PROPS' +# Standalone (headless) — no controller, local ARMR rules only. +com.waratek.ControllerPresent=false +com.waratek.ControllerUnavailableAction=ignore +com.waratek.rules.local=/opt/waratek/rules.armr +com.waratek.log.file=/opt/waratek/events.log +com.waratek.rules.autoreload=true +com.waratek.ShowStart=true +PROPS + + cat > "$AGENT_DIR/rules.armr" <<'ARMR' +app("Spiracle SQLi protect"): + requires(version: ARMR/2.12) + sql("Block SQL injection from HTTP, any vendor"): + vendor(any) + input(http) + injection(successful-attempt, failed-attempt) + protect(message: "SQL injection blocked", severity: Very-High) + endsql +endapp +ARMR + echo "Agent ready at $AGENT_DIR (ControllerPresent=false, SQLi protect rule installed)." +} + +hurl() { LD_LIBRARY_PATH="$HURL_LIBS" command hurl "$@"; } + +compose_files() { # echoes -f args for a db, optionally + agent overlay + local db="$1" agent="${2:-}" + printf -- '-f %s ' "$REPO/docker-compose.${db}.yml" + [ "$agent" = agent ] && printf -- '-f %s ' "$REPO/docker-compose.rasp-agent.yml" +} + +wait_app() { # wait for app root 200 (default 120s) + local i=0 max="${1:-40}" + until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/spiracle/ 2>/dev/null)" = 200 ] || [ $i -ge "$max" ]; do + i=$((i+1)); sleep 3 + done + [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/spiracle/ 2>/dev/null)" = 200 ] +} + +build() { # build [db] + local j="$1" db="${2:-mysql}" + load_matrix "$j" + echo "==> build Java $j (BUILD_JDK=$BUILD_JDK VERSION_JDK=$VERSION_JDK RUNTIME_BASE=$RUNTIME_BASE Tomcat $TOMCAT_VERSION)" + docker compose $(compose_files "$db") build app +} + +test_version() { # test [db] — with agent + rasp suite + local j="$1" db="${2:-mysql}" + local proj="rasp${j}_$$" + load_matrix "$j" + setup_agent + echo "==> Java $j / $db : build + up (agent attached)" + AGENT_DIR="$AGENT_DIR" docker compose -p "$proj" $(compose_files "$db" agent) up -d --build + local rc=0 + if wait_app; then + echo "==> app ready; running rasp/$db" + if hurl --test --variables-file "$REPO/tests/hurl/rasp/protected.env" \ + --variable host=localhost --variable port=8080 --variable block_status=550 \ + "$REPO"/tests/hurl/rasp/${db}/*.hurl; then + echo "RESULT Java $j ($db): PASS" + else + echo "RESULT Java $j ($db): FAIL (rasp assertions)"; rc=1 + fi + else + echo "RESULT Java $j ($db): FAIL (app did not become ready)"; rc=1 + AGENT_DIR="$AGENT_DIR" docker compose -p "$proj" $(compose_files "$db" agent) logs app | tail -50 || true + fi + if [ "${KEEP_UP:-0}" = 1 ]; then + echo "KEEP_UP=1 — leaving stack '$proj' running" + else + AGENT_DIR="$AGENT_DIR" docker compose -p "$proj" $(compose_files "$db" agent) down -v >/dev/null 2>&1 || true + fi + return $rc +} + +plain_version() { # plain [db] — no agent; smoke+functional + local j="$1" db="${2:-mysql}" + local proj="plain${j}_$$" + load_matrix "$j" + echo "==> Java $j / $db : build + up (no agent)" + docker compose -p "$proj" $(compose_files "$db") up -d --build + local rc=0 + if wait_app; then + hurl --test --variables-file "$REPO/tests/hurl/smoke/local.env" \ + --variable host=localhost --variable port=8080 "$REPO"/tests/hurl/smoke/*.hurl \ + && hurl --test --variables-file "$REPO/tests/hurl/functional/local.env" \ + --variable host=localhost --variable port=8080 "$REPO"/tests/hurl/functional/*.hurl \ + && echo "RESULT Java $j ($db, plain): PASS" || { echo "RESULT Java $j ($db, plain): FAIL"; rc=1; } + else + echo "RESULT Java $j ($db, plain): FAIL (app not ready)"; rc=1 + fi + [ "${KEEP_UP:-0}" = 1 ] || docker compose -p "$proj" $(compose_files "$db") down -v >/dev/null 2>&1 || true + return $rc +} + +sweep() { # sweep "" [db] + local versions="$1" db="${2:-mysql}" v results="" + for v in $versions; do + if test_version "$v" "$db"; then results="$results\nJava $v ($db): PASS" + else results="$results\nJava $v ($db): FAIL"; fi + done + echo "==================== SWEEP SUMMARY ($db) ====================" + echo -e "$results" +} + +# Full matrix: smoke+functional + rasp/mysql on every distinct runtime band, +# plus rasp/oracle and rasp/mssql on Java 8+ (those drivers need Java 8+). The +# bands {6,7,8,11,17,21,25} cover every distinct RUNTIME_BASE/BUILD_JDK — the +# non-LTS versions in between share a neighbour's JRE. +full() { + local mysql_versions="${1:-6 7 8 11 17 21 25}" + local java8plus="${2:-8 11 17 21 25}" + local results="" v + for v in $mysql_versions; do + if plain_version "$v" mysql; then results="$results\nsmoke+functional Java $v : PASS" + else results="$results\nsmoke+functional Java $v : FAIL"; fi + done + for v in $mysql_versions; do + if test_version "$v" mysql; then results="$results\nrasp mysql Java $v : PASS" + else results="$results\nrasp mysql Java $v : FAIL"; fi + done + for v in $java8plus; do + if test_version "$v" oracle; then results="$results\nrasp oracle Java $v : PASS" + else results="$results\nrasp oracle Java $v : FAIL"; fi + done + for v in $java8plus; do + if test_version "$v" mssql; then results="$results\nrasp mssql Java $v : PASS" + else results="$results\nrasp mssql Java $v : FAIL"; fi + done + echo "==================== FULL MATRIX SUMMARY ====================" + echo -e "$results" +} + +usage() { sed -n '2,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; } + +cmd="${1:-}"; shift || true +case "$cmd" in + matrix) matrix "${1:?Java version}";; + setup-agent) setup_agent;; + build) build "${1:?Java version}" "${2:-mysql}";; + test) test_version "${1:?Java version}" "${2:-mysql}";; + plain) plain_version "${1:?Java version}" "${2:-mysql}";; + sweep) sweep "${1:?versions, e.g. \"8 11 17 21\"}" "${2:-mysql}";; + full) full "${1:-}" "${2:-}";; + ""|-h|--help) usage;; + *) echo "unknown command '$cmd'" >&2; usage; exit 2;; +esac From a3f3a7914aa3d4bbc42fd1d1025768188aaaf2ab Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Tue, 9 Jun 2026 11:30:01 +0100 Subject: [PATCH 12/13] fix: let the webapp deploy and connect on the Java 6/7 tail - SpiracleInit/CreateJndiConnectionPool: broaden catch to Throwable so a JDBC driver that cannot load on JRE 6/7 (UnsupportedClassVersionError) is logged instead of failing context startup on Tomcat 8.5 (Tomcat 9 tolerated it) - Spiracle.properties: useSSL=false on the local mysql URL (JRE 6 has no TLS 1.2, which mysql:8 requires) --- src/main/java/com/waratek/spiracle/init/SpiracleInit.java | 5 ++--- .../waratek/spiracle/sql/jndi/CreateJndiConnectionPool.java | 5 ++--- src/main/webapp/conf/Spiracle.properties | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/waratek/spiracle/init/SpiracleInit.java b/src/main/java/com/waratek/spiracle/init/SpiracleInit.java index 6c44d64..fd178d5 100644 --- a/src/main/java/com/waratek/spiracle/init/SpiracleInit.java +++ b/src/main/java/com/waratek/spiracle/init/SpiracleInit.java @@ -84,9 +84,8 @@ public void contextInitialized(ServletContextEvent arg0) { Class.forName(props.getProperty(Constants.C3P0_DB2_CLASSNAME)); Class.forName(props.getProperty(Constants.C3P0_SYBASE_CLASSNAME)); Class.forName(props.getProperty(Constants.C3P0_POSTGRES_CLASSNAME)); - } catch (ClassNotFoundException e) { - logger.error("Unable to load JDBC connector classes from config."); - e.printStackTrace(); + } catch (Throwable e) { + logger.error("Unable to load one or more JDBC connector classes from config: " + e); } } diff --git a/src/main/java/com/waratek/spiracle/sql/jndi/CreateJndiConnectionPool.java b/src/main/java/com/waratek/spiracle/sql/jndi/CreateJndiConnectionPool.java index fcf2cdd..63c64b9 100644 --- a/src/main/java/com/waratek/spiracle/sql/jndi/CreateJndiConnectionPool.java +++ b/src/main/java/com/waratek/spiracle/sql/jndi/CreateJndiConnectionPool.java @@ -26,9 +26,8 @@ public void contextInitialized(ServletContextEvent arg0) { ServletContext application = arg0.getServletContext(); application.setAttribute("jndiConnectionPool", ds); logger.info("Added jndi connection pool " + ds + " to application context."); - } catch (NamingException e) { - logger.error("JNDI reference not found."); - e.printStackTrace(); + } catch (Throwable e) { + logger.error("Optional JNDI resource jdbc/oracle not bound; continuing without it: " + e); } } } diff --git a/src/main/webapp/conf/Spiracle.properties b/src/main/webapp/conf/Spiracle.properties index 58cec1d..414546f 100644 --- a/src/main/webapp/conf/Spiracle.properties +++ b/src/main/webapp/conf/Spiracle.properties @@ -9,7 +9,7 @@ c3p0.oracle.password=test c3p0.oracle.maxPoolSize=50 c3p0.mysql.classname=com.mysql.jdbc.Driver -c3p0.mysql.url=jdbc:mysql://localhost:3306/test +c3p0.mysql.url=jdbc:mysql://localhost:3306/test?useSSL=false c3p0.mysql.username=test c3p0.mysql.password=test c3p0.mysql.maxPoolSize=50 From 560cd5e98e925d541c6a9aaa9a94b352c109bcfb Mon Sep 17 00:00:00 2001 From: Andre Faria Date: Tue, 9 Jun 2026 14:16:11 +0100 Subject: [PATCH 13/13] test: add MSSQL RASP suite and fix the MSSQL seed - tests/hurl/rasp/mssql/ (139 cases against MsSql_* endpoints); add to run.sh - setupdb_mssql.sql: add GO batch separators (CREATE DATABASE must be its own batch before USE) and idempotent DROP TABLE IF EXISTS --- src/main/webapp/conf/setupdb_mssql.sql | 10 +- tests/hurl/rasp/mssql/get_implicit_join.hurl | 179 ++++++++++++++++++ tests/hurl/rasp/mssql/get_int.hurl | 179 ++++++++++++++++++ tests/hurl/rasp/mssql/get_string.hurl | 119 ++++++++++++ tests/hurl/rasp/mssql/get_union.hurl | 173 +++++++++++++++++ .../rasp/mssql/implicit_join_namespace.hurl | 179 ++++++++++++++++++ tests/hurl/run.sh | 2 +- 7 files changed, 837 insertions(+), 4 deletions(-) create mode 100644 tests/hurl/rasp/mssql/get_implicit_join.hurl create mode 100644 tests/hurl/rasp/mssql/get_int.hurl create mode 100644 tests/hurl/rasp/mssql/get_string.hurl create mode 100644 tests/hurl/rasp/mssql/get_union.hurl create mode 100644 tests/hurl/rasp/mssql/implicit_join_namespace.hurl diff --git a/src/main/webapp/conf/setupdb_mssql.sql b/src/main/webapp/conf/setupdb_mssql.sql index 14af500..409ec45 100644 --- a/src/main/webapp/conf/setupdb_mssql.sql +++ b/src/main/webapp/conf/setupdb_mssql.sql @@ -1,10 +1,13 @@ create database spiracle; +GO use spiracle; +GO -DROP TABLE users; -DROP TABLE address; -DROP TABLE TEXT_STORE; +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS address; +DROP TABLE IF EXISTS TEXT_STORE; +GO CREATE TABLE users ( id int, @@ -27,6 +30,7 @@ CREATE TABLE TEXT_STORE ID int, DATA text ); +GO INSERT INTO users (id, name, surname, dob, credit_card, cvv) VALUES (1, 'Patrick', 'Moss', '1955-08-29', '5566 0717 3093 3773', 341); INSERT INTO users (id, name, surname, dob, credit_card, cvv) VALUES (2, 'Margaret', 'Thomas', '1959-01-15', '3461 7946 3370 838', 475); diff --git a/tests/hurl/rasp/mssql/get_implicit_join.hurl b/tests/hurl/rasp/mssql/get_implicit_join.hurl new file mode 100644 index 0000000..dab3d34 --- /dev/null +++ b/tests/hurl/rasp/mssql/get_implicit_join.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20or%20users.id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20or%20'users.name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=''%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=''%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_Implicit_Join?id=%27%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mssql/get_int.hurl b/tests/hurl/rasp/mssql/get_int.hurl new file mode 100644 index 0000000..59f2033 --- /dev/null +++ b/tests/hurl/rasp/mssql/get_int.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=2'%20OR%20name%20LIKE%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20%7C%7C%20name%20like%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20or%20users.name%20LIKE%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20or%20id%20like%20'%25 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20OR%20100=(SELECT%20COUNT(*)%20FROM%20users)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20OR%20users.cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20AND%20DOB%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20OR%20cvv%20IS%20NULL%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20OR%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20or%201=1%20or%20'x'='y'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=a'%20or%20'a'%20=%20'a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=joe'%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20UNION%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=%27%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id='%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=1'%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_int?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mssql/get_string.hurl b/tests/hurl/rasp/mssql/get_string.hurl new file mode 100644 index 0000000..f6ce144 --- /dev/null +++ b/tests/hurl/rasp/mssql/get_string.hurl @@ -0,0 +1,119 @@ +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=x'%20or%20name%20like%20'%25'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=%3C%3E%22%2527%25%3B)(%26%2B + +HTTP * +[Asserts] +status == 200 + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=x'%20AND%20100=(SELECT%20COUNT(*)%20FROM%20address)%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=x'%20OR%20cvv%20IS%20NULL-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=anything'%20OR%20'x'='x + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=x'%20or%201=1%20or%20'x'='y + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='%20UNION%20ALL%20SELECT%20*%20from%20users%20where%20id=1%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='or%201=1%20or%20''=' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=a'%20or%20'a'%20=%20'a + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='%20or%20'1'='1'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='%20UNION%20SELECT%20*%20from%20users-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=%27%20or%20%27x%27=%27x + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='%20or%201%20--' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name='%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Get_string?name=x'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mssql/get_union.hurl b/tests/hurl/rasp/mssql/get_union.hurl new file mode 100644 index 0000000..9b88ada --- /dev/null +++ b/tests/hurl/rasp/mssql/get_union.hurl @@ -0,0 +1,173 @@ +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%20'name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='(%22hi%22)'%20or%20('a'='a')-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id=%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/Get_Union?id='%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/rasp/mssql/implicit_join_namespace.hurl b/tests/hurl/rasp/mssql/implicit_join_namespace.hurl new file mode 100644 index 0000000..9caadd3 --- /dev/null +++ b/tests/hurl/rasp/mssql/implicit_join_namespace.hurl @@ -0,0 +1,179 @@ +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='2'%20OR%20'name'%20LIKE%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20or%20'name'%20like%20'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20or%20users.id%20like%201 + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20or%20'users.name'%20like'Joe' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20OR%20120=(SELECT%20COUNT(*)%20FROM%20users) + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20AND%20'users.cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20AND%20'DOB'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20AND%20'cvv'%20IS%20NULL + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20OR%20'x'='x' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20or%201=1%20or%20'x'='y' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='1'%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=''%20or%201=1%20or%20''=''-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=('hi')%20or%20('a'='a') + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='a'%20or%20'a'%20=%20'a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='joe'%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='hi'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='hi'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='a'%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=''%20or%20'x'='x'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=''%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='a'%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='Joe'%20or%200=0%20-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id='Joe'%20or%20'a'='a'-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=1%20or%20'a'='a' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=1%20or%20'1'='1' + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=''%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=''%20%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=''%20or%203=3-- + +HTTP * +[Asserts] +status == {{block_status}} + +GET http://{{host}}:{{port}}/spiracle/MsSql_Implicit_Join_Namespace?id=%27%27%20or%201=1-- + +HTTP * +[Asserts] +status == {{block_status}} diff --git a/tests/hurl/run.sh b/tests/hurl/run.sh index 19b8aab..5c01368 100755 --- a/tests/hurl/run.sh +++ b/tests/hurl/run.sh @@ -51,7 +51,7 @@ case "$SUITE" in ;; rasp) VARS_FILE="$SCRIPT_DIR/rasp/protected.env" - FILES="$SCRIPT_DIR/rasp/mysql/*.hurl $SCRIPT_DIR/rasp/oracle/*.hurl" + FILES="$SCRIPT_DIR/rasp/mysql/*.hurl $SCRIPT_DIR/rasp/oracle/*.hurl $SCRIPT_DIR/rasp/mssql/*.hurl" REPORT_DIR="${REPORT_DIR:-/tmp/spiracle-rasp-report}" ;; *)