Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/business/cybersec/red_mesh/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def resolve_target_response_timeout(timeout_profile, standard_timeout):
# The backend release, recorded in every JobConfig at launch so a report names
# the scanner that produced it. `pentester_api_01.__VER__` must equal it (a test
# guards the pair; the plugin loader reads the literal there).
REDMESH_BACKEND_VERSION = "0.13.0"
REDMESH_BACKEND_VERSION = "0.13.1"

# =====================================================================
# Risk score computation
Expand Down
2 changes: 1 addition & 1 deletion extensions/business/cybersec/red_mesh/pentester_api_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ def _run_manual_analysis_worker(work: _ManualAnalysisWork) -> _ManualAnalysisOut
return _ManualAnalysisWorker(work).run()


__VER__ = '0.13.0'
__VER__ = '0.13.1'


_CONFIG = {
Expand Down
4 changes: 2 additions & 2 deletions extensions/business/cybersec/red_mesh/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,9 @@ def test_launch_records_the_backend_release_that_ran_the_scan(self):
self._launch(plugin)
from extensions.business.cybersec.red_mesh.models import JobConfig
config_dict = plugin.r1fs.add_json.call_args_list[0][0][0]
self.assertEqual(config_dict["redmesh_release"], {"backend": "0.13.0"})
self.assertEqual(config_dict["redmesh_release"], {"backend": "0.13.1"})
restored = JobConfig.from_dict(config_dict).to_dict()
self.assertEqual(restored["redmesh_release"], {"backend": "0.13.0"})
self.assertEqual(restored["redmesh_release"], {"backend": "0.13.1"})

def test_launch_records_the_console_and_report_pipeline_it_was_sent(self):
"""RM-103 item 12: the release names all three deployables. Only the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ def test_forms_posting_to_one_place_are_one_finding(self):
sharing an id."""
self.assertEqual(len(self._run({"/": _page(_form(), _form())})), 1)

def test_path_parameters_are_dropped_per_segment_not_at_the_first_semicolon(self):
findings = self._run({"/": _page(
_form("/account;jsessionid=ABC/delete"), _form("/account;jsessionid=ABC/update"),
)})
self.assertEqual(len({f["finding_id"] for f in findings}), 2)
self.assertEqual(
sorted(f["affected_assets"][0]["parameter"] for f in findings),
["/account/delete", "/account/update"],
)

def test_a_session_id_in_a_middle_segment_does_not_move_the_id(self):
[clean] = self._run({"/": _page(_form("/a/b"))})
for volatile in ("/a;jsessionid=X/b", "/a;jsessionid=Y/b;v=2"):
with self.subTest(action=volatile):
[finding] = self._run({"/": _page(_form(volatile))})
self.assertEqual(finding["finding_id"], clean["finding_id"])

def test_actions_differing_only_in_a_query_are_one_finding(self):
findings = self._run({"/": _page(_form("/search?t=1"), _form("/search?t=2"))})
self.assertEqual(len(findings), 1)
Expand Down
11 changes: 7 additions & 4 deletions extensions/business/cybersec/red_mesh/worker/web/hardening.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,14 @@ def _csrf_form_action(cls, form_html, path):
@staticmethod
def _csrf_identity_action(action, path):
"""The action as an identity input: entities decoded, then the query,
fragment and `;param` path parameters dropped, since a cache-buster or a
session id there would hand the finding a new id on every run. The raw
action stays in `evidence`."""
fragment and each segment's `;param` path parameters dropped, since a
cache-buster or a session id there would hand the finding a new id on
every run. The segments after a `;param` stay: `/a;jsessionid=X/delete`
and `/a;jsessionid=X/update` are two endpoints. The raw action stays in
`evidence`."""
decoded = _html_unescape(action)
stable = _re.split(r"[?#;]", decoded, maxsplit=1)[0].strip()
stable = _re.split(r"[?#]", decoded, maxsplit=1)[0]
stable = _re.sub(r";[^/]*", "", stable).strip()
return stable or path


Expand Down
2 changes: 1 addition & 1 deletion ver.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__VER__ = '2.10.480'
__VER__ = '2.10.481'