8000 feat(misc): condition-based runs, chunked_by opts, dynamic task profiles, cli improvements by ocervell · Pull Request #659 · freelabz/secator · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(misc): condition-based runs, chunked_by opts, dynamic task profiles, cli improvements #659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
May 23, 2025

Conversation

ocervell
Copy link
Contributor
@ocervell ocervell commented May 20, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced CLI with new command aliases, improved report querying, and alias management commands.
    • Added dynamic task and workflow execution based on user-specified conditions and options.
    • Introduced visual runner tree display for workflows and scans.
    • Added clickable file and screenshot links in output for better navigation.
    • Added new CLI test command for template integration tests and coverage filtering.
  • Improvements

    • Refined debug logging with subcategory tagging for better traceability.
    • Improved option handling and conditional execution in workflows and tasks.
    • Enhanced output formatting and visual presentation in reports and tables.
    • Dynamic profile selection for certain tasks based on options.
    • Unified CLI option management and added a tree display feature.
    • Enhanced extractor processing with context-aware filtering and formatting.
    • Added flexible workflow and scan conditional skipping with informative messages.
    • Added support for printing runner profiles and refined runner initialization.
  • Bug Fixes

    • Addressed handling of revoked tasks in task data retrieval.
    • Fixed logic for dynamic queue selection and option merging.
    • Prevented redundant chunking in command execution.
  • Documentation

    • Updated and clarified descriptions in configuration files.
  • Chores

    • Codebase refactoring for unified option management and improved maintainability.
    • Added or updated trailing newlines in configuration files for consistency.
    • Removed deprecated constructs and reorganized CLI command groups.
    • Added new GitHub Actions job for template tests and coverage.
  • Tests

    • Updated tests to reflect changes in option handling, logging, and CLI behavior.
    • Added comprehensive template tests covering tasks, workflows, and scans.
    • Expanded CLI test coverage with mocks and offline mode assertions.

@ocervell ocervell changed the title feat(runner): add condition-based tasks and workflows feat(runner): add condition-based scans and workflows May 20, 2025
@ocervell ocervell closed this May 21, 2025
@ocervell ocervell reopened this May 21, 2025
@ocervell
Copy link
Contributor Author

@coderabbitai please review

Copy link
Contributor
coderabbitai bot commented May 21, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# View PR information
gh pr view 659
# List files changed in the PR
gh pr view 659 --files
# Show the diff of the PR
gh pr diff 659

Length of output: 69871


@ocervell I've reviewed the PR and here's my assessment:

This is a significant enhancement that adds several valuable features to Secator:

  1. Condition-based execution - The ability to conditionally run tasks and workflows with if: statements in YAML configurations is a major improvement for flexibility.

  2. CLI improvements:

    • Better command organization with consistent aliases (profiles, reports, workspaces, etc.)
    • Improved query processing for report filtering
    • More intuitive command structure overall
  3. Enhanced configuration:

    • Debug configuration is now string-based for more granular control
    • Better options handling in workflows and scans
    • More flexible parameter passing between tasks
  4. Code organization:

    • Extracted Click-related functionality to click.py
    • Better structured decorators and command registration

The PR improves both the developer and user experience while maintaining backward compatibility. The condition-based execution feature is especially valuable as it allows for more sophisticated and targeted security scanning workflows.

Overall, the changes are well-designed and represent a significant improvement to the tool's flexibility and usability.

@ocervell ocervell changed the title feat(runner): add condition-based scans and workflows feat(misc): condition-based runs, chunked_by opts, more debug, dynamic task profiles May 21, 2025
@ocervell ocervell changed the title feat(misc): condition-based runs, chunked_by opts, more debug, dynamic task profiles feat(misc): condition-based runs, chunked_by opts, dynamic task profiles, cli improvements May 21, 2025
Copy link
Contributor
coderabbitai bot commented May 22, 2025

Walkthrough

This update introduces a broad refactor and feature expansion across the codebase. Key changes include enhanced CLI aliasing and grouping, dynamic runner tree visualization, improved debug logging and option handling, conditional task/workflow execution, more robust extraction and reporting logic, and updated configuration schemas. Several modules are introduced or restructured to support these features.

Changes

File(s) Change Summary
secator/cli.py, secator/click.py, secator/decorators.py Refactored CLI command groups with alias support, added new command groups (profile, alias), improved report querying/filtering, and unified CLI option management. Introduced OrderedGroup for CLI aliasing and command ordering. Added new test commands and options.
secator/tree.py New module for building and rendering runner trees, with TaskNode and RunnerTree classes, and a build_runner_tree function for visualizing workflow/scan structures.
secator/runners/_base.py, secator/runners/command.py, secator/runners/workflow.py, secator/runners/task.py, secator/runners/scan.py Refactored runner initialization and execution: improved debug logging, dynamic option resolution, conditional task/workflow execution, dynamic queue selection, and enhanced hook/validator management. Added conditional skipping based on if expressions.
secator/runners/_helpers.py Refactored extractor logic: added context-aware extraction, new extractor formatting, improved condition evaluation, and enhanced error handling.
secator/report.py Refactored report building: removed option merging, improved sorting error handling, and introduced type-aware, operation-aware extractor aggregation with error reporting.
secator/output_types/url.py, secator/output_types/tag.py, secator/output_types/info.py, secator/output_types/_base.py, secator/output_types/error.py Enhanced output types: added fields (e.g., method, stored_response_path), improved string representations, and clickable links for paths. Removed unnecessary escaping in info and error output.
secator/rich.py Updated table formatters for clickable links and icons, improved formatting logic, and simplified column wrapping. Added file existence checks for link display.
secator/utils.py, secator/definitions.py, secator/config.py, secator/celery.py, secator/celery_utils.py Refactored debug configuration: consolidated debug variable, updated logging level logic, improved debug output formatting, and adjusted Celery logging and task chunking behavior. Added explicit handling for revoked Celery tasks.
secator/exporters/console.py Switched to using console_stdout for result output printing.
secator/template.py Enhanced template loader to support workflow option extraction and modular workflow/task handling.
secator/tasks/httpx.py, secator/tasks/katana.py, secator/tasks/ffuf.py, secator/tasks/dnsx.py, secator/tasks/bbot.py, secator/tasks/nmap.py Added dynamic profile selection based on options, new options (e.g., stop_on_error), improved output object construction with stored response paths, refined tag and vulnerability handling, and improved conditional logic.
secator/configs/profiles/default.yaml Updated profile description and disabled options via comments.
secator/configs/scans/*.yaml, secator/configs/workflows/*.yaml Added trailing newlines, new options, conditional execution logic, and removed or modified results sections for various scans and workflows.
tests/integration/*, tests/unit/* Updated logging level logic, test command parameters, and test assertions to match refactored code and new debug handling. Added new template tests and GitHub Actions job for templates.
.github/workflows/tests.yml Added new GitHub Actions job for running template tests 8000 and collecting coverage.
tests/template/test_templates.py Added new unittest for testing all tasks, workflows, and scans including dynamic imports in dry-run mode.
tests/unit/test_cli.py Expanded CLI tests with mocks, added coverage for many commands, and improved assertions for offline mode and output.
tests/integration/test_worker.py Updated test commands to use process=True and added --nuclei flag in host recon test.
tests/unit/test_runners_helpers.py Added comprehensive unit tests for extractor helper functions including parsing, formatting, processing, and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant OrderedGroup
    participant Runner
    participant Tree
    participant Report

    User->>CLI: Invokes command (with aliases)
    CLI->>OrderedGroup: Resolves command/group/alias
    CLI->>Runner: Initializes runner with options
    Runner->>Tree: Optionally builds runner tree (if tree flag)
    Tree-->>Runner: Returns tree structure
    Runner->>Runner: Resolves options, profiles, hooks, validators
    Runner->>Runner: Executes tasks/workflows (conditional on 'if' expressions)
    Runner->>Report: Builds report with extractors
    Report-->>Runner: Returns filtered/deduped results
    Runner->>CLI: Outputs results (with enhanced formatting)
Loading

Poem

A rabbit hops through fields of code,
With trees and tasks in neat abode.
Aliases bloom, commands align,
Workflows skip when stars decline.
Debugging carrots, options new,
Profiles fresh and output too.
🐇 Cheers to changes—onward we go!
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🔭 Outside diff range comments (2)
secator/celery.py (1)

297-327: 🛠️ Refactor suggestion

opts object reused across chunks – each Celery signature should receive its own copy

opts is mutated inside the loop (chunk, chunk_count, results).
Since the same dict is passed to every signature, later iterations overwrite the values captured by earlier ones, leading to inconsistent behaviour when workers unpickle the tasks.

-    # Clone opts
-    opts = task_opts.copy()
+    # Base template for every chunk
+    base_opts = task_opts.copy()
 ...
-        if len(chunks) > 0:
-            opts['chunk'] = ix + 1
-            opts['chunk_count'] = len(chunks)
+        # Build a fresh copy for this specific chunk
+        opts = base_opts.copy()
+        opts.update({'chunk': ix + 1, 'chunk_count': len(chunks)})
 ...
-        opts['results'] = chunked_results
-        sig = type(task).si(chunk, **opts).set(task_id=task_id)
+        opts['results'] = chunked_results
+        sig = type(task).si(chunk, **opts).set(task_id=task_id)
secator/decorators.py (1)

291-325: ⚠️ Potential issue

Duplicate “show version” block and potential NameError

The version-handling logic appears twice (l. 302-313 & 314-324).
Additionally, it references task_cls, which is undefined for scan and workflow commands, raising NameError if --version is used there.

Please de-duplicate and scope by runner type:

-    version = opts['version']
+    version_requested = opts['version']
     ...
-    if version:
+    if version_requested and runner_cls is Task:
         ...

This removes the redundancy and prevents runtime crashes.

♻️ Duplicate comments (1)
secator/runners/_helpers.py (1)

65-74: Replicate the “mutable-default” fix for the other helpers

Both extract_from_results (l. 65) and process_extractor (l. 115) repeat the ctx={} anti-pattern.
For consistency and safety, patch them the same way as suggested above.

Also applies to: 115-124

🧰 Tools
🪛 Ruff (0.11.9)

65-65: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

🧹 Nitpick comments (18)
tests/integration/test_workflows.py (1)

58-61: Consider updating remaining numeric DEBUG comparisons

While the logging level check was updated to use the new string-based approach DEBUG == ["1"], these lines still use numeric comparisons (DEBUG > 1, DEBUG > 2). For consistency, consider updating these to match the new string-based debug configuration pattern.

-			'print_item': DEBUG > 1,
-			'print_line': DEBUG > 2,
-			'table': DEBUG > 1,
-			'output': 'table' if DEBUG > 1 else ''
+			'print_item': "2" in DEBUG or "3" in DEBUG,
+			'print_line': "3" in DEBUG,
+			'table': "2" in DEBUG or "3" in DEBUG,
+			'output': 'table' if "2" in DEBUG or "3" in DEBUG else ''
secator/tasks/httpx.py (1)

80-90: Implemented dynamic profile selection based on task options

The static profile assignment has been replaced with a dynamic approach that selects the appropriate processing profile based on the task's options. This implementation:

  1. Uses a lambda function to call the new dynamic_profile static method
  2. Selects 'cpu' profile when screenshot functionality is enabled, otherwise defaults to 'io'
  3. Demonstrates the "dynamic task profiles" feature mentioned in the PR objectives

This is an excellent enhancement that allows for more efficient resource allocation based on the specific requirements of each task execution.

However, consider using a regular method instead of a lambda function for better readability:

-profile = lambda opts: httpx.dynamic_profile(opts)  # noqa: E731
+profile = httpx.dynamic_profile

This would be slightly cleaner and avoid the need for the E731 linter exclusion.

secator/utils.py (1)

430-431: Optimize boolean logic with inequality operator.

The code uses not DEBUG == ['all'] and not DEBUG == ['1'] which can be simplified.

-	if not DEBUG == ['all'] and not DEBUG == ['1']:
+	if DEBUG != ['all'] and DEBUG != ['1']:
🧰 Tools
🪛 Ruff (0.11.9)

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

secator/tasks/ffuf.py (1)

99-102: Improved Host header fuzzing condition.

The condition has been refined to specifically check for HTTP URLs rather than just checking if inputs exist, which is more precise.

Consider simplifying the nested if statements for better readability:

-		if self.get_opt_value('fuzz_host_header'):
-			if 'http://' in self.inputs[0]:
+		if self.get_opt_value('fuzz_host_header') and 'http://' in self.inputs[0]:
🧰 Tools
🪛 Ruff (0.11.9)

99-100: Use a single if statement instead of nested if statements

(SIM102)

secator/runners/workflow.py (1)

142-144: Provide a deterministic fallback queue when profile returns None

If task.profile(opts) legitimately returns None, the resulting Signature.set(queue=None) lets Celery pick the default queue implicitly. That works, but makes routing harder to reason about and can hide mis-configurations.

-profile = task.profile(opts) if callable(task.profile) else task.profile
-sig = task.s(inputs, **opts).set(queue=profile, task_id=task_id)
+queue = task.profile(opts) if callable(task.profile) else task.profile
+if not queue:
+    queue = CONFIG.celery.default_queue   # or a hard-coded "default"
+sig = task.s(inputs, **opts).set(queue=queue, task_id=task_id)

This turns a silent fallback into an explicit, auditable decision.

secator/template.py (1)

130-137: Memoise workflow extraction for speed & recursion safety

_extract_workflows() instantiates TemplateLoader on every call (invoked by both supported_opts and flat_tasks). In large repos this is noticeable, and repeated parsing risks cyclic loads.

Consider caching the result per instance:

- def _extract_workflows(self):
+ from functools import lru_cache
+
+ @lru_cache(maxsize=1)
+ def _extract_workflows(self):

A one-line decorator yields O(1) subsequent look-ups without altering call-sites.

secator/report.py (1)

95-117: Duplicates may re-appear after extractor union/intersection – run remove_duplicates once more

remove_duplicates() is executed before the extractor pipeline, but the "or" union path (l. 109-111) can introduce the same object several times.
A lightweight second pass keeps the final payload clean and predictable:

-        items = all_res
+        items = remove_duplicates(all_res) if dedupe else all_res
secator/celery.py (1)

311-319: list.remove in filtering loop may raise ValueError

If the same object occurs multiple times in results, calling remove for each occurrence of temp will fail after the first removal.
Safer (and faster) to build a new list:

-    chunked_results = results.copy()
-    ...
-            for item in temp:
-                item_attr = getattr(item, attr)
-                if item_attr not in chunk:
-                    chunked_results.remove(item)
+    chunked_results = [
+        r for r in results
+        if not (r._type == _type and getattr(r, attr) not in chunk)
+    ]
secator/click.py (1)

12-15: Handle None / empty values & strip whitespace in ListParamType.convert

click can pass None (e.g. option omitted) which will raise.
Also trailing spaces remain in elements.

-        if isinstance(value, list):
-            return value
-        return value.split(',')
+        if value is None:
+            return []
+        if isinstance(value, list):
+            return value
+        return [v.strip() for v in value.split(',') if v.strip()]
secator/tasks/katana.py (2)

101-108: Path construction is not OS-agnostic

Hard-coding the "/.outputs" separator makes the command brittle on Windows paths.
Prefer os.path.join (or even Path) to assemble the path.

-            self.cmd += f' -srd {self.reports_folder}/.outputs'
+            output_dir = os.path.join(self.reports_folder, ".outputs")
+            self.cmd += f" -srd {output_dir}"

139-150: Minor optimisation — avoid rewriting unchanged files

os.path.exists is checked, but we always open the file and rewrite its
contents even when the first line is already at the bottom.
Consider comparing before rewriting to save a disk write on large runs.

secator/configs/workflows/host_recon.yaml (1)

23-29: Quote boolean expressions to avoid YAML quirks

Some YAML parsers coerce unquoted scalars such as not opts.full
into unexpected types. Wrapping the expression in quotes makes the
intent crystal-clear.

-    if: not opts.full
+    if: "not opts.full"

Do the same for the naabu/full condition.

secator/tree.py (2)

118-121: Variable shadowing hampers readability

Re-using the name config for the newly-loaded workflow overrides the
function argument, making the code harder to follow.

-            config = TemplateLoader(name=f'workflow/{workflow_name}')
-            workflow_tree = build_runner_tree(config, condition)
+            wf_config = TemplateLoader(name=f'workflow/{workflow_name}')
+            workflow_tree = build_runner_tree(wf_config, condition)

62-64: Tiny micro-perf: cache render function

child_str = self.render_opts.get(child.type, lambda x: str(x))
is looked up for every child. Caching outside the loop or using
functools.lru_cache could shave a few µs on very large trees, though
this is definitely optional.

secator/decorators.py (1)

60-87: decorate_command_options – short option generation can violate Click rules

When no explicit short key is given the code falls back to -<opt_name> even if the name is multi-character (e.g. --profiles-profiles). Click only allows a single character short flag and will raise.

Consider guarding it:

if short_opt:
    short = f'-{short_opt}'
elif len(opt_name) == 1:
    short = f'-{opt_name}'
else:
    short = None
...
params = [long] if short is None else [long, short]
f = click.option(*params, **conf)(f)
secator/runners/_base.py (1)

775-778: Prefer direct comparison over double negation

not self.config.type == 'task' is harder to read and was flagged by Ruff.

-        if not self.has_parent and not self.config.type == 'task':
+        if not self.has_parent and self.config.type != 'task':

Pure readability win, no behavioural change.

🧰 Tools
🪛 Ruff (0.11.9)

776-776: Use self.config.type != 'task' instead of not self.config.type == 'task'

Replace with != operator

(SIM201)

secator/cli.py (2)

765-768: Unused loop index ix – simplify the loop

ix isn’t used; drop enumerate to silence B007 and reduce noise:

- for ix, extractor in enumerate(extractors):
+ for extractor in extractors:

[nitpick but worth tightening]

🧰 Tools
🪛 Ruff (0.11.9)

767-767: Loop control variable ix not used within loop body

(B007)


1498-1526: Three almost-identical test sub-commands – refactor to a helper

test integration, test template, and test performance duplicate

env-var setup ➔ shutil.rmtree ➔ coverage + pytest command building

Extract the common body into a private helper (e.g. _run_pytest(kind, folder, tasks, workflows, scans, test)), then each command becomes a 3-liner. This
cuts >100 lines and future-proofs new kinds of tests.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc65092 and 1b791e9.

📒 Files selected for processing (49)
  • secator/celery.py (3 hunks)
  • secator/celery_utils.py (2 hunks)
  • secator/cli.py (17 hunks)
  • secator/click.py (1 hunks)
  • secator/config.py (2 hunks)
  • secator/configs/profiles/default.yaml (1 hunks)
  • secator/configs/scans/domain.yaml (1 hunks)
  • secator/configs/scans/host.yaml (1 hunks)
  • secator/configs/scans/network.yaml (1 hunks)
  • secator/configs/scans/subdomain.yaml (1 hunks)
  • secator/configs/scans/url.yaml (1 hunks)
  • secator/configs/workflows/cidr_recon.yaml (0 hunks)
  • secator/configs/workflows/code_scan.yaml (1 hunks)
  • secator/configs/workflows/host_recon.yaml (1 hunks)
  • secator/configs/workflows/subdomain_recon.yaml (1 hunks)
  • secator/configs/workflows/url_crawl.yaml (1 hunks)
  • secator/configs/workflows/url_dirsearch.yaml (0 hunks)
  • secator/configs/workflows/url_fuzz.yaml (0 hunks)
  • secator/configs/workflows/url_nuclei.yaml (1 hunks)
  • secator/configs/workflows/user_hunt.yaml (1 hunks)
  • secator/configs/workflows/wordpress.yaml (1 hunks)
  • secator/decorators.py (12 hunks)
  • secator/definitions.py (1 hunks)
  • secator/exporters/console.py (1 hunks)
  • secator/output_types/_base.py (1 hunks)
  • secator/output_types/info.py (2 hunks)
  • secator/output_types/tag.py (2 hunks)
  • secator/output_types/url.py (3 hunks)
  • secator/report.py (3 hunks)
  • secator/rich.py (3 hunks)
  • secator/runners/_base.py (34 hunks)
  • secator/runners/_helpers.py (3 hunks)
  • secator/runners/command.py (17 hunks)
  • secator/runners/scan.py (3 hunks)
  • secator/runners/task.py (1 hunks)
  • secator/runners/workflow.py (6 hunks)
  • secator/tasks/dnsx.py (1 hunks)
  • secator/tasks/ffuf.py (3 hunks)
  • secator/tasks/httpx.py (1 hunks)
  • secator/tasks/katana.py (2 hunks)
  • secator/template.py (2 hunks)
  • secator/tree.py (1 hunks)
  • secator/utils.py (3 hunks)
  • tests/integration/test_scans.py (1 hunks)
  • tests/integration/test_worker.py (2 hunks)
  • tests/integration/test_workflows.py (1 hunks)
  • tests/unit/test_command.py (1 hunks)
  • tests/unit/test_runners.py (1 hunks)
  • tests/unit/test_tasks.py (2 hunks)
💤 Files with no reviewable changes (3)
  • secator/configs/workflows/cidr_recon.yaml
  • secator/configs/workflows/url_dirsearch.yaml
  • secator/configs/workflows/url_fuzz.yaml
🧰 Additional context used
🧬 Code Graph Analysis (7)
tests/integration/test_scans.py (2)
secator/utils.py (1)
  • setup_logging (44-60)
secator/utils_test.py (1)
  • CommandOutputTester (161-270)
secator/tasks/dnsx.py (1)
secator/utils.py (1)
  • extract_domain_info (512-530)
secator/template.py (2)
secator/output_types/_base.py (1)
  • toDict (104-108)
secator/utils.py (1)
  • convert_functions_to_strings (818-834)
secator/celery.py (3)
secator/utils.py (1)
  • debug (428-465)
secator/decorators.py (1)
  • task (42-46)
secator/runners/command.py (1)
  • si (237-241)
secator/report.py (3)
secator/utils.py (2)
  • get_file_timestamp (353-355)
  • traceback_as_string (647-656)
secator/runners/_base.py (1)
  • errors (237-238)
secator/runners/_helpers.py (1)
  • extract_from_results (65-87)
secator/decorators.py (8)
secator/runners/task.py (1)
  • Task (8-80)
secator/runners/workflow.py (1)
  • Workflow (12-149)
secator/tree.py (1)
  • build_runner_tree (73-125)
secator/utils.py (3)
  • deduplicate (128-146)
  • expand_input (63-108)
  • get_command_category (263-274)
secator/click.py (2)
  • decorator (29-53)
  • decorator (59-81)
secator/runners/_base.py (1)
  • toDict (604-631)
secator/template.py (2)
  • flat_tasks (57-59)
  • _extract_workflows (130-137)
secator/config.py (1)
  • get (203-226)
secator/tree.py (1)
secator/template.py (1)
  • TemplateLoader (17-137)
🪛 Ruff (0.11.9)
secator/utils.py

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

secator/tasks/ffuf.py

99-100: Use a single if statement instead of nested if statements

(SIM102)

secator/runners/_helpers.py

7-7: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


7-7: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


65-65: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


115-115: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

secator/runners/_base.py

776-776: Use self.config.type != 'task' instead of not self.config.type == 'task'

Replace with != operator

(SIM201)

secator/runners/command.py

237-237: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

secator/cli.py

693-693: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


767-767: Loop control variable ix not used within loop body

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (80)
secator/configs/workflows/wordpress.yaml (2)

17-17: Approve addition of tags to the wpprobe task.
Adding tags: wordpress under the wpprobe task improves categorization and aligns it with the other tasks in this workflow.


18-18: Trailing newline only.
The blank line at the end is a formatting update for consistency across YAML files.

secator/configs/workflows/user_hunt.yaml (2)

10-10: Add a description for the maigret task.
Including description: Hunt user accounts enhances clarity and documentation consistency.


11-11: Trailing newline only.
Formatting update to ensure consistent file endings.

secator/configs/scans/host.yaml (2)

14-14: Define default target for url_vuln.
Adding - url.url under url_vuln.targets_ ensures the vulnerability scan has a concrete input.


15-15: Trailing newline only.
Consistent formatting update across scan configuration files.

secator/configs/workflows/url_nuclei.yaml (2)

11-11: Introduce exclude_tags for the nuclei task.
The new exclude_tags list provides finer control over skipped templates during the HTTP vulnerability scan.


12-12: Trailing newline only.
Pure formatting change for consistency.

secator/configs/scans/url.yaml (2)

12-12: Define default target for url_vuln.
Specifying - url.url ensures the URL vulnerability scan has a defined input.


13-13: Trailing newline only.
Just a formatting fix for consistent file endings.

secator/configs/workflows/code_scan.yaml (1)

11-11: Add trailing newline for consistency
This change only appends a blank line after the description field under the grype task to ensure all workflow YAMLs end with a newline. No functional or structural modifications.

secator/configs/scans/network.yaml (1)

17-17: Add trailing newline for consistency
A newline was added at the end of this scan configuration to align formatting with the rest of the scan YAML files. No content or behavior changes.

secator/configs/scans/domain.yaml (1)

18-18: Add trailing newline for consistency
Only a trailing newline was introduced at the end of the file to maintain uniform file endings across domain scan configurations. No functional changes.

secator/configs/profiles/default.yaml (1)

3-8: Clear profile configuration changes

The profile description update and the change to an empty opts dictionary with commented-out options provides a good balance between simplification and documentation. This approach gives users the flexibility to enable specific options when needed while maintaining a cleaner default configuration.

secator/output_types/info.py (2)

4-4: Updated imports for simplified formatting

The removal of the rich_escape import aligns with the simplified string formatting approach in the __repr__ method.


23-23: Simplified message formatting in __repr__

The direct embedding of self.message in the formatted string removes an unnecessary escaping step, streamlining the output generation.

secator/output_types/_base.py (1)

15-17: Added useful __str__ method

The new string representation method provides a clean and consistent way to identify output type instances, which improves debugging and display consistency across the application.

secator/exporters/console.py (2)

2-2: Updated console import

The change to import console_stdout instead of console aligns with broader improvements to output handling.


10-10: Using dedicated stdout console

Using console_stdout for printing ensures consistent formatting across the application and properly separates standard output from other console output types.

secator/tasks/dnsx.py (1)

69-70: Improved subdomain source tracking with DNS tagging

The addition of sources=['dns'] enhances observability by explicitly tracking subdomains discovered through DNS resolution. This change aligns with the conditional execution features in the broader PR, allowing for better filtering and traceability of subdomain origins.

tests/unit/test_command.py (1)

14-14: Updated debug configuration to use string-based list matching

The condition for setting the logging level has been changed from a numeric comparison to a list equality check, aligning with the new string-based debug configuration approach introduced in this PR. This standardizes debug level handling across the codebase.

tests/integration/test_scans.py (2)

9-9: Removed unused imports

Clean-up of unused imports from secator.rich and secator.utils improves code cleanliness.


15-15: Updated debug configuration to use string-based list matching

The condition for setting the logging level has been changed from a numeric comparison to a list equality check, aligning with the new string-based debug configuration approach introduced in this PR.

tests/integration/test_workflows.py (1)

18-18: Updated debug configuration to use string-based list matching

The condition for setting the logging level has been changed from a numeric comparison to a list equality check, aligning with the new string-based debug configuration approach.

tests/unit/test_runners.py (1)

95-96: Updated test expectations to match refactored hook implementation

The assertions for run_hooks calls now include the sub='init' parameter, matching the enhanced hook invocation system that provides more granular debug context during command lifecycle events.

tests/unit/test_tasks.py (2)

12-12: Updated debug flag checking to use new string-based format

The condition for setting the logging level has been modified to check if DEBUG equals the list ["1"] rather than checking if it's greater than 0. This change aligns with the refactoring of debug configuration to use a string-based approach for finer control.


33-33: Updated debug command configuration path

The check for debug_command now looks directly in CONFIG.debug instead of CONFIG.debug.component, reflecting the simplified debug configuration structure where debug is now a string rather than an object with components.

secator/output_types/tag.py (2)

13-13: Added stored response path field to Tag dataclass

New field allows tracking the location of stored responses associated with a tag, enhancing data linkage capabilities.


36-37: Enhanced Tag representation with clickable file link

The __repr__ method now includes a clickable envelope icon that links to the stored response file when available, improving user experience by providing direct access to associated content.

secator/configs/scans/subdomain.yaml (2)

7-10: Added test option for conditional workflow execution

New string option test with default value "test" enables configuration-based control of workflow execution.


12-13: Implemented condition-based workflow execution

Added conditional execution for the subdomain_recon workflow using the new if statement syntax. The workflow will only run if opts.test == 'test', demonstrating the new condition-based execution feature.

tests/integration/test_worker.py (2)

28-28: Parameter changed from no_process=False to process=True

This change aligns with the updated API in the Command.execute method that now uses a positive process flag instead of the previous negated no_process flag. The boolean semantics have been inverted but the behavior remains consistent.


48-51: Test updated to verify new nuclei integration option

The test has been properly updated to:

  1. Include the new --nuclei flag in the command string to test the conditional nuclei scanning feature
  2. Use the new process=True parameter instead of the previous no_process=False

These changes ensure the test aligns with the updated command execution API and the new workflow option for conditionally enabling nuclei scanning.

secator/definitions.py (1)

23-23: Debug configuration simplified to string-based approach

The debug configuration has been refactored from a structured model approach (CONFIG.debug.level and CONFIG.debug.component) to a simpler string-based approach using CONFIG.debug.split(','). This change:

  1. Makes debugging more flexible by allowing arbitrary debug categories
  2. Simplifies configuration by consolidating the debug settings into a single string
  3. Enables more granular control over what gets logged

This aligns with the PR's goal of improving configuration handling.

secator/config.py (2)

172-172: Debug configuration type changed from nested model to string

The debug attribute in SecatorConfig has been changed from a structured Pydantic model to a simple string. This simplification:

  1. Makes the configuration more intuitive and easier to modify
  2. Reduces complexity by eliminating nested configuration objects
  3. Aligns with the string-based debug approach seen in definitions.py

This is a positive change that improves the configuration interface.


626-627: Debug condition check updated for string-based approach

The conditional check for debug configuration has been updated to use a substring presence test ('config' in CONFIG.debug) instead of checking a nested attribute. This correctly implements the new string-based debug configuration approach.

secator/utils.py (4)

29-29: Good update to use the consolidated DEBUG variable.

The import statement now correctly uses DEBUG instead of the removed DEBUG_COMPONENT from secator.definitions, which aligns with the consolidated debug configuration approach described in the PR objectives.


422-422: Enhanced console formatting for better readability.

The updated formatting with bold blue keys and yellow arrows creates better visual hierarchy in debug output.


434-443: Improved debug component checking logic.

The code now correctly iterates through the DEBUG list to check for debugging components, supporting the new string-based debug configuration approach.

🧰 Tools
🪛 Ruff (0.11.9)

436-439: Combine if branches using logical or operator

Combine if branches

(SIM114)


438-441: Combine if branches using logical or operator

Combine if branches

(SIM114)


464-465: Properly guarded exception raising for rich markup issues.

The condition now checks if 'rich' is in the DEBUG list before re-raising exceptions, which aligns with the updated debug configuration.

secator/celery_utils.py (3)

8-8: Good addition of TaskRevokedError import.

This import is necessary for the improved task revocation handling implemented below.


242-245: Updated comment to reflect new task revocation handling.

The comment now correctly explains that the exception could specifically be a TaskRevokedError, which is handled differently than other exceptions.


247-253: Enhanced task revocation handling.

This change properly distinguishes between revoked tasks and other exceptions, providing clearer state management and better error reporting. For revoked tasks, it sets a descriptive error message, updates the state to 'REVOKED', and marks the task as ready without raising the exception.

The improved handling of revoked tasks supports the condition-based execution features introduced in this PR, making task and workflow management more robust.

secator/tasks/ffuf.py (2)

37-37: Good addition of stop_on_error option.

This new boolean option gives users more control over ffuf task execution behavior.


65-65: Proper mapping to ffuf's native flag.

The stop_on_error option is correctly mapped to ffuf's -sa flag in the option key map.

secator/runners/task.py (2)

52-56: Clarified responsibility for print and report handling.

These explicit flags ensure that the task class itself manages the printing and reporting behaviors rather than the Celery runner, making the code more maintainable and reducing potential for conflicts.


62-63: Implemented dynamic queue assignment.

This enhancement allows for flexible queue routing based on runtime options. If task_cls.profile is callable, it's invoked with the options to determine the queue name dynamically; otherwise, it uses the static profile string.

This change aligns perfectly with the PR objective of enabling dynamic task profiles, allowing for more sophisticated task routing based on runtime conditions.

secator/output_types/url.py (6)

5-5: Expanded imports to include METHOD constant.

Good addition of the METHOD constant from secator.definitions to support the new method field in the Url class.


22-22: Added HTTP method field to Url dataclass.

Great enhancement to the Url dataclass to track the HTTP method used for the request. This provides more comprehensive information about the URL interaction.


41-41: Added METHOD to table fields.

Appropriate update to include the METHOD field in the table display, ensuring the new field is visible in tabular outputs.


48-49: Replaced TIME with stored_response_path and screenshot_path in table fields.

Good replacement of the TIME field with more useful information about stored responses and screenshots, enhancing the information displayed in tables.


64-65: Added conditional display of HTTP method.

Nice enhancement to only show the method in the string representation when it's not GET, keeping the output clean while still surfacing non-standard methods.


90-93: Added clickable links for screenshots and stored responses.

Excellent improvement to the repr method to render screenshot_path and stored_response_path as clickable icon links, enhancing user interaction with the results.

secator/configs/workflows/url_crawl.yaml (5)

7-12: Added configurable crawler selection option.

Good addition of the crawlers option to dynamically control which crawler tasks are enabled. This provides flexibility for users to customize their crawling strategy.


19-24: Added conditional crawler tasks with if statements.

Excellent implementation of condition-based execution for the gau and gospider tasks. The conditional execution using the if statement with opts.crawlers check aligns with the PR objective of adding condition-based runs.


27-27: Added conditional execution for cariddi crawler.

Consistent implementation of conditional execution for the cariddi task, maintaining the pattern established for other crawlers.


30-30: Added conditional execution for katana crawler.

Consistent implementation of conditional execution for the katana task, ensuring all crawler tasks follow the same pattern of conditional execution.


34-34: Simplified targets_ specification.

Good simplification of the targets_ field from a dictionary to a direct list containing url.url. This makes the configuration more concise and easier to understand.

secator/runners/scan.py (5)

3-6: Added imports for DotMap and Info output type.

Good addition of necessary imports to support the new conditional execution feature and output of informational messages when workflows are skipped.


37-37: Initialized sig variable.

Good initialization of the sig variable to None, ensuring it has a valid value even if no signatures are added to the sigs list.


43-43: Added has_parent flag to run options.

Good addition of the has_parent flag to the run options, which helps in hierarchical workflow execution context awareness.


48-54: Implemented conditional workflow execution.

Excellent implementation of condition-based workflow execution. The code properly:

  1. Extracts the 'if' condition from workflow options
  2. Creates a restricted evaluation context with only the opts variable
  3. Evaluates the condition safely
  4. Skips the workflow with an informative message when the condition is not met

This directly implements the PR objective of adding condition-based runs for workflows.


69-75: Added null check before creating the chain.

Good addition of a check to ensure there are signatures before creating the Celery chain. This prevents attempting to create an empty chain when all workflows are skipped.

secator/rich.py (6)

2-2: Added Path import from pathlib.

Good addition of the Path import to support checking file existence for the new clickable file link formatters.


41-41: Enhanced confidence formatter.

Good improvement to the confidence formatter to display values in dim uppercase text, making them visually distinct.


45-45: Enhanced URL formatter with clickable links.

Excellent enhancement to make URLs clickable in the UI, improving user experience when interacting with URL data.


46-48: Added formatters for file paths with clickable icons.

Great addition of formatters for stored_response_path and screenshot_path that render as clickable icons when the files exist. This enhances the interactivity of the output and provides quick access to response data and screenshots.


51-52: Added formatters for conditional clickable links.

Excellent addition of formatters for matched_at and match fields that render as clickable links when they are URLs. This improves navigation between related resources.


97-106: Simplified table column creation.

Good simplification of the table column creation by removing complex wrapping and overflow logic. The commented code with TODO indicates this was intentional and the simpler approach is preferred.

secator/runners/workflow.py (2)

38-47: 👍 Cleaner option handling

Removing print_profiles early and deferring report generation to the workflow runner keeps concerns well-separated.


147-149: Guard against None signatures – nice touch

Only appending truthy signatures prevents None placeholders leaking into Celery chains. Saves a lot of head-scratching later.

secator/template.py (1)

118-123: Good namespacing of tasks inside scans

Prefixing tasks with their workflow name (wf/task) prevents key collisions when multiple workflows share task IDs – solid choice.

secator/configs/workflows/subdomain_recon.yaml (3)

9-27: Verify “internal + default=True” usability

probe is flagged internal: True yet defaults to True. Because internal options are usually hidden from the CLI, users have no direct way to disable probing (--no-probe) unless the framework still auto-generates a negated flag for internal options. Please confirm that:

  1. secator honours is_flag negation for internal options.
  2. Documentation clarifies the feature.

If not, consider removing internal: True or documenting the implicit behaviour.


37-45: Conditional expressions align with new runner logic

Using opts.brute_dns, opts.probe or opts.brute_http, etc., matches the DotMap evaluation introduced in the runner – nice consistency.


51-56: stop_on_error: True is a welcome safety valve

Explicitly halting ffuf on first error avoids noisy cascades when the HTTP brute stage fails. Good addition.

secator/click.py (1)

22-25: Overwriting self.commands drops the OrderedDict initialised by click

click.Group already sets self.commands (ordered since Python 3.7).
Re-assigning here risks losing commands registered before OrderedGroup’s __init__ completes.
Prefer in-place conversion only when needed:

-        super(OrderedGroup, self).__init__(name, commands, **attrs)
-        self.commands = commands or OrderedDict()
+        super().__init__(name, commands or OrderedDict(), **attrs)
secator/tasks/katana.py (1)

89-100: Dynamic profile – double-check queue naming consistency

The lambda correctly defers profile selection until runtime, nice!
However, please ensure the runner queues are actually declared as "cpu" / "io" elsewhere – a mismatch will silently fall back to the default queue.
If the queues are centrally defined, feel free to ignore this.

secator/configs/workflows/host_recon.yaml (2)

10-19: Great addition of toggleable options :+1

Introducing nuclei and full flags increases flexibility while
preserving previous defaults – looks good.


34-41: Verify targets binding inside ports_ condition

item.host in targets assumes the template engine provides a targets
variable in this context. If that symbol is not injected the
expression will raise at runtime, skipping the task entirely.

secator/runners/command.py (1)

325-329:

❌ Incorrect review comment

process vs no_process flag mismatch may break execution

execute() now injects process:

kwargs['process'] = kwargs.get('process', False)

but the rest of the class (e.g. yielder, process_line) still checks
self.no_process. Unless Runner.__init__ maps process → no_process,
commands launched via Command.execute() will always think they are in
“no-process” mode.

Quick fix:

kwargs['print_cmd'] = not kwargs.get('quiet', False)
kwargs['print_line'] = True
-kwargs['process'] = kwargs.get('process', False)
+# keep both for backward compat
+process_flag = kwargs.pop('process', False)
+kwargs['no_process'] = not process_flag

Please verify the mapping in Runner.


🏁 Script executed:

#!/bin/bash
set -e

# Search for Runner class definitions in secator/runners
rg -n "class Runner" -n secator/runners

# For each file containing Runner, display its __init__ method and surrounding lines
for file in $(rg -l "class Runner" secator/runners); do
  echo "=== File: $file ==="
  rg -n "def __init__" -A10 "$file"
done

# Search for no_process flag usage in secator/runners
rg -n "no_process" -n secator/runners

Length of output: 2317


#!/bin/bash
set -e

# Show the Command class definition with context
rg -n -C10 "class Command" secator/runners/command.py

# Show the __init__ method in Command with context
rg -n -C10 "def __init__" secator/runners/command.py

# Show any super(...) calls in Command.__init__
rg -n -C5 "super" secator/runners/command.py

🏁 Script executed:

#!/bin/bash
set -e

# Display the Command class declaration with context
rg -n -C5 "class Command" secator/runners/command.py

# Display the __init__ method in Command with surrounding lines for context
rg -n -C20 "def __init__" secator/runners/command.py

Length of output: 1336


🏁 Script executed:

#!/bin/bash
set -e

# Display full Command.__init__ method to find super() call and mapping of run_opts
sed -n '120,260p' secator/runners/command.py

Length of output: 3979


#!/bin/bash
set -e

# Display context around lines 320 in secator/runners/command.py to locate the execute() snippet
sed -n '300,360p' secator/runners/command.py

🏁 Script executed:

#!/bin/bash
set -e

# Locate the execute() method in secator/runners/command.py
rg -n "def execute" -n secator/runners/command.py -A5

# Show the lines around the kwargs['process'] injection
rg -n "kwargs\['process'\]" -n secator/runners/command.py -A5

Length of output: 656


No changes needed: process is correctly mapped to no_process
Runner.init already inverts run_opts['process'] into self.no_process:

  • In secator/runners/_base.py line 117:
    self.no_process = not self.run_opts.get('process', True)
  • The execute() method’s injection of kwargs['process'] flows through run_opts into that mapping, so downstream checks of self.no_process behave as intended.

You can safely ignore the suggested diff.

Likely an incorrect or invalid review comment.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
secator/cli.py (2)

693-707: ⚠️ Potential issue

Fix mutable default argument and shadowed built-in

The function uses a mutable default argument (fields=[]) which can lead to unexpected behavior if the function is called multiple times. Additionally, the variable type shadows the built-in Python function.

Apply this fix to address both issues:

-def process_query(query, fields=[]):
+def process_query(query, fields=None):
     otypes = [o.__name__.lower() for o in FINDING_TYPES]
     extractors = []
 
-    # Process fields
+    # Process fields
+    if fields is None:
+        fields = []
     fields_filter = {}
     if fields:
         for field in fields:
             parts = field.split('.')
             if len(parts) == 2:
-                type, field = parts
+                obj_type, field = parts
             else:
-                type = parts[0]
+                obj_type = parts[0]
                 field = None
-            if type not in otypes:
-                console.print(Error(message='Invalid output type: ' + type))
+            if obj_type not in otypes:
+                console.print(Error(message='Invalid output type: ' + obj_type))
                 sys.exit(1)
-            fields_filter[type] = field
+            fields_filter[obj_type] = field
🧰 Tools
🪛 Ruff (0.11.9)

693-693: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


719-735: ⚠️ Potential issue

Improve operator inference in process_query

The current logic silently falls back to || when no operator is present and gives precedence to && when both are present, which could lead to unexpected behavior.

Implement a more explicit parsing approach:

-    operator = '&&' if '&&' in query else '||'
+    if '&&' in query and '||' in query:
+        console.print(Error(message='Cannot mix && and || in the same query'))
+        sys.exit(1)
+    elif '&&' in query:
+        operator = '&&'
+    elif '||' in query:
+        operator = '||'
+    else:
+        operator = '&&'  # sensible default
🧹 Nitpick comments (4)
.github/workflows/tests.yml (1)

119-119: Update the GitHub Action checkout version

The runner for "actions/checkout@v3" is considered outdated. GitHub recommends using the latest versions of actions for security and functionality improvements.

-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
🧰 Tools
🪛 actionlint (1.7.7)

119-119: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

tests/template/test_templates.py (2)

64-78: Consider removing or completing commented-out test methods

These commented-out test methods (test_runner_configs) might be intended for future use, but leaving incomplete code in production can lead to confusion. Either complete and uncomment these methods or remove them if they're no longer needed.


79-101: Consider removing or completing commented-out test methods

Similar to the previous comment, these commented-out test methods (test_runner_dynamic) should either be completed and uncommented or removed to maintain code clarity.

secator/cli.py (1)

767-767: Remove unused loop variable

The loop variable ix is not used in the loop body, which could cause confusion.

-        for ix, extractor in enumerate(extractors):
+        for extractor in extractors:
             console.print(f':zap: [bold blue]{extractor["type"].title()}[/] [bold orange3]->[/] [bold green]{extractor["condition"]}[/]', highlight=False)  # noqa: E501
🧰 Tools
🪛 Ruff (0.11.9)

767-767: Loop control variable ix not used within loop body

(B007)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b791e9 and eafa3cb.

📒 Files selected for processing (3)
  • .github/workflows/tests.yml (1 hunks)
  • secator/cli.py (18 hunks)
  • tests/template/test_templates.py (1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.7)
.github/workflows/tests.yml

119-119: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 Ruff (0.11.9)
secator/cli.py

693-693: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


767-767: Loop control variable ix not used within loop body

(B007)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (15)
.github/workflows/tests.yml (1)

110-147: Good addition of the template test job to CI workflow

The new template job follows good practices by using the same matrix configuration as the integration job and properly sets up the environment with all necessary dependencies. The reuse of setup steps and consistent structure with other jobs shows attention to maintainability.

The addition aligns well with the new template test command in the CLI and helps ensure template integrity through automated testing.

🧰 Tools
🪛 actionlint (1.7.7)

119-119: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

tests/template/test_templates.py (1)

1-101: Well-structured template tests with good isolation

The test module is designed well with:

  • Proper use of subtests to isolate each test case
  • Redirection of stdout/stderr to prevent test output pollution
  • Consistent testing pattern across different template types
  • Appropriate dry-run mode to verify template integrity without execution
  • Good coverage of both regular and dynamically imported templates

This systematic approach ensures all templates can be instantiated and executed without errors, which is crucial for validating the new condition-based execution feature.

secator/cli.py (13)

20-21: Good module reorganization

Moving the OrderedGroup class to a dedicated click.py file improves code organization and separation of concerns, aligning with the PR's objective of "extracting Click-related functionality into a dedicated file".


73-75: Improved CLI structure with consistent aliasing

The addition of consistent aliases (x, t, tasks) for the task command improves usability by providing both short-form aliases for power users and descriptive aliases for better discoverability.


90-92: Enhanced workflow command with OrderedGroup and aliases

Using OrderedGroup for the workflow command ensures consistent command ordering in help output, while adding aliases (w, workflows) provides better usability. This is a good application of the refactored CLI infrastructure.


533- A93C 534: Consistent aliasing for workspace command

Adding consistent aliases (ws, workspaces) to the workspace command maintains the same aliasing pattern used throughout the CLI, enhancing user experience through consistency.


570-587: Well-implemented profiles group

The new profiles group with its list command provides a clean way to explore available profiles, showing relevant information (name, description, options) in a well-formatted table. This enhances usability for profile management.


593-680: Comprehensive alias management functionality

The new alias group with commands for enabling, disabling, and listing aliases provides powerful functionality for users to customize their experience. The implementation is thorough, generating appropriate shell commands and providing clear instructions for users.


813-816: Improved UX with clearer loading indicators

The enhanced loading indicators with progress tracking ([1/n]) and result counts provide better user feedback during report loading, especially important for unified reports that might process many files.


841-843: Enhanced report building feedback

The improved feedback during report building provides users with clear expectations about potentially time-consuming operations, enhancing the overall user experience.


868-873: Good CLI adaptation for piped output

Adding support for piped output in the report_list command enhances CLI interoperability, allowing the command to be used in shell pipelines for further processing.


957-964: Improved health command with aliases

Adding aliases (h) to the health command improves command discoverability and usability, consistent with the aliasing pattern used throughout the CLI.


1124-1127: Improved install command with aliases

Adding aliases (i) to the install command and improving the help text enhances command usability and maintains consistency with the aliasing pattern used throughout the CLI.


1498-1527: Well-implemented template test command

The new template test command provides a comprehensive way to test all templates systematically. It's well-integrated with the existing test infrastructure and provides appropriate environment setup and coverage tracking.


1706-1709: Enhanced coverage command with template support

Adding the --template-only option to the coverage command provides a clean way to focus on template test coverage, complementing the existing unit and integration options.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
secator/runners/command.py (2)

215-217: self.chunk may still be undefined
A previous review already raised this; the code still references self.chunk before guaranteeing it exists, risking AttributeError if Runner.__init__ does not initialise it.

Please ensure self.chunk is always set (e.g. self.chunk = getattr(self, "chunk", False) in Runner.__init__).


237-242: ⚠️ Potential issue

Mutable default argument results=[]

Using a list as a default argument shares the same list between calls (classic Python pitfall ‑ Ruff B006). Replace with None and initialise inside:

-    def si(cls, *args, results=[], **kwargs):
+    def si(cls, *args, results=None, **kwargs):
         # TODO: Move this to TaskBase
         from secator.celery import run_command
-        profile = cls.profile(kwargs) if callable(cls.profile) else cls.profile
-        return run_command.si(results, cls.__name__, *args, opts=kwargs).set(queue=profile)
+        profile = cls.profile(kwargs) if callable(cls.profile) else cls.profile
+        return run_command.si(results or [], cls.__name__, *args, opts=kwargs).set(queue=profile)

Prevents hard-to-trace state leakage between tasks.

🧰 Tools
🪛 Ruff (0.11.9)

237-237: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

secator/cli.py (2)

693-707: ⚠️ Potential issue

Fix mutable default argument and shadowed built-in

The function uses a mutable default argument (fields=[]) which can cause unexpected behavior if called multiple times, and shadows the built-in type function.

Apply this fix:

- def process_query(query, fields=[]):
+ def process_query(query, fields=None):
+    if fields is None:
+        fields = []
     otypes = [o.__name__.lower() for o in FINDING_TYPES]
     extractors = []

     # Process fields
     fields_filter = {}
     if fields:
         for field in fields:
             parts = field.split('.')
             if len(parts) == 2:
-                type, field = parts
+                obj_type, field = parts
             else:
-                type = parts[0]
+                obj_type = parts[0]
                 field = None
-            if type not in otypes:
-                console.print(Error(message='Invalid output type: ' + type))
+            if obj_type not in otypes:
+                console.print(Error(message='Invalid output type: ' + obj_type))
                 sys.exit(1)
-            fields_filter[type] = field
+            fields_filter[obj_type] = field
🧰 Tools
🪛 Ruff (0.11.9)

693-693: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


718-735: ⚠️ Potential issue

Improve operator inference with explicit error handling

The current operator inference logic silently falls back to || when no operator is present and gives precedence to && when both are present, which could lead to unexpected behavior.

Consider implementing explicit parsing with error handling:

- operator = '&&' if '&&' in query else '||'
+ if '&&' in query and '||' in query:
+     console.print(Error(message='Cannot mix && and || in the same query'))
+     sys.exit(1)
+ elif '&&' in query:
+     operator = '&&'
+ elif '||' in query:
+     operator = '||'
+ else:
+     operator = '&&'  # sensible default - consider what makes most sense for your use case
🧹 Nitpick comments (5)
secator/utils.py (1)

430-444: Simplify equality checks and remove double-negation

The current guard

if not DEBUG == ['all'] and not DEBUG == ['1']:

adds an unnecessary cognitive load and was flagged by Ruff (SIM201).
A more idiomatic and readable alternative is to use !=:

-if not DEBUG == ['all'] and not DEBUG == ['1']:
+if DEBUG not in (['all'], ['1']):

This keeps the exact same semantics while improving clarity and avoids the double negation pattern that static-analysis tools complain about.
No functional impact, just readability.

🧰 Tools
🪛 Ruff (0.11.9)

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)


436-439: Combine if branches using logical or operator

Combine if branches

(SIM114)


438-441: Combine if branches using logical or operator

Combine if branches

(SIM114)

tests/unit/test_cli.py (2)

8-9: Remove unused imports

PackageInstaller and SourceInstaller are imported but never used, triggering Ruff F401 and unnecessarily increasing module load time.

-from secator.installer import InstallerStatus, PackageInstaller, SourceInstaller
+from secator.installer import InstallerStatus

No behaviour changes; just a cleanup.

🧰 Tools
🪛 Ruff (0.11.9)

8-8: secator.installer.PackageInstaller imported but unused

Remove unused import

(F401)


8-8: secator.installer.SourceInstaller imported but unused

Remove unused import

(F401)


88-102: Collapse nested with statements for brevity

The inner nesting can be flattened for better readability (Ruff SIM117):

-with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}):
-    with mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)):
-        result = self.runner.invoke(cli, ['util', 'build'])
+with (
+    mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}),
+    mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)),
+):
+    result = self.runner.invoke(cli, ['util', 'build'])

Apply the same pattern to the analogous block at lines 96-102 and 203-206.

🧰 Tools
🪛 Ruff (0.11.9)

88-89: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


96-97: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)

secator/cli.py (2)

920-924: Consider using ternary operator for cleaner code

The if-else block for setting workspace_name could be simplified using a ternary operator.

- split = json_path.split('/')
- if len(split) > 4:
-     workspace_name = '/'.join(split[:-4])
- else:
-     workspace_name = '_default'
+ split = json_path.split('/')
+ workspace_name = '/'.join(split[:-4]) if len(split) > 4 else '_default'
🧰 Tools
🪛 Ruff (0.11.9)

920-923: Use ternary operator workspace_name = '/'.join(split[:-4]) if len(split) > 4 else '_default' instead of if-else-block

Replace if-else-block with workspace_name = '/'.join(split[:-4]) if len(split) > 4 else '_default'

(SIM108)


767-767: Unused loop index variable

The loop index variable ix is not used within the loop body. You can use an underscore _ to indicate an unused variable.

- for ix, extractor in enumerate(extractors):
+ for _, extractor in enumerate(extractors):

Alternatively, if you plan to use the index in the future, add a comment explaining its intended purpose.

🧰 Tools
🪛 Ruff (0.11.9)

767-767: Loop control variable ix not used within loop body

(B007)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eafa3cb and 7019a46.

📒 Files selected for processing (4)
  • secator/cli.py (24 hunks)
  • secator/runners/command.py (17 hunks)
  • secator/utils.py (4 hunks)
  • tests/unit/test_cli.py (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
secator/cli.py (13)
secator/click.py (3)
  • OrderedGroup (21-85)
  • group (56-82)
  • command (26-54)
secator/decorators.py (2)
  • register_runner (224-421)
  • task (42-46)
secator/serializers/dataclass.py (2)
  • default (8-16)
  • loads_dataclass (38-39)
secator/output_types/error.py (1)
  • Error (8-43)
secator/report.py (2)
  • is_empty (122-123)
  • send (48-56)
secator/exporters/console.py (1)
  • send (6-10)
secator/exporters/txt.py (1)
  • send (7-28)
secator/exporters/csv.py (1)
  • send (12-37)
secator/exporters/table.py (1)
  • send (9-35)
secator/output_types/info.py (1)
  • Info (8-24)
secator/utils.py (1)
  • debug (428-465)
secator/installer.py (4)
  • install (60-110)
  • install (126-169)
  • install (176-227)
  • install (234-257)
secator/runners/command.py (2)
  • Command (28-993)
  • execute (308-333)
🪛 Ruff (0.11.9)
tests/unit/test_cli.py

8-8: secator.installer.PackageInstaller imported but unused

Remove unused import

(F401)


8-8: secator.installer.SourceInstaller imported but unused

Remove unused import

(F401)


88-89: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


96-97: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


203-204: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)

secator/cli.py

693-693: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


767-767: Loop control variable ix not used within loop body

(B007)


920-923: Use ternary operator workspace_name = '/'.join(split[:-4]) if len(split) > 4 else '_default' instead of if-else-block

Replace if-else-block with workspace_name = '/'.join(split[:-4]) if len(split) > 4 else '_default'

(SIM108)

secator/runners/command.py

237-237: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

secator/utils.py

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (18)
tests/unit/test_cli.py (1)

184-192: Temporary file not removed

tmp_report.json is created but never deleted, which will pollute the project root when the suite is run locally or in CI.

Consider tempfile.NamedTemporaryFile(delete=False) or tmp_path from pytest, or explicitly os.remove('tmp_report.json') in a finally block.

secator/cli.py (17)

19-20: Good refactoring of CLI-related code

Moving the OrderedGroup class to a dedicated secator/click.py file is a good example of separation of concerns and aligns with the PR objective of extracting Click-related functionality into a dedicated module.


73-75: Improved CLI usability with consistent aliasing

Adding multiple aliases (x, t, tasks) to the task command group enhances usability and provides flexibility for users. This is consistent with the PR objectives of improved command organization and consistent aliases.


90-95: Enhanced workflow group with OrderedGroup and aliases

Using the OrderedGroup class for the workflow group and adding aliases improves command organization and makes the CLI more intuitive. The aliases w and workflows provide consistent naming conventions across the CLI.


533-536: Consistent aliasing convention for workspace group

Adding aliases ws and workspaces to the workspace group maintains the consistent naming pattern established with other command groups, improving overall CLI usability.


569-587: Well-structured profile command group

The addition of a dedicated profile command group with aliases improves organization of CLI commands. The implementation of the list command with a clean tabular output makes it easy for users to view available profiles and their configurations.


589-680: Comprehensive alias management system

The new alias group with enable, disable, and list commands provides a well-structured way for users to manage shell aliases. The implementation includes:

  1. Generation of shell script files for enabling/disabling aliases
  2. Helpful instructions on how to use the generated files
  3. Comprehensive listing of all available aliases with clear formatting

This significantly improves user experience by making it easier to work with Secator commands from the shell.


756-760: Good support for piped input in report show command

Adding support for piped input in the report_show command improves CLI interoperability by allowing the use of the command in shell pipelines. Setting unified=True in this case is a sensible default that enhances usability.


784-786: Improved report query handling

The enhanced report query handling with fuzzy matching logic improves user experience by allowing more flexible query inputs and providing better error handling when queries don't match.


813-816: Enhanced report loading feedback

The improved reporting during unified report loading provides better user feedback, especially for large reports, by showing progress information and result counts. This enhances the user experience for complex operations.


841-843: Improved user feedback for report processing

Adding informative messages about the report building process with the number of results being processed and a friendly warning about potential processing time improves the user experience.


868-876: Effective piped output handling in report list

Adding support for piped output in the report_list command improves shell interoperability, allowing the output to be easily consumed by other commands. The early return pattern after printing paths ensures efficient processing.


906-908: Consistent error handling for report list

Using Error output type for the "No reports found" message instead of a plain console print ensures consistent error formatting across the application, improving user experience.


965-971: Expanded health command with alias

Adding the h alias to the health command improves usability with a short, memorable command alternative. The command docstring is now properly aligned with the rest of the codebase style.


1132-1135: Improved install command with alias

Adding the i alias to the install command and clarifying its purpose in the docstring improves usability and makes the command easier to discover and use.


1402-1409: Improved test command error handling

The enhanced error handling for the test command with clear error messages and early exit using sys.exit(1) ensures users receive proper feedback when attempting to run tests without the required development environment.


1506-1535: Well-structured template testing command

The new template test command follows the same pattern as other test commands, promoting consistency in the codebase. It properly sets up the test environment variables and handles test selection based on input options, enhancing the testing capabilities of the framework.


1716-1728: Expanded coverage reporting options

Adding the --template-only option to the coverage command enhances the test reporting capabilities by allowing users to focus specifically on template test coverage, which complements the newly added template test command.

ocervell and others added 2 commits May 22, 2025 11:55
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
secator/utils.py (1)

527-529: Fixed property mismatch in domain validation.

Great! This change addresses the previous review comment about a potential security issue where validation used top_domain_under_public_suffix but returned registered_domain. The code now consistently uses top_domain_under_public_suffix for both validation and return.

🧹 Nitpick comments (1)
secator/utils.py (1)

430-431: Simplify boolean expression with != operator.

The current expressions can be simplified for better readability.

-	if not DEBUG == ['all'] and not DEBUG == ['1']:
+	if DEBUG != ['all'] and DEBUG != ['1']:
🧰 Tools
🪛 Ruff (0.11.9)

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7019a46 and 6bdbbc9.

📒 Files selected for processing (1)
  • secator/utils.py (4 hunks)
🧰 Additional context used
🪛 Ruff (0.11.9)
secator/utils.py

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (4)
secator/utils.py (4)

29-29: Import updated to align with new debug configuration approach.

The change from DEBUG_COMPONENT to DEBUG aligns with the PR's objective of making debug configuration string-based for finer control, as mentioned in the PR summary.


422-422: Improved debug output styling.

Changed styling from dim cyan/yellow/green to bold blue/yellow/blue for better readability of debug output.


435-436: Updated debug component iteration.

The code now correctly iterates over the new string-based debug configuration values.


464-465: Updated rich markup exception handling.

The condition now correctly checks for 'rich' in the new DEBUG list.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
.github/workflows/tests.yml (1)

1-3: 🛠️ Refactor suggestion

Add a minimal permissions block to tighten GITHUB_TOKEN scope

The workflow currently defaults to full GITHUB_TOKEN permissions. For security best practices, explicitly define a minimal permissions block (e.g., contents: read) at the top level to avoid over-permission.

< 10000 /details>
♻️ Duplicate comments (1)
.github/workflows/tests.yml (1)

150-150: Confirm inclusion of template in coverage dependencies

This correctly updates the coverage job’s needs array to include the template job, ensuring template tests are factored into the aggregated coverage report.

🧹 Nitpick comments (1)
.github/workflows/tests.yml (1)

110-147: DRY up common setup steps in the template job

The template job duplicates the same sequence of steps from the integration job (checkout, install, Docker Compose setup, PATH exports). Consider extracting these into a reusable workflow or composite action (or using YAML anchors) to reduce maintenance overhead and keep the CI DRY.

🧰 Tools
🪛 actionlint (1.7.7)

119-119: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bdbbc9 and 76348c7.

📒 Files selected for processing (1)
  • .github/workflows/tests.yml (1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.7)
.github/workflows/tests.yml

119-119: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 GitHub Check: CodeQL
.github/workflows/tests.yml

[warning] 111-148: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (1)
.github/workflows/tests.yml (1)

118-119: Skip updating actions/checkout@v3

The actionlint warning about actions/checkout@v3 being “too old” is a false positive; v3 is the current stable branch. No changes are needed here.

🧰 Tools
🪛 actionlint (1.7.7)

119-119: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
secator/decorators.py (1)

303-326: ⚠️ Potential issue

Remove duplicated version check code

There's an exact duplicate of the version checking code block (lines 303-313 and 315-325). This is likely an oversight and could cause confusion during maintenance.

 		# Show version
 		if version:
 			data = task_cls.get_version_info()
 			current = data['version']
 			latest = data['latest_version']
 			installed = data['installed']
 			if not installed:
 				console.print(f'[bold red]{task_cls.__name__} is not installed.[/]')
 			else:
 				console.print(f'{task_cls.__name__} version: [bold green]{current}[/] (recommended: [bold green]{latest}[/])')
 			sys.exit(0)

-		# Show version
-		if version:
-			data = task_cls.get_version_info()
-			current = data['version']
-			latest = data['latest_version']
-			installed = data['installed']
-			if not installed:
-				console.print(f'[bold red]{task_cls.__name__} is not installed.[/]')
-			else:
-				console.print(f'{task_cls.__name__} version: [bold green]{current}[/] (recommended: [bold green]{latest}[/])')
-			sys.exit(0)
🧹 Nitpick comments (1)
secator/decorators.py (1)

339-345: Consider removing or documenting commented code

There's a block of commented code related to removing default-valued options. Either remove it if it's no longer needed or add a comment explaining why it's kept and when it might be uncommented.

-		# Remove options whose values are default values
-		# for k, v in options.items():
-		# 	if k in config.options.toDict():
-		# 		continue
-		# 	opt_name = k.replace('-', '_')
-		# 	if opt_name in opts and opts[opt_name] == v.get('default', None):
-		# 		del opts[opt_name]
+		# TODO: Remove this commented code block or document why it's kept
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76348c7 and d2d5119.

📒 Files selected for processing (1)
  • secator/decorators.py (12 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (9)
secator/decorators.py (9)

25-26: Implementation properly addresses the previous flag/description mismatch

The process flag has been updated to fix the previous issue by changing the help text to "Enable secator processing" and adding reverse=True, which properly aligns the flag behavior with its description. This maintains backward compatibility while making the flag's purpose clearer to users.


30-30: Good addition of the tree visualization feature

The new tree flag allows users to visualize the runner tree structure, which enhances debugging and understanding of complex workflows. This is a useful usability improvement.


43-48: Clean task decorator implementation

The new task decorator provides a simple and clear way to mark classes as tasks, which improves code organization and makes task registration more explicit.


50-89: Improved option decoration with better handling of flag opposites

The refactored decorate_command_options function now properly handles option flags with opposites and reverse flags, including fixing the short option for opposites. This makes the CLI more intuitive for users who want to toggle options.


110-121: Good addition of recursive option merging from child workflows

The implementation now correctly merges options from child workflows, enabling better inheritance of configuration options through the workflow hierarchy. This enhances the flexibility of the configuration system.


185-186: Good fix for required flags defined in config

The code now correctly handles the case where a required option is defined in the YAML configuration, making it no longer required in the CLI. This prevents unnecessary input and improves user experience.


332-337: Well-implemented tree visualization feature

The implementation of the tree visualization feature is clean and self-contained. It builds the runner tree, renders it to the console, and exits, providing a nice user experience enhancement.


358-370: Good enhancement to driver tracking

The addition of tracking actual drivers in a separate list is a good improvement that allows for better reporting and validation of used drivers.


406-408: Enhanced print options for better debugging

The addition of more granular print options (print_profiles, print_start, print_end) gives users better control over the verbosity and content of the output, which is especially useful for debugging.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
secator/runners/_helpers.py (4)

7-7: Fix mutable default arguments

Using mutable objects ([] and {}) as default parameters is dangerous as they are shared across function calls, which can lead to unexpected state leakage.

-def run_extractors(results, opts, inputs=[], ctx={}, dry_run=False):
+def run_extractors(results, opts, inputs=None, ctx=None, dry_run=False):
+    if inputs is None:
+        inputs = []
+    if ctx is None:
+        ctx = {}
🧰 Tools
🪛 Ruff (0.11.9)

7-7: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


7-7: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


65-65: Fix mutable default argument

-def extract_from_results(results, extractors, ctx={}):
+def extract_from_results(results, extractors, ctx=None):
+    if ctx is None:
+        ctx = {}
🧰 Tools
🪛 Ruff (0.11.9)

65-65: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


115-115: Fix mutable default argument

-def process_extractor(results, extractor, ctx={}):
+def process_extractor(results, extractor, ctx=None):
+    if ctx is None:
+        ctx = {}
🧰 Tools
🪛 Ruff (0.11.9)

115-115: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


139-147: 💡 Verification agent

🧩 Analysis chain

Security vulnerability with eval()

While you've restricted the builtins to only include len(), the current implementation of eval() is still potentially unsafe. A more robust approach would be to use a dedicated expression evaluation library.

-			safe_globals = {'__builtins__': {'len': len}}
-			eval_result = eval(_condition, safe_globals, ctx)
+			# Option 1: Use simpleeval library
+			from simpleeval import SimpleEval
+			evaluator = SimpleEval(names=ctx, functions={"len": len})
+			eval_result = evaluator.eval(_condition)
+
+			# Option 2: Use asteval library
+			# from asteval import Interpreter
+			# interpreter = Interpreter(usersyms=ctx, minimal=True)
+			# interpreter.symtable['len'] = len
+			# eval_result = interpreter.eval(_condition)

Consider using simpleeval, asteval, or a similar library designed for safe expression evaluation to prevent arbitrary code execution.


🌐 Web query:

Python simpleeval vs asteval for safe expression evaluation

💡 Result:

When choosing between simpleeval and asteval for safe expression evaluation in Python, here's a comparison to help you decide which might best suit your needs:

simpleeval

  • Purpose: A minimalistic and safe expression evaluator that evaluates simple Python expressions.
  • Safety: Designed to be safe and prevent execution of arbitrary code. It only evaluates expressions, not statements.
  • Features: Supports arithmetic operations, comparisons, boolean logic, and basic math functions. It allows adding custom functions and names.
  • Ease of Use: Very straightforward API, easy to integrate and use.
  • Limitations: Not designed for complex code or statements, only expressions.
  • Use Case: Ideal when you want to evaluate simple, user-generated expressions safely without the risk of code injection.

asteval

  • Purpose: A more comprehensive evaluator that interprets a subset of Python's abstract syntax tree (AST).
  • Safety: More flexible and powerful but tries to keep evaluation safe by limiting the accessible built-ins and modules. However, due to its complexity, it can potentially be less secure than simpleeval if not carefully managed.
  • Features: Supports assignment, function calls, loops, and more complex Python constructs beyond simple expressions.
  • Ease of Use: More powerful but more complex API. Requires careful setup to maintain security.
  • Limitations: Larger attack surface due to broader feature set; security needs more attention.
  • Use Case: Suitable for scenarios needing more than just expressions—where small scripts or more complex logic need safe evaluation.

Summary

  • Use simpleeval if you need to safely evaluate user-defined expressions with a clean, minimal API and strong security.
  • Use asteval if you need to evaluate more complex code snippets safely but be prepared to handle security carefully.

If you want, I can provide simple example codes for both to demonstrate their usage. Would you like that?


Replace eval() with simpleeval for safe, minimal expression evaluation

The current eval(_condition, safe_globals, ctx)—even with restricted builtins—can still allow attribute traversal on objects in ctx and poses a security risk. Since you only need to evaluate simple boolean and comparison expressions, switch to simpleeval which is designed for this use case.

Files to update:

  • secator/runners/_helpers.py (around lines 139–147)

Suggested changes:

--- a/secator/runners/_helpers.py
+++ b/secator/runners/_helpers.py
@@ -1,3 +1,4 @@
+from simpleeval import SimpleEval
 import ...

@@ -135,15 +136,17 @@ def _apply_condition(results, _condition, _type, debug):
             tmp_results = []
-            ctx['item'] = item
-            ctx[f'{_type}'] = item
-            safe_globals = {'__builtins__': {'len': len}}
-            eval_result = eval(_condition, safe_globals, ctx)
+            # inject the current item into the evaluation context
+            ctx['item'] = item
+            ctx[f'{_type}'] = item
+
+            # evaluate condition safely using simpleeval
+            evaluator = SimpleEval(names=ctx, functions={'len': len})
+            eval_result = evaluator.eval(_condition)
+
             if eval_result:
                 tmp_results.append(item)
             del ctx['item']
             del ctx[f'{_type}']
-        debug(f'kept {len(tmp_results)} out of {len(results)} items after condition [bold]{_condition}[/bold]', sub='extractor')
+        debug(
+            f'kept {len(tmp_results)} out of {len(results)} items after condition [bold]{_condition}[/bold]',
+            sub='extractor'
+        )

• Move the SimpleEval import to the module level for clarity and performance.
• Remove the manual safe_globals and eval() call entirely.
simpleeval restricts operations to expressions only, eliminating risks of arbitrary code execution—perfect for user‐provided filter logic.

🧹 Nitpick comments (2)
secator/runners/_helpers.py (1)

137-137: Use != operator instead of not ==

-			if not item._type == _type:
+			if item._type != _type:

Using != is more idiomatic and clearer than not ==.

🧰 Tools
🪛 Ruff (0.11.9)

137-137: Use item._type != _type instead of not item._type == _type

Replace with != operator

(SIM201)

tests/unit/test_runners_helpers.py (1)

136-161: TODO comment needs addressing

There's a TODO comment about testing nested field access. Consider implementing this test to ensure complete coverage of the field formatting functionality.

def test_process_extractor_with_nested_field(self):
    """Test process_extractor with nested field access."""
    extractor = {
        'type': 'mock',
        'field': '{nested.subfield}'
    }
    result = process_extractor(self.results, extractor)
    self.assertEqual(result, ['nested_value', 'nested_value', 'nested_value'])

Would you like me to implement the commented-out test for nested field access?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2d5119 and 7a7c037.

📒 Files selected for processing (7)
  • secator/configs/workflows/cidr_recon.yaml (1 hunks)
  • secator/configs/workflows/url_crawl.yaml (1 hunks)
  • secator/configs/workflows/url_dirsearch.yaml (2 hunks)
  • secator/configs/workflows/url_fuzz.yaml (2 hunks)
  • secator/decorators.py (12 hunks)
  • secator/runners/_helpers.py (3 hunks)
  • tests/unit/test_runners_helpers.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • secator/configs/workflows/url_dirsearch.yaml
  • secator/configs/workflows/cidr_recon.yaml
  • secator/configs/workflows/url_fuzz.yaml
  • secator/configs/workflows/url_crawl.yaml
  • secator/decorators.py
🧰 Additional context used
🪛 Ruff (0.11.9)
secator/runners/_helpers.py

7-7: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


7-7: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


65-65: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


115-115: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


137-137: Use item._type != _type instead of not item._type == _type

Replace with != operator

(SIM201)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (16)
secator/runners/_helpers.py (7)

26-27: LGTM: Good context tracking enhancement

Tracking the current key in the context and passing it to downstream functions improves state management and enables better debug logging.


30-32: LGTM: Improved dry run formatting and context management

Nice improvement to use fmt_extractor for more descriptive placeholder values in dry run mode, and storing computed inputs in the context makes them available to downstream processors.


46-63: LGTM: Well-designed formatter function

The new fmt_extractor function provides a clean way to format extractors for display, handling both string and dict formats with proper condition representation.


76-87: LGTM: Improved results collection

Using extend instead of individual append operations is a cleaner approach for collecting results from multiple extractors.


90-113: LGTM: Clean extractor parsing logic

Good refactoring to extract the parsing logic into its own function, which improves code organization and reusability.


125-131: LGTM: Good debug logging and parsing

Adding debug logging with context details and using the new parser function improves code readability and diagnostics.


148-157: LGTM: Improved filtering and debug logging

Good implementation of type filtering and field formatting with helpful debug logging.

tests/unit/test_runners_helpers.py (9)

1-50: LGTM: Well-structured test setup

Good test class organization with a comprehensive setup method that creates various mock objects for testing different scenarios.


52-64: LGTM: Thorough string extractor parsing tests

The tests cover valid, invalid, and empty string formats for extractor parsing, which ensures robustness.


66-83: LGTM: Complete dict extractor parsing tests

Good test coverage for dictionary-based extractors with both full and minimal field sets.


84-102: LGTM: Comprehensive formatter tests

The tests verify formatter behavior for string and dictionary extractors, as well as invalid formats.


103-114: LGTM: Good type filtering tests

Thorough verification of type-based filtering for both mock and URL objects.


115-135: LGTM: Comprehensive condition testing

Tests cover both simple conditions and more complex ones using functions like len().


162-182: LGTM: Good error handling tests

The tests properly verify both successful extraction and error handling scenarios using mocking.


183-203: LGTM: Thorough run_extractors tests

Good tests for both normal and dry run modes, including verification of deduplication by mocking.


204-238: LGTM: Comprehensive folder ID tests

The tests cover all important scenarios for task folder ID generation, including non-existent paths, empty directories, and directories with numeric names.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
tests/unit/test_runners_helpers.py (1)

187-192: Use a temporary file to avoid polluting the project root

Creating tmp_report.json in the repo root may leave garbage if the test crashes and also forces the test runner to have write permissions on the repository.
Switching to tempfile.NamedTemporaryFile() (or patching open) keeps the FS clean and makes the test less brittle.

tests/unit/test_cli.py (3)

88-93: Collapse nested with statements for better readability

Ruff SIM117 flags the nested context-managers. A single with line is shorter and easier to read:

-		with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}):
-			with mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)):
-				result = self.runner.invoke(cli, ['util', 'build'])
+		with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}), \
+		     mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)):
+			result = self.runner.invoke(cli, ['util', 'build'])
🧰 Tools
🪛 Ruff (0.11.9)

88-89: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


96-101: Same comment as above – merge the nested with blocks

This will satisfy SIM117 and keep the style consistent across the module.

🧰 Tools
🪛 Ruff (0.11.9)

96-97: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


187-191: Clean up the temporary file created during the test

After the report show assertion succeeds, delete tmp_report.json (or use tempfile.NamedTemporaryFile) so repeated test runs don’t reuse stale artefacts.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a7c037 and fb56bdd.

📒 Files selected for processing (2)
  • tests/unit/test_cli.py (2 hunks)
  • tests/unit/test_runners_helpers.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/unit/test_cli.py (2)
secator/cli.py (1)
  • cli (54-66)
secator/installer.py (3)
  • InstallerStatus (29-44)
  • PackageInstaller (122-169)
  • SourceInstaller (172-227)
🪛 Ruff (0.11.9)
tests/unit/test_runners_helpers.py

12-12: secator.output_types.Error imported but unused

Remove unused import: secator.output_types.Error

(F401)

tests/unit/test_cli.py

8-8: secator.installer.PackageInstaller imported but unused

Remove unused import

(F401)


8-8: secator.installer.SourceInstaller imported but unused

Remove unused import

(F401)


88-89: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


96-97: Use a s 47C5 ingle with statement with multiple contexts instead of nested with statements

(SIM117)


203-204: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (1)
tests/unit/test_cli.py (1)

203-206: CLI option is probably --output, not -output

Most Click options are prefixed with a double dash (--).
If the CLI really expects --output or -o, the current call will raise No such option and the test will pass falsely because the command exits with code 2 (Click usage error) rather than executing the logic.

-				result = self.runner.invoke(cli, ['report', 'export', 'test.json', '-output', 'console'])
+				result = self.runner.invoke(cli, ['report', 'export', 'test.json', '--output', 'console'])

Please double-check the actual option name in secator.cli.report.export.

🧰 Tools
🪛 Ruff (0.11.9)

203-204: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)

ocervell and others added 7 commits May 22, 2025 19:31
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
secator/report.py (1)

107-107: ⚠️ Potential issue

data['info']['errors'] is overwritten – append instead to avoid silently dropping earlier errors

The current implementation overwrites previously collected errors instead of appending new ones. This could cause runner-level errors (collected at line 78) to be lost.

Apply this fix to preserve all errors:

-            if errors:
-                data['info']['errors'] = errors
+            if errors:
+                # keep any previously recorded errors
+                data['info'].setdefault('errors', []).extend(errors)
secator/tree.py (1)

95-97: ⚠️ Potential issue

Guard against non-mapping sub-tasks

subtask_details may be None or a bool if the user disables the task in YAML, which would cause an AttributeError when calling .get().

Apply this fix:

-                    condition = subtask_details.get('if')
+                    condition = (
+                        subtask_details.get('if')
+                        if isinstance(subtask_details, dict)
+                        else None
+                    )
tests/unit/test_cli.py (1)

95-103: Improve nested context managers

Multiple nested with statements reduce code readability and maintainability.

Consider combining the nested with statements for better readability:

-with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}):
-    with mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)):
-        with mock.patch.dict(os.environ, {'HATCH_INDEX_AUTH': 'test_token'}):
+with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}), \
+     mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)), \
+     mock.patch.dict(os.environ, {'HATCH_INDEX_AUTH': 'test_token'}):
🧰 Tools
🪛 Ruff (0.11.9)

97-98: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)

🧹 Nitpick comments (4)
tests/unit/test_runners_helpers.py (1)

177-183: Implement commented-out error handling test

The test for error handling during extraction is commented out. Consider implementing this test to ensure error handling works correctly.

Uncomment and implement the error handling test to ensure robust error capture and reporting in the extraction process.

secator/runners/_helpers.py (1)

121-166: Security improvement in condition evaluation

The condition evaluation now uses a restricted __builtins__ dictionary that only includes the len function, significantly reducing the risk of arbitrary code execution through YAML-supplied conditions.

Good security practice with the restricted globals. Consider adding a docstring comment highlighting this security feature for future maintainers.

tests/unit/test_cli.py (2)

87-94: Improve nested context managers

Multiple nested with statements reduce code readability and maintainability.

Consider combining the nested with statements for better readability:

-with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}):
-    with mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)):
+with mock.patch('secator.cli.ADDONS_ENABLED', {'build': True}), \
+     mock.patch('secator.cli.Command.execute', return_value=mock.MagicMock(return_code=0)):
🧰 Tools
🪛 Ruff (0.11.9)

89-90: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


204-211: Improve multi-line context managers

Using backslash line continuation makes the code less readable and more error-prone.

Consider using parentheses to group the context managers for better readability:

-with mock.patch('builtins.open', mock.mock_open(read_data='{"info":{"name":"test", "title": "test"}, "results":{}}')), \
-     mock.patch('secator.cli.loads_dataclass', return_value={"info": {"name": "test", "title": "test"}, "results": {}}):
+with (
+    mock.patch('builtins.open', mock.mock_open(read_data='{"info":{"name":"test", "title": "test"}, "results":{}}')),
+    mock.patch('secator.cli.loads_dataclass', return_value={"info": {"name": "test", "title": "test"}, "results": {}})
+):
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb56bdd and a8fe9f8.

📒 Files selected for processing (15)
  • secator/celery.py (3 hunks)
  • secator/cli.py (24 hunks)
  • secator/click.py (1 hunks)
  • secator/configs/workflows/wordpress.yaml (0 hunks)
  • secator/decorators.py (12 hunks)
  • secator/report.py (3 hunks)
  • secator/runners/_base.py (34 hunks)
  • secator/runners/_helpers.py (2 hunks)
  • secator/runners/command.py (17 hunks)
  • secator/tasks/ffuf.py (3 hunks)
  • secator/tree.py (1 hunks)
  • tests/integration/test_workflows.py (1 hunks)
  • tests/template/test_templates.py (1 hunks)
  • tests/unit/test_cli.py (2 hunks)
  • tests/unit/test_runners_helpers.py (1 hunks)
💤 Files with no reviewable changes (1)
  • secator/configs/workflows/wordpress.yaml
✅ Files skipped from review due to trivial changes (1)
  • tests/template/test_templates.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/integration/test_workflows.py
  • secator/tasks/ffuf.py
  • secator/celery.py
  • secator/click.py
  • secator/runners/command.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
secator/tree.py (1)
secator/template.py (1)
  • TemplateLoader (17-137)
🪛 Ruff (0.11.9)
tests/unit/test_runners_helpers.py

12-12: SyntaxError: Expected an identifier, but found a keyword 'from' that cannot be used here


12-12: SyntaxError: Simple statements must be separated by newlines or semicolons


12-12: SyntaxError: Simple statements must be separated by newlines or semicolons


13-13: SyntaxError: Expected an identifier, but found a keyword 'from' that cannot be used here


13-13: SyntaxError: Simple statements must be separated by newlines or semicolons


13-13: SyntaxError: Simple statements must be separated by newlines or semicolons

tests/unit/test_cli.py

89-90: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


97-98: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


188-188: Use a context manager for opening files

(SIM115)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (55)
secator/report.py (2)

90-94: Good addition of error handling for sorting operations

The try-except block for catching TypeError during sorting is a valuable addition that prevents the report generation from failing completely if items can't be sorted properly.


97-116: Enhanced extractor processing with type-aware grouping and logical operations

The new implementation significantly improves extractor processing by:

  1. Grouping extractors by type
  2. Supporting logical operations ('or'/'and') for combining results
  3. Applying proper deduplication

This provides greater flexibility and control over how extractors filter and combine results.

secator/tree.py (4)

4-10: Well-structured formatting options for tree visualization

The DEFAULT_RENDER_OPTS dictionary provides a clean, configurable way to define rendering styles for different node types using Rich formatting.


13-30: Clean implementation of TaskNode with proper typing

The TaskNode class provides a simple yet effective representation of workflow/scan task nodes with proper type annotations and a clear string representation.


109-110: Good defensive check for dictionary type

The proper guard to check if workflow_details is a dictionary before calling .get() is a good practice that prevents potential AttributeError exceptions.


44-71: Well-implemented tree rendering with proper recursion and formatting

The tree rendering logic elegantly handles:

  1. Recursive rendering of nested structures
  2. Proper indentation and branch characters
  3. Conditional expressions in nodes
  4. Custom formatting based on node types
tests/unit/test_runners_helpers.py (4)

12-13: Unused import removed

Good cleanup removing the unused Error import.

🧰 Tools
🪛 Ruff (0.11.9)

12-12: SyntaxError: Expected an identifier, but found a keyword 'from' that cannot be used here


12-12: SyntaxError: Simple statements must be separated by newlines or semicolons


12-12: SyntaxError: Simple statements must be separated by newlines or semicolons


13-13: SyntaxError: Expected an identifier, but found a keyword 'from' that cannot be used here


13-13: SyntaxError: Simple statements must be separated by newlines or semicolons


13-13: SyntaxError: Simple statements must be separated by newlines or semicolons


17-24: Well-designed mock class for testing

The MockOutputType class provides a clean test double with appropriate default values and nested structure for thorough testing of the extractor functions.


53-183: Comprehensive test coverage for extractor functions

The tests thoroughly validate all aspects of the extractor functionality:

  1. Parsing from strings and dictionaries
  2. Formatting for display
  3. Type filtering
  4. Conditional expressions
  5. Field formatting and extraction
  6. Error handling

This ensures the extractor code is robust and behaves as expected.


184-203: Thorough testing of the run_extractors function

The tests cover both normal extraction and dry-run mode, ensuring all aspects of the function work correctly including option updating and error handling.

secator/decorators.py (6)

25-25: Fixed flag/description mismatch for process option

The process flag now correctly uses reverse: True and the help text matches its behavior.


29-29: Good addition of tree visualization flag

The new tree flag enables users to visualize the task structure, which is a valuable feature for understanding complex workflows.


42-47: Clean implementation of task decorator

The task decorator provides a simple way to mark classes as tasks with minimal overhead.


124-126: Security improvement: removed eval in favor of explicit mapping

Replacing eval with an explicit type mapping dictionary eliminates a potential security risk while maintaining the same functionality.


322-325: Well-integrated tree visualization implementation

The tree visualization feature is cleanly integrated into the CLI command function, building and rendering the runner tree with proper exits.


387-389: Enhanced output control with additional print options

Adding print_profiles, print_start, and print_end options gives users more granular control over the output.

secator/runners/_helpers.py (6)

7-37: Great fix for mutable default arguments

The change from using mutable default arguments (inputs=[], ctx={}) to None with proper initialization inside the function prevents shared state bugs that can lead to unexpected behavior.

-def run_extractors(results, opts, inputs=[], ctx={}, dry_run=False):
+def run_extractors(results, opts, inputs=None, ctx=None, dry_run=False):
+    if inputs is None:
+        inputs = []
+    if ctx is None:
+        ctx = {}

30-37: Enhanced context-aware extraction

Adding the current key to the context and storing computed inputs under targets enables better state management between extractors and downstream processing.


34-36: Improved dry run output with formatted extractors

Using the new fmt_extractor function to format extractors in dry run mode provides more readable and informative output for debugging.


50-67: Well-designed formatter for extractor representation

This new helper function converts extractor definitions into human-readable string format, making debug output and dry run results more informative.


69-94: Context-aware extraction improvements

The updated function signature now accepts and passes a context parameter, enabling better state sharing between extractors and more informative debug output.


96-119: Good modularization with extracted parsing logic

Moving the parsing logic to a dedicated parse_extractor function improves code organization and maintainability. The function properly handles both dictionary and string extractor formats.

tests/unit/test_cli.py (4)

14-26: Good test setup with proper mocking

Mocking the installer methods prevents actual installation during tests, making the tests more reliable and faster.


27-30: Proper tearDown implementation

Adding tearDown to stop the mocks prevents test leakage that could affect other tests.


31-36: Enhanced version test assertion

The test now verifies the specific version string is in the output, making it more precise.


62-66: Better offline mode testing

The test now properly verifies that the command fails with the expected exit code and error message when in offline mode.

secator/runners/_base.py (10)

42-50: Good addition of colorized runner name formatting

Adding a helper function to format runner names with consistent colors based on their type improves visual cues in the output.


77-79: New profiles support

Adding profiles support enhances configuration management and reusability.


117-118: Improved process option handling

Converting the 'process' run option to 'no_process' with logical negation makes the code more intuitive.


133-134: Added print_profiles option

This new option allows controlling profile information visibility, enhancing flexibility in output control.


144-160: Enhanced debug logging for initialization

Adding detailed debug information with categorized subsystems improves observability and troubleshooting capabilities.


202-213: Good use of properties for option categorization

These properties provide a cleaner interface for accessing different categories of options, making the code more maintainable.


307-310: Helpful ID property

This property abstracts away the complexity of determining the runner ID from various context sources.


379-392: Improved context handling with DotMap

Using DotMap for the context provides more structured access to nested values and better integration with the extraction system.


774-787: Enhanced visual output with runner tree

The integration with build_runner_tree provides a more informative visual representation of runner hierarchies.


1023-1050: Improved profile resolution with feedback

The resolve_profiles method now returns loaded profiles and optionally provides user feedback about loaded profiles.

secator/cli.py (19)

73-78: Enhanced CLI with command aliases

Adding multiple aliases for the task command improves usability by providing shorter alternatives.


90-96: Ordered workflow commands with aliases

Using OrderedGroup and adding aliases improves the command organization and usability.


106-112: Consistent command aliasing pattern

Adding aliases to scan commands maintains a consistent pattern across the CLI.


207-241: Added explicit exit codes

Adding explicit sys.exit(1) calls ensures proper process termination on errors.


533-536: Consistent workspace command aliasing

Adding aliases for the workspace command maintains a consistent pattern across the CLI.


566-587: New profiles management commands

Adding a profile command group with list functionality improves configuration management capabilities.


589-680: New shell alias management commands

The alias command group provides a robust way to manage shell aliases for Secator commands, improving workflow integration.


687-688: Report command aliasing

Adding aliases for the report command maintains a consistent pattern across the CLI.


693-752: Well-designed query processing function

The new process_query function properly handles null defaults, explicitly checks for mixing operators, and provides clear error messages.


767-771: Improved piped input handling

The report_show command now intelligently handles piped input by reading from stdin and enabling unified mode.


795-797: Better report query handling

The command now properly handles comma-separated report queries and determines whether to load all reports based on path existence.


823-827: Enhanced user feedback during report loading

Adding detailed progress information makes the report loading process more transparent to users.


852-854: Improved feedback for unified report building

Adding detailed messaging with a progress indicator improves the user experience for potentially long-running operations.


879-887: Smart handling of piped output

The report_list command now detects piped output and adjusts its output format accordingly.


917-919: Better report count feedback

Adding an informational message about the number of reports found improves user feedback.


973-979: Health command aliasing

Adding an alias for the health command and improving the docstring enhances usability.


1140-1143: Install command aliasing

Adding an alias for the install command and improving the docstring maintains a consistent pattern across the CLI.


1514-1543: New template test command

Adding a dedicated command for template testing improves test coverage and organization.


1724-1736: Enhanced coverage reporting

Adding template-only coverage option provides more granular control over coverage reporting.

Comment on lines +186 to +195
def test_report_show_command(self, mock_list_reports):
mock_list_reports.return_value = []
tf = tempfile.NamedTemporaryFile(delete=False)
with open(tf.name, 'w') as f:
f.write('{"info":{"name":"test", "title": "test"}, "results":{}}')
result = self.runner.invoke(cli, ['report', 'show', tf.name])
os.remove(tf.name)
assert not result.exception
assert result.exit_code == 0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use context manager for temporary file

The temporary file is created but not properly managed with a context manager, which could lead to resource leaks.

Use a context manager to ensure proper file cleanup:

-tf = tempfile.NamedTemporaryFile(delete=False)
-with open(tf.name, 'w') as f:
-    f.write('{"info":{"name":"test", "title": "test"}, "results":{}}')
-result = self.runner.invoke(cli, ['report', 'show', tf.name])
-os.remove(tf.name)
+with tempfile.NamedTemporaryFile(delete=False) as tf:
+    tf.write(b'{"info":{"name":"test", "title": "test"}, "results":{}}')
+    tf.flush()
+    result = self.runner.invoke(cli, ['report', 'show', tf.name])
+os.remove(tf.name)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_report_show_command(self, mock_list_reports):
mock_list_reports.return_value = []
tf = tempfile.NamedTemporaryFile(delete=False)
with open(tf.name, 'w') as f:
f.write('{"info":{"name":"test", "title": "test"}, "results":{}}')
result = self.runner.invoke(cli, ['report', 'show', tf.name])
os.remove(tf.name)
assert not result.exception
assert result.exit_code == 0
def test_report_show_command(self, mock_list_reports):
mock_list_reports.return_value = []
- tf = tempfile.NamedTemporaryFile(delete=False)
- with open(tf.name, 'w') as f:
- f.write('{"info":{"name":"test", "title": "test"}, "results":{}}')
- result = self.runner.invoke(cli, ['report', 'show', tf.name])
- os.remove(tf.name)
+ with tempfile.NamedTemporaryFile(delete=False) as tf:
+ tf.write(b'{"info":{"name":"test", "title": "test"}, "results":{}}')
+ tf.flush()
+ result = self.runner.invoke(cli, ['report', 'show', tf.name])
+ os.remove(tf.name)
assert not result.exception
assert result.exit_code == 0
🧰 Tools
🪛 Ruff (0.11.9)

188-188: Use a context manager for opening files

(SIM115)

🤖 Prompt for AI Agents
In tests/unit/test_cli.py around lines 186 to 195, the temporary file is created
without using a context manager, which risks resource leaks. Refactor the test
to create and write to the temporary file within a with statement that manages
the NamedTemporaryFile context, ensuring the file is properly closed and deleted
after use. Adjust the code to remove manual file deletion and rely on the
context manager for cleanup.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
secator/celery.py (1)

170-170: Fix mutable default argument issue

Using a mutable default argument like opts={} can cause unexpected behavior when state from one call leaks into another.

Change the signature to use None and create a fresh dict:

-@app.task(bind=True)
-def run_command(self, results, name, targets, opts={}):
+@app.task(bind=True)
+def run_command(self, results, name, targets, opts=None):
+    if opts is None:
+        opts = {}
🧰 Tools
🪛 Ruff (0.11.9)

170-170: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

secator/runners/_base.py (1)

128-128: Fix duplicated condition in print_start initialization

There's a duplicated condition (not self.piped_output) in the print_start initialization.

-self.print_start = self.run_opts.get('print_start', False) and not self.piped_output and not self.piped_output and not self.dry_run
+self.print_start = self.run_opts.get('print_start', False) and not self.piped_input and not self.piped_output and not self.dry_run
🧹 Nitpick comments (1)
secator/runners/_base.py (1)

308-311: Good addition of ID property

The new id property centralizes the logic for extracting task/workflow/scan IDs from the context, which is used for tagging debug messages.

Consider handling the case when no ID is found in the context more explicitly:

@property
def id(self):
-    return self.context.get('task_id', '') or self.context.get('workflow_id', '') or self.context.get('scan_id', '')
+    id_value = self.context.get('task_id', '') or self.context.get('workflow_id', '') or self
10000
.context.get('scan_id', '')
+    return id_value if id_value else f"runner_{self.unique_name}"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3e9403 and 7aba480.

📒 Files selected for processing (4)
  • secator/celery.py (4 hunks)
  • secator/runners/_base.py (34 hunks)
  • secator/tasks/nmap.py (1 hunks)
  • secator/tree.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • secator/tree.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
secator/runners/_base.py (13)
secator/tree.py (1)
  • build_runner_tree (73-115)
secator/utils.py (3)
  • debug (428-465)
  • rich_to_ansi (376-393)
  • should_update (659-676)
secator/output_types/target.py (1)
  • Target (9-30)
secator/output_types/_base.py (4)
  • keys (101-102)
  • OutputType (11-108)
  • toDict (104-108)
  • load (61-94)
secator/output_types/error.py (2)
  • Error (8-43)
  • from_exception (23-30)
secator/runners/celery.py (1)
  • yielder (6-15)
secator/runners/_helpers.py (1)
  • run_extractors (7-47)
secator/runners/scan.py (1)
  • build_celery_workflow (23-75)
secator/runners/task.py (1)
  • build_celery_workflow (17-65)
secator/runners/workflow.py (1)
  • build_celery_workflow (26-81)
secator/output_types/info.py (1)
  • Info (8-24)
secator/tasks/bbot.py (1)
  • output_discriminator (172-179)
secator/output_types/warning.py (1)
  • Warning (8-24)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (10)
secator/tasks/nmap.py (1)

415-415: Simplified vulnerability tagging

The change to remove vuln_id from the tags list simplifies the tagging structure while maintaining all necessary information, as the vulnerability ID remains accessible through the ID field (line 411). This refinement aligns with the broader workflow enhancements in the PR.

secator/celery.py (3)

172-175: Good improvement to options management and task debugging

Adding print options and routing key information improves observability, and setting the quiet flag based on global configuration is more consistent.

The debug log with the routing key is particularly valuable for understanding task routing and troubleshooting distribution issues.


192-193: Good optimization for chunking check

Caching the result of task.needs_chunking(sync) in a local variable and setting task.has_children avoids redundant calls and improves code readability.


309-332: Great implementation of chunked results filtering

The added chunk_by option allows filtering results for each chunk based on a specified attribute, which is more efficient than passing all results to each chunk.

The implementation properly:

  1. Creates a new options copy for each chunk to prevent mutation issues
  2. Adds chunk metadata to options
  3. Filters results based on chunk attributes
  4. Logs the chunked targets and filtered results for debugging
secator/runners/_base.py (6)

43-51: Good addition of runner name formatting

The format_runner_name function improves readability by adding consistent color-coding based on runner type.

This enhances the user experience by making it easier to distinguish between different types of runners in the console output.


203-214: Well-structured property accessors for option management

The new properties provide a clean way to categorize and access different types of options:

  • resolved_opts: Non-null, non-print, non-extractor options
  • resolved_print_opts: Only print options that are enabled
  • dynamic_opts: Only extractor options (ending with '_')

This organization makes the code more maintainable and supports the improved option handling mentioned in the PR objectives.


634-657: Improved hook execution with better debug context

The updated run_hooks method now includes a sub parameter for debug context, which enhances traceability of hook execution in logs.

The additional debug messages provide valuable insights into hook execution, including:

  • When hooks are skipped due to frequency limitations
  • When hooks are disabled or no_process is set
  • When hooks succeed or fail

This is particularly useful for understanding the complex lifecycle of runners.


773-789: Enhanced start logging with visual tree representation

The completely rewritten log_start method now includes tree visualization of tasks and workflows, providing a much clearer picture of the execution plan.

The use of the build_runner_tree function to render a hierarchical view of the runner configuration is an excellent usability improvement, making complex workflows more understandable.


1027-1054: Improved profile resolution with optional printing

The updated resolve_profiles method now supports the new print_profiles option, providing better visibility into which profiles are being applied.

This change enhances usability by making it clearer to users which profiles are active and what their descriptions are.


382-392: Enhanced result filtering with context-aware extraction

The filter_results method now passes a DotMap context to run_extractors, enabling more sophisticated extraction based on context.

Using DotMap allows for dot notation access to nested structures, which makes the code more readable and less error-prone when working with complex contexts.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
secator/utils.py (1)

527-529: Fixed domain validation consistency

Now the function validates and returns the same property (top_domain_under_public_suffix) instead of validating one property and returning another. This prevents potential security issues where an unvalidated domain might be returned.

secator/decorators.py (2)

16-30: Renamed and enhanced CLI options

Renamed RUNNER_OPTS to CLI_OPTS for better clarity, fixed the process flag to use reverse=True for more intuitive behavior, and added a new tree flag to visualize runner hierarchies. The help text for the process flag now correctly describes that it enables processing (with --no-process disabling it).


101-127: Replaced eval with explicit type mapping

Replaced the use of eval for type conversion with a safer explicit mapping approach, eliminating a potential security risk while maintaining the same functionality.

🧹 Nitpick comments (2)
secator/utils.py (1)

430-431: Simplify DEBUG check condition

The condition uses not DEBUG == ['all'] and not DEBUG == ['1'] which could be simplified to DEBUG != ['all'] and DEBUG != ['1'] for better readability.

-	if not DEBUG == ['all'] and not DEBUG == ['1']:
-		if not DEBUG or DEBUG == [""]:
+	if DEBUG != ['all'] and DEBUG != ['1']:
+		if not DEBUG or DEBUG == [""]:
🧰 Tools
🪛 Ruff (0.11.9)

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

secator/decorators.py (1)

91-100: TODO comment for future refactoring

There's a TODO comment suggesting refactoring the get_command_options function to use runner.supported_opts instead. Consider creating a follow-up task to implement this refactoring once the current changes are stable.

Would you like me to create a separate issue to track this future refactoring task?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7aba480 and 61ad57b.

📒 Files selected for processing (9)
  • secator/celery.py (4 hunks)
  • secator/cli.py (26 hunks)
  • secator/config.py (6 hunks)
  • secator/decorators.py (12 hunks)
  • secator/output_types/error.py (1 hunks)
  • secator/output_types/url.py (3 hunks)
  • secator/runners/_base.py (34 hunks)
  • secator/tasks/katana.py (3 hunks)
  • secator/utils.py (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • secator/output_types/url.py
  • secator/tasks/katana.py
  • secator/celery.py
  • secator/runners/_base.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
secator/decorators.py (9)
secator/runners/task.py (1)
  • Task (8-80)
secator/tree.py (2)
  • build_runner_tree (73-115)
  • render_tree (44-54)
secator/utils.py (3)
  • expand_input (63-108)
  • get_command_category (263-274)
  • debug (428-465)
tests/performance/loadtester.py (1)
  • cli (51-74)
secator/output_types/_base.py (2)
  • keys (101-102)
  • toDict (104-108)
secator/click.py (2)
  • decorator (31-55)
  • decorator (61-83)
secator/runners/_base.py (2)
  • debug (484-501)
  • toDict (605-632)
secator/template.py (2)
  • flat_tasks (57-59)
  • _extract_workflows (130-137)
secator/config.py (1)
  • get (207-230)
🪛 Ruff (0.11.9)
secator/utils.py

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (58)
secator/output_types/error.py (2)

29-29: Improved error message formatting

This change ensures consistent and proper escaping of error messages when creating error objects from exceptions. By applying the rich text escape function _s() at the creation point, the message is correctly formatted for display.


36-36: Enhanced error display format

The direct use of the message string in the repr output without additional escaping prevents double-escaping issues, resulting in cleaner and more consistent error presentation in the console.

secator/utils.py (5)

29-29: Updated import to use new DEBUG structure

The import has been updated to use DEBUG instead of DEBUG_COMPONENT, aligning with the broader refactoring of debug configuration to use string-based flags.


422-422: Improved visual distinction in debug output

The styling for dictionary keys and values has been updated from dim cyan/yellow/green to bold blue/yellow/blue, providing better visual distinction and readability in debug output.


435-436: Updated debug component checking

The code now iterates over the elements in the DEBUG list to check if the component should be logged, replacing the previous DEBUG_COMPONENT approach. This provides more flexible and granular control over debug output.


464-464: Consistent debug flag checking

Exception handling in the debug function now checks for 'rich' in DEBUG rather than using the removed DEBUG_COMPONENT, maintaining the same behavior while aligning with the new debug configuration approach.


807-812: Simplified wordlist processing logic

The process_wordlist function has been simplified by removing the explicit existence check and always calling download_file with the given parameters. This leverages the improved download_file function in secator/config.py that now handles local file security checks and URL validation more robustly.

secator/config.py (11)

7-9: Added validators and switched to shutil

Added validators package for URL validation and switched from os.symlink to shutil for more robust file operations. This improves security by properly validating URLs before download attempts.


75-76: Added worker command verbosity control

Added a new configuration option to control worker command output verbosity, providing better flexibility for debugging and production environments.


82-84: Enhanced HTTP response control options

Added new configuration options to control HTTP response header visibility and filtering, giving users more granular control over output verbosity and sensitive information display.


176-176: Simplified debug configuration

Changed the debug attribute in SecatorConfig from a complex object to a simple string, supporting the broader refactoring of debug configuration to use string-based flags that can be parsed into a list of components.


537-537: Using structured output types

Now importing Info and Error from secator.output_types to provide consistent, structured output for download operations, improving error handling and user feedback.


546-547: Improved Git clone error handling

Enhanced error reporting for Git clone operations using the structured Error type, providing more consistent and visually clear error messages to users.

Also applies to: 554-556


565-569: Added security validation for local file access

Added a security check that prevents access to files outside the data directory when security.allow_local_file_access is disabled. This is an important security enhancement to prevent potential directory traversal vulnerabilities.


570-576: Changed from symlinking to moving files

Replaced the symlink operation with a file move operation, which is more secure and portable across different operating systems. This also uses structured Info output for consistent user feedback.


577-599: Enhanced URL download with better error handling

Improved the URL download logic with proper validation, existence checking, and structured error handling. The function now skips downloads for existing files and provides clear feedback using Info objects.


602-603: Improved invalid resource error reporting

Added structured error reporting for invalid resources (not a valid git repository, URL, or local path), providing clearer feedback to users about the nature of the error.


640-641: Updated debug condition check

Updated the debug print condition to check for the substring 'config' in the debug string instead of comparing with a boolean, consistent with the new string-based debug flag approach.

secator/decorators.py (15)

9-10: Imported refined CLI components

Now importing CLICK_LIST from the dedicated secator.click module and build_runner_tree from secator.tree, reflecting a better code organization where CLI-related functionality is consolidated in dedicated modules.


33-35: Renamed global options and improved execution controls

Renamed RUNNER_GLOBAL_OPTS to CLI_GLOBAL_OPTS for clarity and replaced worker with sync flag, using the opposite parameter to maintain backward compatibility. This makes the execution mode selection more intuitive.


42-47: Added task decorator

Introduced a new task decorator to mark classes as tasks, providing a cleaner and more explicit way to identify task classes in the codebase.


49-89: Improved command option decoration

Completely rewrote the decorate_command_options function with comprehensive handling for reverse flags and opposites. The function now correctly processes option configurations and generates appropriate Click option decorators with proper help text and flag combinations.


110-120: Enhanced options gathering from child workflows

Added logic to collect options from child workflows (for scans), preserving the workflow name as metadata. This ensures that all relevant options are available at the CLI level and properly attributed to their source.


130-131: Unified option collection

Updated the options collection logic to use the renamed option dictionaries (CLI_OPTS and CLI_GLOBAL_OPTS), ensuring consistent option handling across the application.


147-152: Updated option support checks

Modified the option support checking to use the renamed option dictionaries, maintaining the same functionality with the new naming convention.


164-169: Improved option prefix determination

Updated the prefix determination logic to handle the renamed option dictionaries, ensuring options are correctly categorized in the CLI help output.


172-173: Simplified option value retrieval

Simplified the retrieval of option values from the YAML configuration, making the code more readable while maintaining the same functionality.


186-188: Fixed required option handling

Modified the handling of required options to properly set required=False when the option is defined in the configuration, preventing unnecessary prompts for values that are already provided.


202-204: Improved boolean option handling

Enhanced the handling of boolean options by adding reverse flags, providing users with more intuitive CLI options (e.g., --flag/--no-flag).


300-301: Added tree visualization

Added support for the new tree flag that allows users to visualize the runner hierarchy using the build_runner_tree function. This provides a helpful overview of task relationships and execution flow.

Also applies to: 322-326


339-339: Improved driver tracking

Added tracking of actual drivers used, which improves reporting and debugging capabilities by maintaining a list of active drivers.

Also applies to: 351-351


362-370: Enhanced synchronous mode logic

Updated the logic for enabling synchronous mode to use the sync flag, improving clarity and consistency with the new CLI option naming.


387-390: Added print control options

Added new print control options (print_profiles, print_start, print_end) to the run options, providing finer-grained control over output verbosity and presentation.

secator/cli.py (25)

19-21: Improved module organization with better separation of concerns

The import changes reflect a good architecture improvement, separating Click-related functionality into a dedicated module (secator.click) instead of keeping it in decorators. This aligns with the PR objective of code reorganization for better structure.


73-75: Improved CLI usability with consistent aliases

Adding aliases ('x', 't', 'tasks') to the task group improves usability by providing both short and descriptive alternatives. This change aligns with the PR objective of enhancing CLI command organization.


90-92: Consistent use of OrderedGroup with aliases

Using the new OrderedGroup class for the workflow group with aliases follows the same pattern as the task group, maintaining consistency across the CLI interface.


106-108: CLI command consistency maintained for scan group

The scan group follows the same pattern as the task and workflow groups, using OrderedGroup and adding an alias, ensuring a consistent user experience.


208-208: Improved error handling with explicit exit codes

Replacing return with sys.exit(1) for error cases ensures the CLI properly signals failures to the calling process or shell, which is a best practice for command-line tools.

Also applies to: 228-228, 237-237, 1129-1129, 1309-1309


303-308: Enhanced UX with automatic payload download

The changes to the serve command now check if the payload directory is empty and automatically download payload files, improving the user experience by eliminating manual steps.


540-540: Consistent command naming with workspace aliases

Adding aliases ('ws', 'workspaces') to the workspace group follows the same pattern as other command groups, maintaining CLI consistency.


573-594: Added profiles management functionality

The new profile command group with its list command enhances the CLI by providing a way to view available profiles with their descriptions and options, which aligns with the PR objective of improving configuration handling.


596-687: Added alias management functionality

The new alias command group with enable, disable, and list commands provides a comprehensive way to manage shell aliases for Secator commands, improving the overall CLI usability as mentioned in the PR objectives.


694-694: Consistent report command group with aliases

Adding aliases ('r', 'reports') to the report group maintains the CLI consistency pattern established with other command groups.


700-759: Improved query processing with better validation

The new process_query function provides structured query parsing and validation, preventing mixed operators and handling field filtering effectively. This implementation addresses the previous review comment about operator inference.

The error handling for mixed operators (&&, ||) in the same query is now explicit, improving the user experience by providing clear error messages instead of silent fallbacks.


700-702: Fixed mutable default argument issue

The implementation correctly addresses the previous review comment about mutable default arguments by using None as the default and initializing fields to an empty list inside the function.


711-715: Avoid shadowing built-in type name

Using _type instead of type prevents shadowing the built-in Python type() function, addressing the previous review comment.


728-736: Improved operator detection with explicit error handling

The operator detection logic now properly checks for mixed operators and provides clear error messages, addressing the previous review comment about logical operator inference.


774-778: Enhanced CLI with piped input support

Adding support for reading report queries from piped input improves the CLI's interoperability with other command-line tools, enabling more complex automation workflows.


831-833: Improved user feedback when loading multiple reports

The updated output formatting when loading multiple reports provides better visibility into the loading progress, enhancing the user experience especially for large report sets.


847-849: Only show report path for non-empty reports

This change improves the output by only showing the report path information for non-empty reports, reducing noise in the output.


859-860: Better user feedback during report processing

Improved progress messaging during report building provides better user feedback, especially for time-consuming operations with large result sets.


871-872: Smart CLI output based on context

The report_list command now detects if its output is being piped and adjusts the output format accordingly, printing only report paths when piped. This is a good practice for CLI tools that may be used in automation scripts.

Also applies to: 886-894


924-926: Consistent error messaging

Using Error message for no reports found instead of plain text maintains consistency with the error reporting style used elsewhere in the codebase.


1147-1149: Removed excessive styling from docstrings

Removing the dim styling from the install command docstring improves readability while maintaining a clean CLI help output.


1417-1417: Removed excessive styling from test docstring

Similar to the install command, removing the dim styling from the test command docstring improves readability.


1483-1483: Updated test commands with better coverage tracking

The test commands now use more specific coverage data files, improving the organization of test coverage data.

Also applies to: 1514-1514


1521-1550: Added template testing functionality

The new template test command enhances the testing capabilities by specifically focusing on templates, with proper environment setup and coverage tracking, which aligns with the PR objective of improved configuration handling.


1731-1743: Enhanced coverage command with template support

The coverage command now includes a --template-only option to generate coverage reports specifically for template tests, providing more granular control over coverage reporting.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
secator/utils.py (1)

430-431: Use != operator for better readability.

The current boolean expression uses not x == y which is less readable than the equivalent x != y.

-	if not DEBUG == ['all'] and not DEBUG == ['1']:
+	if DEBUG != ['all'] and DEBUG != ['1']:
🧰 Tools
🪛 Ruff (0.11.9)

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61ad57b and 6d21c40.

📒 Files selected for processing (5)
  • secator/config.py (6 hunks)
  • secator/runners/_base.py (34 hunks)
  • secator/tasks/bbot.py (1 hunks)
  • secator/utils.py (5 hunks)
  • tests/unit/test_template.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tests/unit/test_template.py
🧰 Additional context used
🪛 Ruff (0.11.9)
secator/utils.py

430-430: Use DEBUG != ['all'] instead of not DEBUG == ['all']

Replace with != operator

(SIM201)


430-430: Use DEBUG != ['1'] instead of not DEBUG == ['1']

Replace with != operator

(SIM201)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: integration (3.11, ubuntu-latest)
  • GitHub Check: test-tasks (ubuntu-latest)
🔇 Additional comments (24)
secator/tasks/bbot.py (1)

353-353: Good improvement using shutil.copyfile instead of shutil.copy

This change improves reliability when copying screenshot files. Using shutil.copyfile() instead of shutil.copy() ensures only the file content is copied without preserving metadata like permissions, which can help avoid potential permission issues in cross-platform environments.

secator/utils.py (5)

29-29: Import update for unified debug handling.

The change from DEBUG_COMPONENT to DEBUG aligns with the PR's objective of making debug configuration string-based for finer control.


422-422: Improved debug output formatting.

Changed color styling from dim cyan/yellow/green to bold blue/yellow/blue, making debug output more readable.


463-464: Updated debug configuration check.

Updated to check for 'rich' in the DEBUG string list, aligning with the new debug configuration approach.


526-528: Fixed property mismatch in domain validation.

This change ensures that the same property (top_domain_under_public_suffix) is both validated and returned, eliminating a potential security/logic bug where an unvalidated domain might be returned.


806-811: Simplified wordlist processing.

Removed redundant file existence check since download_file now handles all necessary validation and error reporting with structured output types.

secator/config.py (8)

7-9: Added essential imports for enhanced functionality.

  • validators for improved URL validation
  • shutil for file operations in the enhanced download_file function

75-75: Added Celery worker command verbosity control.

This addition allows users to control the verbosity of Celery worker commands, supporting the CLI improvements goal of the PR.


82-84: Enhanced CLI configuration with header controls.

Added options to:

  • Show HTTP response headers (show_http_response_headers)
  • Show command output (show_command_output)
  • Exclude specific HTTP headers from display (exclude_http_response_headers)

These additions provide more fine-grained control over CLI output display.


176-176: Simplified debug configuration.

Changed debug from a complex Debug class to a simple string, making it easier to customize debug levels and components through string parsing.


537-538: Added structured output types for improved error handling.

Now using Info and Error types from secator.output_types to provide consistent, well-formatted feedback.


564-574: Added security check for local file access.

Enhanced download_file to validate that files accessed outside the data directory are only accessed when explicitly allowed by the security configuration, preventing potential security risks.


576-602: Improved URL validation and error handling.

Enhanced download_file to:

  1. Validate URLs properly using the validators package
  2. Provide structured error reporting
  3. Skip downloads for existing files
  4. Handle request exceptions gracefully

These changes make the function more robust and user-friendly.


639-640: Updated debug configuration check.

Changed to check if 'config' is contained in the CONFIG.debug string, aligning with the new string-based debug configuration approach.

secator/runners/_base.py (10)

4-21: Added imports for enhanced visualization and data handling.

  • textwrap for indenting tree output
  • DotMap for structured context data
  • build_runner_tree from the new tree module for hierarchical task visualization

These additions support the runner tree visualization feature and improved context handling.


43-51: Added helpful runner name formatting utility.

The new format_runner_name function provides consistent, color-coded runner names based on type (task, workflow, scan), improving readability in logs and console output.


128-130: Added print control options for enhanced output management.

New options:

  • print_start: Controls printing of runner start information
  • print_end: Controls printing of runner end information
  • print_profiles: Controls printing of profile information

These options align with the PR's goal of improving CLI output control and provide more granular control over what gets displayed.

Also applies to: 134-134


203-214: Added properties for cleaner option categorization.

New properties:

  • resolved_opts: Filters run options to only non-None, non-print, non-dynamic options
  • resolved_print_opts: Extracts print-related options
  • dynamic_opts: Extracts dynamic options (ending with underscore)

This change improves code organization and makes option handling more maintainable.


309-311: Added ID property for improved debug tagging.

Extracts a task/workflow/scan ID from context for enhanced debug message tagging, making it easier to trace log messages to specific operations.


382-392: Enhanced filter_results with context-aware extraction.

Modified to pass a DotMap context to run_extractors, allowing for more sophisticated context-aware extraction logic that can leverage run options and targets.


634-635: Improved hook and validator function signatures.

Added the sub parameter to run_hooks and run_validators to provide consistent debug context information, making debugging more structured and informative.

Also applies to: 672-673


774-798: Enhanced runner start/end logging with tree visualization.

The updated logging now:

  1. Checks for print control flags
  2. Renders a hierarchical tree of the runner's structure for non-task runners
  3. Uses consistent formatting with the new format_runner_name function
  4. Provides more context through formatted messages

This aligns with the PR's goal of improved command organization and visibility.


1027-1053: Improved profile resolution with optional output.

Enhanced resolve_profiles to:

  1. Return the loaded profiles (instead of None)
  2. Only print profile information when print_profiles is enabled
  3. Use consistent formatting for profile messages

This improves the function's utility while maintaining control over output verbosity.


118-118:

❓ Verification inconclusive

Changed process flag from opt-out to opt-in.

The initialization of no_process has been inverted to use the negation of process, changing from an opt-out to an opt-in approach.

Verify that all callers set the process parameter correctly when needed, as this change could affect existing code that relies on the default behavior.


🏁 Script executed:

#!/bin/bash
# Search for instances where run_opts is created and check if 'process' is set
echo "Searching for run_opts assignments that might need updating..."
rg -A 5 "run_opts\s*=\s*\{" --glob "*.py" | grep -i -E "process|no_process"

Length of output: 330


🏁 Script executed:

#!/bin/bash
echo "=== Inspecting BaseRunner class and its run method ==="
rg -n "class BaseRunner" -A 200 secator/runners/_base.py

echo -e "\n=== Showing run() method in BaseRunner ==="
rg -n "def run" -A 50 secator/runners/_base.py

echo -e "\n=== Context around self.no_process assignment ==="
rg -n "self.no_process" -A 10 secator/runners/_base.py

echo -e "\n=== Inspecting _process_opts implementation ==="
rg -n "def _process_opts" -A 200 .

echo -e "\n=== Checking CLI definitions for 'process' flag ==="
rg -n "add_argument.*process" -A 5 .

Length of output: 22799


Verify that all callers set process correctly

We’ve inverted the no_process flag in secator/runners/_base.py (line 118):

self.no_process = not self.run_opts.get('process', True)

This means:

  • By default process=Trueno_process=False (processing enabled).
  • Callers now must pass process=False to disable processing.

We did not find any existing use of a process key in run_opts, nor a corresponding CLI flag. Please confirm that:

  • Your CLI definitions (argparse/click) expose --process/--no-process and populate run_opts['process'].
  • Any direct instantiations of runners that used to rely on the old default now explicitly pass process=False when needed.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
secator/cli.py (2)

303-308: Nice enhancement to handle empty payload directories

The automatic download of payload files when the directory is empty improves user experience by eliminating manual steps. Consider adding a check if the directory exists before listing its contents to prevent errors.

fnames = list(os.listdir(directory))
+ if not os.path.exists(directory):
+     os.makedirs(directory, exist_ok=True)
+     console.print(Warning(message=f'Creating payload directory {directory}.'))
+     fnames = []
if not fnames:
    console.print(Warning(message=f'No payloads found in {directory}.'))
    download_files(CONFIG.payloads.templates, CONFIG.dirs.payloads, CONFIG.offline_mode, 'payload')
    fnames = list(os.listdir(directory))

727-736: Improved operator detection logic

The operator detection logic now properly handles mixed operators and provides better error messages, addressing the issues identified in previous reviews. This prevents potential confusion and unexpected behavior.

# Get operator
operator = '||'
if '&&' in query and '||' in query:
-    console.print(Error(message='Cannot mix && and || in the same query'))
-    sys.exit(1)
+   console.print(Error(message='Cannot mix && and || in the same query'))
+   sys.exit(1)
elif '&&' in query:
-    operator = '&&'
+   operator = '&&'
elif '||' in query:
-    operator = '||'
+   operator = '||'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d21c40 and 855207a.

📒 Files selected for processing (1)
  • secator/cli.py (26 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: integration (3.11, ubuntu-latest)
🔇 Additional comments (12)
secator/cli.py (12)

19-21: Good module organization improvement

The imports have been updated to reflect the code restructuring, particularly importing OrderedGroup from the dedicated secator.click module and adding download_files from config. This aligns with the PR objective of extracting Click-related functionality into a separate module.


73-75: Well-implemented command aliasing

Adding consistent aliases for the task group (x, t, tasks) improves the CLI usability by providing shorter command alternatives while maintaining the descriptive primary command name.


90-92: Good use of OrderedGroup for workflow commands

Using the OrderedGroup class for the workflow group with appropriate aliases ensures commands appear in a logical order in help text while maintaining consistent aliasing patterns across the CLI.


573-594: Good addition of profiles command group

The new profile group with aliases improves command organization and provides a clear way to list and manage profiles, which enhances the CLI's usability. The table formatting for displaying profiles is also well-done.


596-687: Excellent alias management implementation

The new alias group with enable, disable, and list commands provides a powerful way to manage shell aliases for Secator commands. The implementation is thorough, including helpful instructions for users on how to use the generated alias files.


700-720: Fixed mutable default argument issue

The rewritten process_query function properly initializes the default value for fields parameter, addressing the potential issues with mutable default arguments that were flagged in previous reviews.


771-787: Improved report query handling with piped input

The enhancement to handle piped input for report queries and the improved formatting of the query summary provide a more flexible and user-friendly interface for report filtering and extraction.


802-804: Enhanced report path handling

The improved report query handling supports comma-separated queries and fuzzy matching, making it easier for users to work with multiple reports or locate reports without specifying exact paths.


831-833: Improved progress indicators for report processing

The added progress indicators provide better feedback during report loading and processing, especially helpful for large reports or when processing multiple reports.

Also applies to: 847-849, 859-860


887-894: Good support for piped output in report listing

Adding support for piped output enhances the CLI's flexibility and enables integration with other tools in command pipelines, which is a valuable feature for a CLI tool.


1521-1550: Well-implemented template test command

The addition of a template test command enhances the testing capabilities and provides a dedicated way to run integration tests on templates, which is valuable for template development and validation.


1731-1743: Improved coverage command with template support

The enhanced coverage command with template-only option provides more flexibility for test coverage analysis, which is important for comprehensive testing of the codebase.

@ocervell ocervell merged commit e8225cd into main May 23, 2025
12 checks passed
@ocervell ocervell deleted the add-runner-meta-opts branch June 4, 2025 19:27
ocervell added a commit that referenced this pull request Jun 5, 2025
🤖 I have created a release *beep* *boop*
---


##
[0.16.0](v0.15.1...v0.16.0)
(2025-06-05)


### Features

* **`dnsx`:** merge `dnsxbrute` into `dnsx`
([#571](#571))
([d30a497](d30a497))
* add task revoke state and perf improvements
([#678](#678))
([2a3bf08](2a3bf08))
* allow returning errors in hooks
([#632](#632))
([39a56bd](39a56bd))
* improve bbot output types
([#627](#627))
([3b0aa5d](3b0aa5d))
* improve runner logic, workflow building, results filtering logic; and
add config defaults for profiles & drivers
([#673](#673))
([df94657](df94657))
* improve template loading flow
([#667](#667))
([f223120](f223120))
* memory optimizations
([#681](#681))
([d633133](d633133))
* **misc:** condition-based runs, chunked_by opts, dynamic task
profiles, cli improvements
([#659](#659))
([e8225cd](e8225cd))
* **runner:** add input validation to all tasks and workflows
([#663](#663))
([8392551](8392551))
* **runner:** improve option handling
([#670](#670))
([59b1c68](59b1c68))
* **scans:** improve scans
([#660](#660))
([bdd38ec](bdd38ec))
* use os system for CLI and better labs
([#649](#649))
([8b49912](8b49912))
* **workflow:** improve subdomain_recon workflow
([#657](#657))
([bc65092](bc65092))


### Bug Fixes

* allow dry-run mode to work without targets
([#624](#624))
([cccffb9](cccffb9))
* check task is registered before running test
([1f5cd83](1f5cd83))
* formatting for dynamic opts
([#628](#628))
([dcbbfe9](dcbbfe9))
* header options conversion
([#633](#633))
([6ae8423](6ae8423))
* header parsing
([#629](#629))
([db2f028](db2f028))
* improve mongodb duplicate checker
([#626](#626))
([bf277a9](bf277a9))
* **installer:** compound distro.like() on distribs like popos
([#653](#653))
([3687e1d](3687e1d))
* **installer:** ignore dev/post release from PyPI
([#634](#634))
([614c3e2](614c3e2))
* **installer:** secator update with correct package version
([#648](#648))
([a9cf189](a9cf189))
* lab --wait not in gitlab runner
([070ae84](070ae84))
* logic to test all tasks
([3bd7503](3bd7503))
* os.system return code
([02aed75](02aed75))
* progress type fields
([#652](#652))
([f146914](f146914))
* remove duplicates from txt exporter
([#630](#630))
([88ba5c5](88ba5c5))
* remove fping -r flag by default, show alive hosts better
([#665](#665))
([5c945fd](5c945fd))
* remove no-recreate flag in labs as not supported by github runner
([bd997a8](bd997a8))
* short opt incorrectly named
([#631](#631))
([0c73c60](0c73c60))
* tasks with no file flag need input_chunk_size=1
([#668](#668))
([a088c94](a088c94))
* tools in readme, arjun chunk and ffuf header
([#679](#679))
([654ff30](654ff30))
* tools table generator update
([9420f14](9420f14))
* update ci workflow
([f4c2b13](f4c2b13))
* update generate table workflow
([ff62702](ff62702))
* vulnerability output reference when unset
([#625](#625))
([a656fbf](a656fbf))


### Documentation

* generate tools table md
([#610](#610))
([d60f11e](d60f11e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0