mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-09-11 04:07:47 +02:00
Complete rewrite and re-architecture Osmedeus Engine in v5
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
name: basic-recon-flow
|
||||
kind: flow
|
||||
desc: Basic reconnaissance flow orchestrating subdomain enumeration, port scanning, and screenshots
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "20"
|
||||
- name: timeout
|
||||
value: "3600"
|
||||
|
||||
modules:
|
||||
# Module 1: Subdomain Enumeration
|
||||
- name: subdomain-enum
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: setup-enum
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/subdomains
|
||||
- mkdir -p {{Output}}/resolved
|
||||
exports:
|
||||
subdomain_dir: "{{Output}}/subdomains"
|
||||
|
||||
- name: passive-enum
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "{{Binaries}}/subfinder -d {{Target}} -silent -o {{subdomain_dir}}/subfinder.txt"
|
||||
- "{{Binaries}}/assetfinder --subs-only {{Target}} > {{subdomain_dir}}/assetfinder.txt"
|
||||
timeout: 600
|
||||
|
||||
- name: merge-subdomains
|
||||
type: bash
|
||||
command: "cat {{subdomain_dir}}/*.txt | sort -u > {{Output}}/all-subdomains.txt"
|
||||
exports:
|
||||
all_subdomains: "{{Output}}/all-subdomains.txt"
|
||||
|
||||
# Module 2: Port Scanning (depends on subdomain-enum)
|
||||
- name: port-scan
|
||||
depends_on:
|
||||
- subdomain-enum
|
||||
condition: "fileLength('{{all_subdomains}}') > 0"
|
||||
steps:
|
||||
- name: setup-ports
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/ports
|
||||
exports:
|
||||
ports_dir: "{{Output}}/ports"
|
||||
|
||||
- name: scan-ports
|
||||
type: foreach
|
||||
input: "{{all_subdomains}}"
|
||||
variable: subdomain
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: naabu-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/naabu -host [[subdomain]] -top-ports 100 -silent >> {{ports_dir}}/open-ports.txt"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
|
||||
- name: aggregate-ports
|
||||
type: bash
|
||||
command: "sort -u {{ports_dir}}/open-ports.txt -o {{Output}}/all-ports.txt"
|
||||
exports:
|
||||
all_ports: "{{Output}}/all-ports.txt"
|
||||
|
||||
# Module 3: Screenshot Capture (depends on port-scan)
|
||||
- name: screenshot
|
||||
depends_on:
|
||||
- port-scan
|
||||
condition: "fileLength('{{all_ports}}') > 0"
|
||||
steps:
|
||||
- name: setup-screenshots
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/screenshots
|
||||
|
||||
- name: probe-http
|
||||
type: bash
|
||||
command: "{{Binaries}}/httpx -l {{all_subdomains}} -silent -o {{Output}}/http-hosts.txt"
|
||||
timeout: 300
|
||||
exports:
|
||||
http_hosts: "{{Output}}/http-hosts.txt"
|
||||
|
||||
- name: capture-screenshots
|
||||
type: foreach
|
||||
input: "{{http_hosts}}"
|
||||
variable: url
|
||||
threads: 10
|
||||
step:
|
||||
name: gowitness-capture
|
||||
type: bash
|
||||
command: "{{Binaries}}/gowitness single --url=[[url]] --screenshot-path={{Output}}/screenshots --timeout=30"
|
||||
timeout: 60
|
||||
on_error: continue
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
name: cidr-probing
|
||||
kind: module
|
||||
description: Running HTTP fingerprint technology and response with CIDR inputs - demonstrates port scanning, HTTP probing, and result processing
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: inputFile
|
||||
default: "{{Target}}"
|
||||
- name: output_dir
|
||||
default: "{{Output}}/portscan"
|
||||
- name: httpFile
|
||||
default: "{{Output}}/portscan/http-{{Workspace}}.txt"
|
||||
- name: enableScreenshot
|
||||
default: "false"
|
||||
- name: httpTimeout
|
||||
default: "10"
|
||||
- name: ports
|
||||
default: "3000,3128,3333,4243,443,4567,4711,4712,4993,5000,5104,5108,5800,591,593,6443,6543,7000,7396,7474,7779,80,8000,8001,8008,8014,8042,8069,8080,8081,8088,8090,8091,81,8118,8123,8172,8222,8243,8280,8281,832,8333,8443,8500,8834,8880,8888,8983,9000,9043,9060,9080,9090,9091,9200,9443,9800,981,9981,11443,7443,3001,8009"
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: httpThreads
|
||||
default: "{{threads * 8}}"
|
||||
- name: rateRustScan
|
||||
default: "{{threads * 500}}"
|
||||
|
||||
steps:
|
||||
# ============================================================
|
||||
# Phase 1: Validate Dependencies
|
||||
# ============================================================
|
||||
- name: validate-dependencies
|
||||
type: function
|
||||
function: |
|
||||
fileExists("{{Binaries}}/metabigor") &&
|
||||
fileExists("{{Binaries}}/httpx")
|
||||
exports:
|
||||
deps_valid: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Required binaries (metabigor, httpx) not found"
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Setup Output Directories
|
||||
# ============================================================
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
command: mkdir -p {{output_dir}}
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Port Scanning with Metabigor
|
||||
# ============================================================
|
||||
- name: port-scanning
|
||||
type: bash
|
||||
command: "cat {{inputFile}} | {{Binaries}}/metabigor scan --rate {{rateRustScan}} -p {{ports}} --pipe >> {{output_dir}}/raw-open-ports.txt"
|
||||
timeout: 1800
|
||||
exports:
|
||||
raw_ports_file: "{{output_dir}}/raw-open-ports.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Port scanning failed"
|
||||
- action: continue
|
||||
|
||||
- name: clean-portscan-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/raw-open-ports.txt")'
|
||||
function: CleanRustScan("{{output_dir}}/raw-open-ports.txt", "{{output_dir}}/open-ports.txt")
|
||||
exports:
|
||||
clean_ports_file: "{{output_dir}}/open-ports.txt"
|
||||
|
||||
- name: count-open-ports
|
||||
type: function
|
||||
function: fileLength("{{output_dir}}/open-ports.txt")
|
||||
exports:
|
||||
open_port_count: "output"
|
||||
|
||||
# Decision: Skip HTTP probing if no open ports found
|
||||
- name: check-port-results
|
||||
type: bash
|
||||
command: "echo {{open_port_count}}"
|
||||
decision:
|
||||
switch: "{{open_port_count}}"
|
||||
cases:
|
||||
"0":
|
||||
goto: generate-empty-report
|
||||
default:
|
||||
goto: http-probing
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: HTTP Probing
|
||||
# ============================================================
|
||||
- name: http-probing
|
||||
type: bash
|
||||
command: "cat {{output_dir}}/open-ports.txt | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -silent -t {{httpThreads}} >> {{httpFile}}"
|
||||
timeout: 900
|
||||
exports:
|
||||
http_file: "{{httpFile}}"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "HTTP probing failed"
|
||||
- action: continue
|
||||
|
||||
- name: sort-http-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{httpFile}}")'
|
||||
function: SortU("{{httpFile}}")
|
||||
|
||||
- name: count-http-hosts
|
||||
type: function
|
||||
function: fileLength("{{httpFile}}")
|
||||
exports:
|
||||
http_host_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Parallel HTTP Fingerprinting
|
||||
# ============================================================
|
||||
- name: http-fingerprinting
|
||||
type: parallel-steps
|
||||
pre_condition: 'parseInt("{{http_host_count}}") > 0'
|
||||
parallel_steps:
|
||||
- name: httpx-json-fingerprint
|
||||
type: bash
|
||||
command: "cat {{httpFile}} | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -t {{httpThreads}} -no-color -json -title -tech-detect -status-code -silent >> {{output_dir}}/{{Workspace}}-http-overview.txt"
|
||||
timeout: 1200
|
||||
on_error:
|
||||
- action: log
|
||||
message: "HTTP fingerprinting failed"
|
||||
- action: continue
|
||||
|
||||
- name: extract-technologies
|
||||
type: bash
|
||||
command: "cat {{httpFile}} | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -t {{httpThreads}} -tech-detect -silent >> {{output_dir}}/{{Workspace}}-technologies.txt"
|
||||
timeout: 600
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Process HTTP Results
|
||||
# ============================================================
|
||||
- name: clean-http-json
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/{{Workspace}}-http-overview.txt")'
|
||||
function: CleanJSONHttpx("{{output_dir}}/{{Workspace}}-http-overview.txt", "{{output_dir}}/{{Workspace}}-raw-overview.txt")
|
||||
exports:
|
||||
raw_overview: "{{output_dir}}/{{Workspace}}-raw-overview.txt"
|
||||
|
||||
- name: beautify-results
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{output_dir}}/{{Workspace}}-raw-overview.txt")'
|
||||
command: "cat {{output_dir}}/{{Workspace}}-raw-overview.txt | csvtk pretty --no-header-row -I -s ' | ' -W 75 > {{output_dir}}/beautify-{{Workspace}}-http.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Beautify failed, copying raw results"
|
||||
- action: run
|
||||
step: fallback-beautify
|
||||
|
||||
- name: fallback-beautify
|
||||
type: bash
|
||||
pre_condition: '!fileExists("{{output_dir}}/beautify-{{Workspace}}-http.txt")'
|
||||
command: "cp {{output_dir}}/{{Workspace}}-raw-overview.txt {{output_dir}}/beautify-{{Workspace}}-http.txt 2>/dev/null || touch {{output_dir}}/beautify-{{Workspace}}-http.txt"
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: Foreach - Detailed Host Analysis
|
||||
# ============================================================
|
||||
- name: detailed-host-analysis
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{http_host_count}}") > 0 && parseInt("{{http_host_count}}") < 100'
|
||||
input: "{{httpFile}}"
|
||||
variable: host
|
||||
threads: 5
|
||||
step:
|
||||
name: analyze-single-host
|
||||
type: bash
|
||||
command: |
|
||||
echo "Analyzing [[host]]..."
|
||||
curl -s -I "[[host]]" 2>/dev/null | grep -i "server\|x-powered-by\|content-type" >> {{output_dir}}/headers-{{Workspace}}.txt
|
||||
echo "---" >> {{output_dir}}/headers-{{Workspace}}.txt
|
||||
timeout: 30
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Generate Reports
|
||||
# ============================================================
|
||||
- name: generate-report
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "=== CIDR Probing Report ===" > {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Workspace: {{Workspace}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Date: $(date)" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== Statistics ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Open Ports Found: {{open_port_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "HTTP Hosts: {{http_host_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== HTTP Hosts ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
cat {{httpFile}} >> {{output_dir}}/final-report-{{Workspace}}.txt 2>/dev/null || echo "No HTTP hosts found"
|
||||
- "cat {{output_dir}}/beautify-{{Workspace}}-http.txt 2>/dev/null || true"
|
||||
|
||||
- name: generate-markdown-report
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{Data}}/markdown/simple-template.md")'
|
||||
function: GenMarkdownReport("{{Data}}/markdown/simple-template.md", "{{Output}}/summary.html")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Markdown report generation skipped - template not found"
|
||||
- action: continue
|
||||
|
||||
- name: generate-empty-report
|
||||
type: bash
|
||||
pre_condition: '{{open_port_count}} == 0'
|
||||
commands:
|
||||
- |
|
||||
echo "=== CIDR Probing Report ===" > {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "No open ports found for target." >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
- "touch {{output_dir}}/beautify-{{Workspace}}-http.txt"
|
||||
- "touch {{httpFile}}"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: Cleanup and Notifications
|
||||
# ============================================================
|
||||
- name: final-sort
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{httpFile}}")'
|
||||
function: SortU("{{httpFile}}")
|
||||
|
||||
- name: notify-completion
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{open_port_count}}") > 0'
|
||||
function: printf("CIDR scan complete: {{open_port_count}} open ports, {{http_host_count}} HTTP hosts")
|
||||
@@ -0,0 +1,147 @@
|
||||
name: content-discovery
|
||||
kind: module
|
||||
desc: Directory and file discovery workflow with threaded foreach and parallel execution
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "50"
|
||||
- name: wordlist_small
|
||||
value: "{{Data}}/wordlists/common.txt"
|
||||
- name: wordlist_medium
|
||||
value: "{{Data}}/wordlists/directory-list-2.3-medium.txt"
|
||||
- name: extensions
|
||||
value: "php,asp,aspx,jsp,html,js,json,xml,txt,bak,old,conf"
|
||||
- name: status_codes
|
||||
value: "200,201,204,301,302,307,401,403,405"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Initialize directories and prepare targets
|
||||
- name: initialize
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/discovery
|
||||
- mkdir -p {{Output}}/endpoints
|
||||
- mkdir -p {{Output}}/parameters
|
||||
- echo "{{Target}}" > {{Output}}/discovery/target.txt
|
||||
exports:
|
||||
discovery_dir: "{{Output}}/discovery"
|
||||
endpoints_dir: "{{Output}}/endpoints"
|
||||
|
||||
# Step 2: function - Determine scan intensity
|
||||
- name: determine-intensity
|
||||
type: function
|
||||
script: |
|
||||
var target = "{{Target}}";
|
||||
var wordlist = "{{wordlist_small}}";
|
||||
var threadCount = parseInt("{{threads}}");
|
||||
|
||||
// Use larger wordlist for known targets
|
||||
if (target.includes(".com") || target.includes(".org")) {
|
||||
wordlist = "{{wordlist_medium}}";
|
||||
log_info("Using medium wordlist for domain target");
|
||||
}
|
||||
|
||||
// Adjust threads based on target
|
||||
if (target.includes("localhost") || target.includes("127.0.0.1")) {
|
||||
threadCount = 100;
|
||||
log_info("Increased threads for local target");
|
||||
}
|
||||
|
||||
return JSON.stringify({wordlist: wordlist, threads: threadCount});
|
||||
exports:
|
||||
scan_config: "{{Result}}"
|
||||
|
||||
# Step 3: parallel-steps - Multiple discovery tools simultaneously
|
||||
- name: parallel-discovery
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: ffuf-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/ffuf -u {{Target}}/FUZZ -w {{wordlist_small}} -mc {{status_codes}} -t {{threads}} -o {{discovery_dir}}/ffuf.json -of json"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: gobuster-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/gobuster dir -u {{Target}} -w {{wordlist_small}} -t {{threads}} -o {{discovery_dir}}/gobuster.txt --no-error"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: feroxbuster-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/feroxbuster -u {{Target}} -w {{wordlist_small}} -t {{threads}} -o {{discovery_dir}}/feroxbuster.txt --quiet"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
|
||||
# Step 4: bash - Extract and merge discovered endpoints
|
||||
- name: merge-discoveries
|
||||
type: bash
|
||||
command: |
|
||||
# Extract URLs from ffuf JSON
|
||||
cat {{discovery_dir}}/ffuf.json 2>/dev/null | jq -r '.results[].url' >> {{discovery_dir}}/all-endpoints.txt
|
||||
# Extract from gobuster
|
||||
grep -oE 'https?://[^ ]+' {{discovery_dir}}/gobuster.txt 2>/dev/null >> {{discovery_dir}}/all-endpoints.txt
|
||||
# Extract from feroxbuster
|
||||
grep -oE 'https?://[^ ]+' {{discovery_dir}}/feroxbuster.txt 2>/dev/null >> {{discovery_dir}}/all-endpoints.txt
|
||||
# Deduplicate
|
||||
sort -u {{discovery_dir}}/all-endpoints.txt -o {{discovery_dir}}/all-endpoints.txt
|
||||
exports:
|
||||
all_endpoints: "{{discovery_dir}}/all-endpoints.txt"
|
||||
|
||||
# Step 5: function - Log discovery statistics
|
||||
- name: log-statistics
|
||||
type: function
|
||||
script: |
|
||||
var count = fileLength("{{all_endpoints}}");
|
||||
log_info("Total unique endpoints discovered: " + count);
|
||||
|
||||
if (count == 0) {
|
||||
log_warn("No endpoints discovered");
|
||||
return "empty";
|
||||
} else if (count > 500) {
|
||||
log_info("Large number of endpoints, will batch process");
|
||||
return "large";
|
||||
}
|
||||
return "normal";
|
||||
|
||||
# Step 6: foreach - Probe each endpoint for parameters
|
||||
- name: parameter-discovery
|
||||
type: foreach
|
||||
pre_condition: "fileLength('{{all_endpoints}}') > 0"
|
||||
input: "{{all_endpoints}}"
|
||||
variable: endpoint
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: probe-endpoint
|
||||
type: bash
|
||||
command: "{{Binaries}}/arjun -u [[endpoint]] -oJ {{endpoints_dir}}/params_$(echo [[endpoint]] | md5sum | cut -d' ' -f1).json"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
|
||||
# Step 7: parallel-steps - Additional content checks
|
||||
- name: additional-checks
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: wayback-urls
|
||||
type: bash
|
||||
command: "{{Binaries}}/waybackurls {{Target}} | sort -u > {{endpoints_dir}}/wayback.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: gau-fetch
|
||||
type: bash
|
||||
command: "{{Binaries}}/gau {{Target}} | sort -u > {{endpoints_dir}}/gau.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
|
||||
# Step 8: bash - Generate final content discovery report
|
||||
- name: finalize-report
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "cat {{endpoints_dir}}/*.txt 2>/dev/null | sort -u > {{Output}}/all-urls.txt"
|
||||
- "cat {{endpoints_dir}}/*.json 2>/dev/null | jq -s '.' > {{Output}}/all-params.json"
|
||||
- |
|
||||
echo "# Content Discovery Report" > {{Output}}/discovery-report.md
|
||||
echo "Target: {{Target}}" >> {{Output}}/discovery-report.md
|
||||
echo "Endpoints Found: $(wc -l < {{discovery_dir}}/all-endpoints.txt)" >> {{Output}}/discovery-report.md
|
||||
echo "Historical URLs: $(wc -l < {{endpoints_dir}}/wayback.txt 2>/dev/null || echo 0)" >> {{Output}}/discovery-report.md
|
||||
exports:
|
||||
final_urls: "{{Output}}/all-urls.txt"
|
||||
discovery_report: "{{Output}}/discovery-report.md"
|
||||
@@ -0,0 +1,242 @@
|
||||
name: data-processing
|
||||
kind: module
|
||||
desc: Data aggregation and processing workflow with function steps, decision routing, and parallel execution
|
||||
|
||||
params:
|
||||
- name: input_dir
|
||||
value: "{{Output}}"
|
||||
- name: output_format
|
||||
value: "json"
|
||||
- name: max_entries
|
||||
value: "10000"
|
||||
- name: enable_dedup
|
||||
value: "true"
|
||||
|
||||
steps:
|
||||
# Step 1: function - Initialize processing context
|
||||
- name: initialize-context
|
||||
type: function
|
||||
script: |
|
||||
log_info("Data Processing Pipeline Started");
|
||||
log_info("Input Directory: {{input_dir}}");
|
||||
log_info("Output Format: {{output_format}}");
|
||||
|
||||
// Create processing manifest
|
||||
var manifest = {
|
||||
start_time: timestamp(),
|
||||
input_dir: "{{input_dir}}",
|
||||
output_format: "{{output_format}}",
|
||||
files_processed: 0,
|
||||
total_entries: 0
|
||||
};
|
||||
|
||||
writeFile("{{Output}}/processing/manifest.json", JSON.stringify(manifest, null, 2));
|
||||
return true;
|
||||
exports:
|
||||
processing_started: "{{Result}}"
|
||||
|
||||
# Step 2: bash - Setup processing directories
|
||||
- name: setup-processing
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/processing
|
||||
- mkdir -p {{Output}}/aggregated
|
||||
- mkdir -p {{Output}}/reports
|
||||
- find {{input_dir}} -type f \( -name "*.txt" -o -name "*.json" -o -name "*.csv" \) > {{Output}}/processing/file-list.txt
|
||||
exports:
|
||||
file_list: "{{Output}}/processing/file-list.txt"
|
||||
processing_dir: "{{Output}}/processing"
|
||||
aggregated_dir: "{{Output}}/aggregated"
|
||||
|
||||
# Step 3: function with decision - Determine processing strategy
|
||||
- name: determine-strategy
|
||||
type: function
|
||||
script: |
|
||||
var fileCount = fileLength("{{file_list}}");
|
||||
log_info("Files to process: " + fileCount);
|
||||
|
||||
if (fileCount == 0) {
|
||||
log_warn("No files found to process");
|
||||
return "no_data";
|
||||
} else if (fileCount > 100) {
|
||||
log_info("Large dataset, using batch processing");
|
||||
return "batch";
|
||||
} else if (fileCount > 20) {
|
||||
log_info("Medium dataset, using parallel processing");
|
||||
return "parallel";
|
||||
}
|
||||
|
||||
log_info("Small dataset, using sequential processing");
|
||||
return "sequential";
|
||||
exports:
|
||||
processing_strategy: "{{Result}}"
|
||||
decision:
|
||||
switch: "{{processing_strategy}}"
|
||||
cases:
|
||||
"no_data":
|
||||
goto: handle-no-data
|
||||
"batch":
|
||||
goto: batch-processing
|
||||
"parallel":
|
||||
goto: parallel-processing
|
||||
"sequential":
|
||||
goto: sequential-processing
|
||||
|
||||
# Step 4a: foreach - Sequential processing for small datasets
|
||||
- name: sequential-processing
|
||||
type: foreach
|
||||
input: "{{file_list}}"
|
||||
variable: datafile
|
||||
threads: 1
|
||||
step:
|
||||
name: process-file
|
||||
type: bash
|
||||
command: |
|
||||
filename=$(basename "[[datafile]]")
|
||||
extension="${filename##*.}"
|
||||
if [ "$extension" = "json" ]; then
|
||||
cat "[[datafile]]" | jq -c '.' >> {{aggregated_dir}}/combined.jsonl 2>/dev/null || cat "[[datafile]]" >> {{aggregated_dir}}/combined.jsonl
|
||||
else
|
||||
cat "[[datafile]]" >> {{aggregated_dir}}/combined.txt
|
||||
fi
|
||||
on_error: continue
|
||||
exports:
|
||||
processing_mode: "sequential"
|
||||
|
||||
# Step 4b: parallel-steps - Parallel processing for medium datasets
|
||||
- name: parallel-processing
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: process-json-files
|
||||
type: bash
|
||||
command: "find {{input_dir}} -name '*.json' -exec cat {} \\; | jq -c '.' > {{aggregated_dir}}/all-json.jsonl 2>/dev/null || true"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: process-txt-files
|
||||
type: bash
|
||||
command: "find {{input_dir}} -name '*.txt' -exec cat {} \\; | sort -u > {{aggregated_dir}}/all-txt.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: process-csv-files
|
||||
type: bash
|
||||
command: "find {{input_dir}} -name '*.csv' -exec tail -n +2 {} \\; > {{aggregated_dir}}/all-csv.csv"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
exports:
|
||||
processing_mode: "parallel"
|
||||
|
||||
# Step 4c: bash - Batch processing for large datasets
|
||||
- name: batch-processing
|
||||
type: bash
|
||||
command: |
|
||||
# Process in batches of 50 files
|
||||
split -l 50 {{file_list}} {{processing_dir}}/batch_
|
||||
for batch in {{processing_dir}}/batch_*; do
|
||||
while read -r file; do
|
||||
cat "$file" >> {{aggregated_dir}}/batch-output.txt 2>/dev/null
|
||||
done < "$batch"
|
||||
done
|
||||
timeout: 3600
|
||||
exports:
|
||||
processing_mode: "batch"
|
||||
|
||||
# Step 4d: function - Handle no data case
|
||||
- name: handle-no-data
|
||||
type: function
|
||||
script: |
|
||||
log_warn("No data files found for processing");
|
||||
writeFile("{{Output}}/reports/no-data.txt", "No data files found in " + "{{input_dir}}");
|
||||
return false;
|
||||
exports:
|
||||
processing_mode: "skipped"
|
||||
|
||||
# Step 5: function - Deduplicate and clean data
|
||||
- name: deduplicate-data
|
||||
type: function
|
||||
pre_condition: "'{{enable_dedup}}' == 'true'"
|
||||
script: |
|
||||
log_info("Deduplicating aggregated data");
|
||||
|
||||
var txtFile = "{{aggregated_dir}}/all-txt.txt";
|
||||
if (fileExists(txtFile)) {
|
||||
var lineCount = fileLength(txtFile);
|
||||
log_info("Text entries before dedup: " + lineCount);
|
||||
sortUnix(txtFile);
|
||||
var newCount = fileLength(txtFile);
|
||||
log_info("Text entries after dedup: " + newCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
exports:
|
||||
dedup_complete: "{{Result}}"
|
||||
|
||||
# Step 6: parallel-steps - Generate multiple report formats
|
||||
- name: generate-reports
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: json-report
|
||||
type: bash
|
||||
command: |
|
||||
cat {{aggregated_dir}}/*.jsonl 2>/dev/null | head -{{max_entries}} > {{Output}}/reports/data.json
|
||||
echo '{"total": '$(wc -l < {{Output}}/reports/data.json 2>/dev/null || echo 0)'}' > {{Output}}/reports/summary.json
|
||||
on_error: continue
|
||||
- name: csv-report
|
||||
type: bash
|
||||
command: |
|
||||
echo "source,data" > {{Output}}/reports/data.csv
|
||||
cat {{aggregated_dir}}/*.txt 2>/dev/null | head -{{max_entries}} | while read line; do
|
||||
echo "aggregated,\"$line\"" >> {{Output}}/reports/data.csv
|
||||
done
|
||||
on_error: continue
|
||||
- name: markdown-report
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/reports/report.md << EOF
|
||||
# Data Processing Report
|
||||
|
||||
**Target:** {{Target}}
|
||||
**Processing Mode:** {{processing_mode}}
|
||||
**Generated:** $(date)
|
||||
|
||||
## Statistics
|
||||
- Input Directory: {{input_dir}}
|
||||
- Files Processed: $(wc -l < {{file_list}})
|
||||
- Output Format: {{output_format}}
|
||||
|
||||
## Files Generated
|
||||
- data.json
|
||||
- data.csv
|
||||
- summary.json
|
||||
EOF
|
||||
|
||||
# Step 7: function - Calculate final statistics
|
||||
- name: calculate-statistics
|
||||
type: function
|
||||
script: |
|
||||
var stats = {
|
||||
processing_mode: "{{processing_mode}}",
|
||||
files_processed: fileLength("{{file_list}}"),
|
||||
dedup_enabled: "{{enable_dedup}}" === "true",
|
||||
output_format: "{{output_format}}",
|
||||
completion_time: timestamp()
|
||||
};
|
||||
|
||||
log_info("Processing Complete:");
|
||||
log_info(" Mode: " + stats.processing_mode);
|
||||
log_info(" Files: " + stats.files_processed);
|
||||
|
||||
writeFile("{{Output}}/reports/stats.json", JSON.stringify(stats, null, 2));
|
||||
return JSON.stringify(stats);
|
||||
exports:
|
||||
final_stats: "{{Result}}"
|
||||
|
||||
# Step 8: bash - Archive and cleanup
|
||||
- name: archive-results
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "tar -czf {{Output}}/data-archive.tar.gz -C {{Output}} reports aggregated 2>/dev/null || true"
|
||||
- "rm -rf {{processing_dir}}/batch_* 2>/dev/null || true"
|
||||
- "echo 'Processing pipeline completed at:' $(date) > {{Output}}/COMPLETED.txt"
|
||||
exports:
|
||||
archive_file: "{{Output}}/data-archive.tar.gz"
|
||||
pipeline_complete: "true"
|
||||
@@ -0,0 +1,184 @@
|
||||
name: full-assessment-flow
|
||||
kind: flow
|
||||
desc: Complete security assessment flow combining reconnaissance, vulnerability scanning, and data processing
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "30"
|
||||
- name: scan_depth
|
||||
value: "standard"
|
||||
- name: enable_bruteforce
|
||||
value: "false"
|
||||
- name: output_format
|
||||
value: "json"
|
||||
|
||||
modules:
|
||||
# Module 1: Reconnaissance - Asset discovery and enumeration
|
||||
- name: recon
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-recon
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/recon/subdomains
|
||||
- mkdir -p {{Output}}/recon/ports
|
||||
- mkdir -p {{Output}}/recon/tech
|
||||
exports:
|
||||
recon_dir: "{{Output}}/recon"
|
||||
subdomain_dir: "{{Output}}/recon/subdomains"
|
||||
ports_dir: "{{Output}}/recon/ports"
|
||||
|
||||
- name: subdomain-discovery
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: subfinder
|
||||
type: bash
|
||||
command: "{{Binaries}}/subfinder -d {{Target}} -silent -o {{subdomain_dir}}/subfinder.txt"
|
||||
timeout: 600
|
||||
- name: amass
|
||||
type: bash
|
||||
command: "{{Binaries}}/amass enum -passive -d {{Target}} -o {{subdomain_dir}}/amass.txt"
|
||||
timeout: 900
|
||||
on_error: continue
|
||||
- name: crt-sh
|
||||
type: bash
|
||||
command: "curl -s 'https://crt.sh/?q=%25.{{Target}}&output=json' | jq -r '.[].name_value' | sort -u > {{subdomain_dir}}/crtsh.txt"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
|
||||
- name: merge-and-resolve
|
||||
type: bash
|
||||
commands:
|
||||
- cat {{subdomain_dir}}/*.txt | sort -u > {{recon_dir}}/all-subdomains.txt
|
||||
- "{{Binaries}}/dnsx -l {{recon_dir}}/all-subdomains.txt -silent -a -resp -o {{recon_dir}}/resolved.txt"
|
||||
exports:
|
||||
all_subdomains: "{{recon_dir}}/all-subdomains.txt"
|
||||
resolved_hosts: "{{recon_dir}}/resolved.txt"
|
||||
|
||||
# Module 2: Vulnerability Scanning - Security assessment
|
||||
- name: vuln-scan
|
||||
depends_on:
|
||||
- recon
|
||||
condition: "fileLength('{{all_subdomains}}') > 0"
|
||||
steps:
|
||||
- name: init-vulns
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/vulns/nuclei
|
||||
- mkdir -p {{Output}}/vulns/web
|
||||
exports:
|
||||
vulns_dir: "{{Output}}/vulns"
|
||||
|
||||
- name: http-probe
|
||||
type: bash
|
||||
command: "{{Binaries}}/httpx -l {{all_subdomains}} -silent -status-code -title -tech-detect -o {{vulns_dir}}/http-probe.txt"
|
||||
timeout: 900
|
||||
exports:
|
||||
http_hosts: "{{vulns_dir}}/http-probe.txt"
|
||||
|
||||
- name: vulnerability-scans
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: nuclei-critical
|
||||
type: bash
|
||||
command: "{{Binaries}}/nuclei -l {{http_hosts}} -severity critical,high -c {{threads}} -o {{vulns_dir}}/nuclei/critical.json -jsonl"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: nuclei-medium
|
||||
type: bash
|
||||
command: "{{Binaries}}/nuclei -l {{http_hosts}} -severity medium -c {{threads}} -o {{vulns_dir}}/nuclei/medium.json -jsonl"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: tech-detect
|
||||
type: bash
|
||||
command: "{{Binaries}}/whatweb -i {{http_hosts}} --log-json={{vulns_dir}}/web/tech.json"
|
||||
timeout: 1800
|
||||
on_error: continue
|
||||
exports:
|
||||
critical_vulns: "{{vulns_dir}}/nuclei/critical.json"
|
||||
medium_vulns: "{{vulns_dir}}/nuclei/medium.json"
|
||||
|
||||
# Module 3: Data Processing - Aggregate and report
|
||||
- name: data-processing
|
||||
depends_on:
|
||||
- vuln-scan
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-processing
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/reports
|
||||
- mkdir -p {{Output}}/aggregated
|
||||
exports:
|
||||
reports_dir: "{{Output}}/reports"
|
||||
aggregated_dir: "{{Output}}/aggregated"
|
||||
|
||||
- name: aggregate-findings
|
||||
type: function
|
||||
script: |
|
||||
var summary = {
|
||||
target: "{{Target}}",
|
||||
scan_depth: "{{scan_depth}}",
|
||||
timestamp: timestamp(),
|
||||
statistics: {
|
||||
subdomains: fileLength("{{all_subdomains}}"),
|
||||
http_hosts: fileLength("{{http_hosts}}"),
|
||||
critical_findings: 0,
|
||||
medium_findings: 0
|
||||
}
|
||||
};
|
||||
|
||||
if (fileExists("{{critical_vulns}}")) {
|
||||
summary.statistics.critical_findings = fileLength("{{critical_vulns}}");
|
||||
}
|
||||
if (fileExists("{{medium_vulns}}")) {
|
||||
summary.statistics.medium_findings = fileLength("{{medium_vulns}}");
|
||||
}
|
||||
|
||||
log_info("Assessment Summary:");
|
||||
log_info(" Subdomains: " + summary.statistics.subdomains);
|
||||
log_info(" HTTP Hosts: " + summary.statistics.http_hosts);
|
||||
log_info(" Critical: " + summary.statistics.critical_findings);
|
||||
log_info(" Medium: " + summary.statistics.medium_findings);
|
||||
|
||||
writeFile("{{aggregated_dir}}/summary.json", JSON.stringify(summary, null, 2));
|
||||
return JSON.stringify(summary.statistics);
|
||||
exports:
|
||||
assessment_stats: "{{Result}}"
|
||||
|
||||
- name: generate-final-report
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{reports_dir}}/full-assessment.md << 'EOF'
|
||||
# Full Security Assessment Report
|
||||
|
||||
## Target Information
|
||||
- **Target:** {{Target}}
|
||||
- **Scan Depth:** {{scan_depth}}
|
||||
- **Generated:** $(date)
|
||||
|
||||
## Executive Summary
|
||||
This report contains findings from a comprehensive security assessment including:
|
||||
- Subdomain enumeration and DNS resolution
|
||||
- HTTP service discovery and technology detection
|
||||
- Vulnerability scanning with multiple severity levels
|
||||
|
||||
## Statistics
|
||||
{{assessment_stats}}
|
||||
|
||||
## Methodology
|
||||
1. **Reconnaissance**: Passive and active subdomain enumeration
|
||||
2. **Service Discovery**: HTTP probing and technology fingerprinting
|
||||
3. **Vulnerability Assessment**: Template-based scanning for known vulnerabilities
|
||||
|
||||
## Files Generated
|
||||
- `recon/all-subdomains.txt` - Discovered subdomains
|
||||
- `vulns/nuclei/*.json` - Vulnerability findings
|
||||
- `aggregated/summary.json` - Machine-readable summary
|
||||
|
||||
## Recommendations
|
||||
Review all critical and high severity findings immediately.
|
||||
Medium severity findings should be addressed in the next security sprint.
|
||||
EOF
|
||||
exports:
|
||||
final_report: "{{reports_dir}}/full-assessment.md"
|
||||
@@ -0,0 +1,32 @@
|
||||
name: http-probing
|
||||
kind: module
|
||||
description: Running HTTP fingerprint technology and response with the supplied inputs
|
||||
|
||||
params:
|
||||
- name: inputFile
|
||||
default: "{{Target}}"
|
||||
- name: httpFile
|
||||
default: "{{Output}}/fingerprint/http-{{Workspace}}.txt"
|
||||
- name: httpThreads
|
||||
default: "{{ threads * 10 }}"
|
||||
- name: httpTimeout
|
||||
default: "10"
|
||||
- name: defaultUA
|
||||
default: "User-Agent: Mozilla/5.0 (compatible; Osmedeus/v4; +https://github.com/j3ssie/osmedeus)"
|
||||
|
||||
steps:
|
||||
- name: httpx-probe
|
||||
type: bash
|
||||
commands:
|
||||
- "echo {{inputFile}} | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -silent -t {{httpThreads}} >> {{httpFile}}"
|
||||
|
||||
- name: httpx-fingerprint
|
||||
type: bash
|
||||
pre_condition: "fileExists('{{httpFile}}')"
|
||||
command: >
|
||||
cat {{httpFile}} | {{Binaries}}/httpx -H '{{defaultUA}}' -timeout {{httpTimeout}}
|
||||
-t {{httpThreads}} -no-fallback -no-color -silent -json -title -favicon
|
||||
-hash sha256 -jarm -tech-detect -status-code -cdn -tls-grab -ztls -vhost
|
||||
-follow-host-redirects -include-chain -store-response
|
||||
-store-response-dir {{Output}}/fingerprint/raw-data
|
||||
>> {{Output}}/fingerprint/{{Workspace}}-http-overview.txt
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
name: port-scanning
|
||||
kind: module
|
||||
desc: Port and service scanning workflow with decision routing and parallel execution
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "50"
|
||||
- name: rate_limit
|
||||
value: "1000"
|
||||
- name: ports
|
||||
value: "top-1000"
|
||||
- name: scan_type
|
||||
value: "standard"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Setup directories
|
||||
- name: setup
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/ports
|
||||
- mkdir -p {{Output}}/services
|
||||
- mkdir -p {{Output}}/banners
|
||||
exports:
|
||||
ports_dir: "{{Output}}/ports"
|
||||
services_dir: "{{Output}}/services"
|
||||
|
||||
# Step 2: function - Determine port range based on scan_type
|
||||
- name: configure-ports
|
||||
type: function
|
||||
script: |
|
||||
var scanType = "{{scan_type}}";
|
||||
var portRange = "";
|
||||
if (scanType == "quick") {
|
||||
portRange = "21,22,23,25,80,110,143,443,445,3306,3389,8080";
|
||||
} else if (scanType == "full") {
|
||||
portRange = "1-65535";
|
||||
} else {
|
||||
portRange = "1-10000";
|
||||
}
|
||||
log_info("Port range: " + portRange + " for scan type: " + scanType);
|
||||
return portRange;
|
||||
exports:
|
||||
port_range: "{{Result}}"
|
||||
|
||||
# Step 3: parallel-steps - Run multiple port scanners
|
||||
- name: port-discovery
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: naabu-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/naabu -host {{Target}} -p {{port_range}} -rate {{rate_limit}} -silent -o {{ports_dir}}/naabu.txt"
|
||||
timeout: 3600
|
||||
- name: masscan-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/masscan {{Target}} -p{{port_range}} --rate={{rate_limit}} -oL {{ports_dir}}/masscan.txt"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
|
||||
# Step 4: bash - Merge port results
|
||||
- name: merge-ports
|
||||
type: bash
|
||||
command: |
|
||||
cat {{ports_dir}}/naabu.txt 2>/dev/null | sort -u > {{ports_dir}}/all-ports.txt
|
||||
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+' {{ports_dir}}/masscan.txt 2>/dev/null | sort -u >> {{ports_dir}}/all-ports.txt
|
||||
sort -u {{ports_dir}}/all-ports.txt -o {{ports_dir}}/all-ports.txt
|
||||
exports:
|
||||
open_ports: "{{ports_dir}}/all-ports.txt"
|
||||
|
||||
# Step 5: function with decision - Check port count and route
|
||||
- name: analyze-results
|
||||
type: function
|
||||
script: |
|
||||
var portCount = fileLength("{{open_ports}}");
|
||||
log_info("Total open ports found: " + portCount);
|
||||
if (portCount == 0) {
|
||||
return "no_ports";
|
||||
} else if (portCount > 100) {
|
||||
return "many_ports";
|
||||
}
|
||||
return "normal";
|
||||
exports:
|
||||
port_analysis_result: "{{Result}}"
|
||||
decision:
|
||||
switch: "{{port_analysis_result}}"
|
||||
cases:
|
||||
"no_ports":
|
||||
goto: skip-service-detection
|
||||
"many_ports":
|
||||
goto: batch-service-detection
|
||||
"normal":
|
||||
goto: service-detection
|
||||
|
||||
# Step 6: foreach - Standard service detection
|
||||
- name: service-detection
|
||||
type: foreach
|
||||
input: "{{open_ports}}"
|
||||
variable: target_port
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: detect-service
|
||||
type: bash
|
||||
command: "{{Binaries}}/nmap -sV -sC -p [[target_port]] -oN {{services_dir}}/[[target_port]].txt {{Target}}"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
exports:
|
||||
detection_complete: "true"
|
||||
|
||||
# Step 7: bash - Batch service detection for many ports
|
||||
- name: batch-service-detection
|
||||
type: bash
|
||||
pre_condition: "fileLength('{{open_ports}}') > 100"
|
||||
command: "{{Binaries}}/nmap -sV --version-intensity 5 -iL {{open_ports}} -oN {{services_dir}}/batch-scan.txt"
|
||||
timeout: 7200
|
||||
exports:
|
||||
detection_complete: "true"
|
||||
|
||||
# Step 8: bash - Final report generation
|
||||
- name: generate-report
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "cat {{services_dir}}/*.txt 2>/dev/null | grep -E 'open|filtered' > {{Output}}/service-summary.txt"
|
||||
- "echo 'Port Scan Report for {{Target}}' > {{Output}}/report.txt && date >> {{Output}}/report.txt && wc -l {{open_ports}} >> {{Output}}/report.txt"
|
||||
exports:
|
||||
port_report: "{{Output}}/report.txt"
|
||||
|
||||
# Placeholder for skip case
|
||||
- name: skip-service-detection
|
||||
type: function
|
||||
script: |
|
||||
log_warn("No open ports found, skipping service detection");
|
||||
return true;
|
||||
@@ -0,0 +1,178 @@
|
||||
name: screenshot-capture
|
||||
kind: module
|
||||
desc: Web screenshot and visual analysis workflow with pre_condition checks and exports
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "10"
|
||||
- name: timeout_per_page
|
||||
value: "30"
|
||||
- name: viewport_width
|
||||
value: "1920"
|
||||
- name: viewport_height
|
||||
value: "1080"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Setup screenshot directories
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/screenshots
|
||||
- mkdir -p {{Output}}/thumbnails
|
||||
- mkdir -p {{Output}}/analysis
|
||||
exports:
|
||||
screenshots_dir: "{{Output}}/screenshots"
|
||||
thumbnails_dir: "{{Output}}/thumbnails"
|
||||
analysis_dir: "{{Output}}/analysis"
|
||||
|
||||
# Step 2: function - Validate URL input and prepare target list
|
||||
- name: prepare-targets
|
||||
type: function
|
||||
script: |
|
||||
var target = "{{Target}}";
|
||||
var urls = [];
|
||||
|
||||
// Check if target is a file or single URL
|
||||
if (fileExists(target)) {
|
||||
log_info("Target is a file, reading URLs");
|
||||
var content = readFile(target);
|
||||
urls = content.trim().split("\n").filter(function(u) { return u.length > 0; });
|
||||
} else {
|
||||
// Ensure URL has protocol
|
||||
if (!target.startsWith("http")) {
|
||||
target = "https://" + target;
|
||||
}
|
||||
urls = [target];
|
||||
log_info("Single target mode: " + target);
|
||||
}
|
||||
|
||||
log_info("Total URLs to screenshot: " + urls.length);
|
||||
writeFile("{{Output}}/url-list.txt", urls.join("\n"));
|
||||
return urls.length;
|
||||
exports:
|
||||
url_count: "{{Result}}"
|
||||
url_list: "{{Output}}/url-list.txt"
|
||||
|
||||
# Step 3: bash with pre_condition - Quick probe to filter live URLs
|
||||
- name: probe-live-urls
|
||||
type: bash
|
||||
pre_condition: "fileExists('{{url_list}}')"
|
||||
command: "{{Binaries}}/httpx -l {{url_list}} -silent -mc 200,201,301,302,307,401,403 -o {{Output}}/live-urls.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
exports:
|
||||
live_urls: "{{Output}}/live-urls.txt"
|
||||
|
||||
# Step 4: function - Check live URL count before proceeding
|
||||
- name: validate-live-urls
|
||||
type: function
|
||||
pre_condition: "fileExists('{{live_urls}}')"
|
||||
script: |
|
||||
var count = fileLength("{{live_urls}}");
|
||||
log_info("Live URLs found: " + count);
|
||||
|
||||
if (count == 0) {
|
||||
log_warn("No live URLs found, screenshots may fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
return count > 0;
|
||||
exports:
|
||||
has_live_urls: "{{Result}}"
|
||||
|
||||
# Step 5: foreach - Capture screenshots of each URL
|
||||
- name: capture-screenshots
|
||||
type: foreach
|
||||
pre_condition: "fileExists('{{live_urls}}') && fileLength('{{live_urls}}') > 0"
|
||||
input: "{{live_urls}}"
|
||||
variable: url
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: screenshot-single
|
||||
type: bash
|
||||
command: |
|
||||
filename=$(echo "[[url]]" | md5sum | cut -d' ' -f1)
|
||||
{{Binaries}}/gowitness single --url="[[url]]" --screenshot-path={{screenshots_dir}} --screenshot-filename=${filename}.png --timeout={{timeout_per_page}}
|
||||
timeout: 60
|
||||
on_error: continue
|
||||
|
||||
# Step 6: parallel-steps - Generate thumbnails and analyze
|
||||
- name: process-screenshots
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: generate-thumbnails
|
||||
type: bash
|
||||
command: |
|
||||
for img in {{screenshots_dir}}/*.png; do
|
||||
if [ -f "$img" ]; then
|
||||
filename=$(basename "$img" .png)
|
||||
convert "$img" -resize 400x300 {{thumbnails_dir}}/${filename}_thumb.png 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
timeout: 300
|
||||
on_error: continue
|
||||
- name: extract-metadata
|
||||
type: bash
|
||||
command: |
|
||||
echo "[]" > {{analysis_dir}}/metadata.json
|
||||
for img in {{screenshots_dir}}/*.png; do
|
||||
if [ -f "$img" ]; then
|
||||
size=$(stat -f%z "$img" 2>/dev/null || stat -c%s "$img" 2>/dev/null)
|
||||
echo "{\"file\": \"$(basename $img)\", \"size\": $size}" >> {{analysis_dir}}/metadata.json
|
||||
fi
|
||||
done
|
||||
timeout: 120
|
||||
|
||||
# Step 7: function - Generate screenshot statistics
|
||||
- name: generate-stats
|
||||
type: function
|
||||
script: |
|
||||
var stats = {
|
||||
total_urls: parseInt("{{url_count}}"),
|
||||
live_urls: fileLength("{{live_urls}}"),
|
||||
screenshots: 0,
|
||||
thumbnails: 0
|
||||
};
|
||||
|
||||
// Count screenshot files
|
||||
var screenshotDir = "{{screenshots_dir}}";
|
||||
log_info("Screenshot capture statistics:");
|
||||
log_info(" Total URLs: " + stats.total_urls);
|
||||
log_info(" Live URLs: " + stats.live_urls);
|
||||
|
||||
writeFile("{{analysis_dir}}/stats.json", JSON.stringify(stats, null, 2));
|
||||
return JSON.stringify(stats);
|
||||
exports:
|
||||
screenshot_stats: "{{Result}}"
|
||||
|
||||
# Step 8: bash - Create HTML gallery and final report
|
||||
- name: create-gallery
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
cat > {{Output}}/gallery.html << 'HTMLEOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Screenshot Gallery - {{Target}}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 20px; }
|
||||
.item { border: 1px solid #ddd; padding: 10px; }
|
||||
.item img { max-width: 100%; height: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Screenshot Gallery</h1>
|
||||
<p>Target: {{Target}}</p>
|
||||
<div class="grid">
|
||||
HTMLEOF
|
||||
- |
|
||||
for img in {{screenshots_dir}}/*.png; do
|
||||
if [ -f "$img" ]; then
|
||||
filename=$(basename "$img")
|
||||
echo "<div class='item'><img src='screenshots/$filename'><p>$filename</p></div>" >> {{Output}}/gallery.html
|
||||
fi
|
||||
done
|
||||
echo "</div></body></html>" >> {{Output}}/gallery.html
|
||||
exports:
|
||||
gallery_html: "{{Output}}/gallery.html"
|
||||
@@ -0,0 +1,102 @@
|
||||
name: subdomain-enumeration
|
||||
kind: module
|
||||
desc: Comprehensive subdomain enumeration workflow demonstrating various step types
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "10"
|
||||
- name: resolvers
|
||||
value: "{{Data}}/resolvers.txt"
|
||||
- name: wordlist
|
||||
value: "{{Data}}/subdomains-top1million-5000.txt"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Initialize output directories
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/subdomains
|
||||
- mkdir -p {{Output}}/resolved
|
||||
- mkdir -p {{Output}}/wordlists
|
||||
exports:
|
||||
subdomain_dir: "{{Output}}/subdomains"
|
||||
resolved_dir: "{{Output}}/resolved"
|
||||
|
||||
# Step 2: function - Log start and validate target
|
||||
- name: validate-target
|
||||
type: function
|
||||
script: |
|
||||
log_info("Starting subdomain enumeration for: {{Target}}");
|
||||
if (isEmpty("{{Target}}")) {
|
||||
log_error("Target is empty");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
# Step 3: parallel-steps - Run multiple passive enumeration tools concurrently
|
||||
- name: passive-enumeration
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: subfinder-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/subfinder -d {{Target}} -silent -o {{subdomain_dir}}/subfinder.txt"
|
||||
timeout: 600
|
||||
- name: amass-passive
|
||||
type: bash
|
||||
command: "{{Binaries}}/amass enum -passive -d {{Target}} -o {{subdomain_dir}}/amass.txt"
|
||||
timeout: 900
|
||||
- name: assetfinder-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/assetfinder --subs-only {{Target}} > {{subdomain_dir}}/assetfinder.txt"
|
||||
timeout: 300
|
||||
|
||||
# Step 4: bash - Merge and deduplicate results
|
||||
- name: merge-results
|
||||
type: bash
|
||||
command: "cat {{subdomain_dir}}/*.txt | sort -u > {{subdomain_dir}}/all-subdomains.txt"
|
||||
exports:
|
||||
all_subdomains: "{{subdomain_dir}}/all-subdomains.txt"
|
||||
|
||||
# Step 5: function - Check if we found any subdomains
|
||||
- name: check-results
|
||||
type: function
|
||||
script: |
|
||||
var count = fileLength("{{all_subdomains}}");
|
||||
log_info("Found " + count + " unique subdomains");
|
||||
if (count == 0) {
|
||||
log_warn("No subdomains found, trying bruteforce");
|
||||
}
|
||||
return count;
|
||||
exports:
|
||||
subdomain_count: "{{Result}}"
|
||||
|
||||
# Step 6: bash with pre_condition - Active bruteforce if passive found few results
|
||||
- name: active-bruteforce
|
||||
type: bash
|
||||
pre_condition: "fileLength('{{all_subdomains}}') < 50"
|
||||
command: "{{Binaries}}/puredns bruteforce {{wordlist}} {{Target}} -r {{resolvers}} -w {{subdomain_dir}}/bruteforce.txt"
|
||||
timeout: 1800
|
||||
on_error: continue
|
||||
|
||||
# Step 7: foreach - Resolve each subdomain for live hosts
|
||||
- name: resolve-subdomains
|
||||
type: foreach
|
||||
input: "{{all_subdomains}}"
|
||||
variable: subdomain
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: resolve-single
|
||||
type: bash
|
||||
command: "echo [[subdomain]] | {{Binaries}}/dnsx -silent -a -resp -o {{resolved_dir}}/[[subdomain]].txt"
|
||||
timeout: 30
|
||||
on_error: continue
|
||||
|
||||
# Step 8: bash with parallel_commands - Final aggregation
|
||||
- name: final-aggregation
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "cat {{resolved_dir}}/*.txt 2>/dev/null | grep -v '^$' | sort -u > {{Output}}/resolved-subdomains.txt"
|
||||
- "wc -l {{subdomain_dir}}/all-subdomains.txt | awk '{print $1}' > {{Output}}/stats.txt"
|
||||
- "echo 'Enumeration completed at:' $(date) >> {{Output}}/stats.txt"
|
||||
exports:
|
||||
final_subdomains: "{{Output}}/resolved-subdomains.txt"
|
||||
@@ -0,0 +1,143 @@
|
||||
name: vulnerability-assessment
|
||||
kind: module
|
||||
desc: Vulnerability scanning with Docker runner and comprehensive error handling
|
||||
|
||||
runner: docker
|
||||
runner_config:
|
||||
image: "osmedeus/scanner:latest"
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
- "{{Data}}:/data"
|
||||
network: "host"
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "25"
|
||||
- name: severity
|
||||
value: "medium,high,critical"
|
||||
- name: templates_path
|
||||
value: "{{Data}}/nuclei-templates"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Setup scan environment
|
||||
- name: setup-environment
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/vulns
|
||||
- mkdir -p {{Output}}/findings
|
||||
- mkdir -p {{Output}}/raw
|
||||
exports:
|
||||
vulns_dir: "{{Output}}/vulns"
|
||||
findings_dir: "{{Output}}/findings"
|
||||
|
||||
# Step 2: function - Validate inputs and log configuration
|
||||
- name: validate-config
|
||||
type: function
|
||||
script: |
|
||||
log_info("Vulnerability Assessment Configuration:");
|
||||
log_info(" Target: {{Target}}");
|
||||
log_info(" Severity: {{severity}}");
|
||||
log_info(" Threads: {{threads}}");
|
||||
|
||||
if (!fileExists("{{templates_path}}")) {
|
||||
log_warn("Templates path not found, using default");
|
||||
}
|
||||
return true;
|
||||
|
||||
# Step 3: remote-bash (docker) - Run nuclei scan
|
||||
- name: nuclei-scan
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: "projectdiscovery/nuclei:latest"
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
- "{{templates_path}}:/templates"
|
||||
command: "nuclei -u {{Target}} -t /templates -severity {{severity}} -c {{threads}} -o /output/vulns/nuclei.json -jsonl"
|
||||
timeout: 7200
|
||||
on_error: continue
|
||||
exports:
|
||||
nuclei_results: "{{vulns_dir}}/nuclei.json"
|
||||
|
||||
# Step 4: bash - Run local vulnerability checks
|
||||
- name: local-vuln-checks
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "{{Binaries}}/nikto -h {{Target}} -output {{vulns_dir}}/nikto.txt -Format txt"
|
||||
- "{{Binaries}}/whatweb {{Target}} --log-json={{vulns_dir}}/whatweb.json"
|
||||
timeout: 1800
|
||||
on_error: continue
|
||||
|
||||
# Step 5: foreach - Check each finding for exploitability
|
||||
- name: verify-findings
|
||||
type: foreach
|
||||
pre_condition: "fileExists('{{nuclei_results}}')"
|
||||
input: "{{nuclei_results}}"
|
||||
variable: finding
|
||||
threads: 5
|
||||
step:
|
||||
name: verify-single
|
||||
type: function
|
||||
script: |
|
||||
var finding = "[[finding]]";
|
||||
log_debug("Verifying finding: " + finding);
|
||||
return true;
|
||||
on_error: continue
|
||||
|
||||
# Step 6: parallel-steps - Additional scanning modules
|
||||
- name: extended-scanning
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: ssl-check
|
||||
type: bash
|
||||
command: "{{Binaries}}/testssl --jsonfile={{vulns_dir}}/ssl.json {{Target}}"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: header-check
|
||||
type: bash
|
||||
command: "curl -sI {{Target}} | tee {{vulns_dir}}/headers.txt"
|
||||
timeout: 60
|
||||
- name: cors-check
|
||||
type: bash
|
||||
command: "{{Binaries}}/corsy -u {{Target}} -o {{vulns_dir}}/cors.json"
|
||||
timeout: 300
|
||||
on_error: continue
|
||||
|
||||
# Step 7: function - Aggregate and calculate risk score
|
||||
- name: calculate-risk
|
||||
type: function
|
||||
script: |
|
||||
var critical = 0;
|
||||
var high = 0;
|
||||
var medium = 0;
|
||||
|
||||
if (fileExists("{{nuclei_results}}")) {
|
||||
var content = readFile("{{nuclei_results}}");
|
||||
critical = (content.match(/critical/gi) || []).length;
|
||||
high = (content.match(/high/gi) || []).length;
|
||||
medium = (content.match(/medium/gi) || []).length;
|
||||
}
|
||||
|
||||
var riskScore = (critical * 10) + (high * 5) + (medium * 2);
|
||||
log_info("Risk Score: " + riskScore);
|
||||
log_info("Critical: " + critical + ", High: " + high + ", Medium: " + medium);
|
||||
|
||||
writeFile("{{findings_dir}}/risk-score.txt", "Risk Score: " + riskScore);
|
||||
return riskScore;
|
||||
exports:
|
||||
risk_score: "{{Result}}"
|
||||
|
||||
# Step 8: bash - Generate final vulnerability report
|
||||
- name: generate-vuln-report
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "# Vulnerability Assessment Report" > {{Output}}/vuln-report.md
|
||||
echo "Target: {{Target}}" >> {{Output}}/vuln-report.md
|
||||
echo "Date: $(date)" >> {{Output}}/vuln-report.md
|
||||
echo "Risk Score: {{risk_score}}" >> {{Output}}/vuln-report.md
|
||||
echo "" >> {{Output}}/vuln-report.md
|
||||
echo "## Findings" >> {{Output}}/vuln-report.md
|
||||
cat {{vulns_dir}}/nuclei.json 2>/dev/null | head -50 >> {{Output}}/vuln-report.md
|
||||
exports:
|
||||
vuln_report: "{{Output}}/vuln-report.md"
|
||||
@@ -0,0 +1,130 @@
|
||||
name: vulnerability-flow
|
||||
kind: flow
|
||||
desc: Vulnerability assessment flow with discovery, scanning, and reporting modules
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "25"
|
||||
- name: severity
|
||||
value: "medium,high,critical"
|
||||
- name: templates
|
||||
value: "{{Data}}/nuclei-templates"
|
||||
|
||||
modules:
|
||||
# Module 1: Discovery - Find attack surface
|
||||
- name: discovery
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-discovery
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/discovery
|
||||
- mkdir -p {{Output}}/endpoints
|
||||
exports:
|
||||
discovery_dir: "{{Output}}/discovery"
|
||||
endpoints_dir: "{{Output}}/endpoints"
|
||||
|
||||
- name: find-endpoints
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "{{Binaries}}/waybackurls {{Target}} > {{endpoints_dir}}/wayback.txt"
|
||||
- "{{Binaries}}/gau {{Target}} > {{endpoints_dir}}/gau.txt"
|
||||
- "{{Binaries}}/katana -u {{Target}} -silent -o {{endpoints_dir}}/katana.txt"
|
||||
timeout: 900
|
||||
on_error: continue
|
||||
|
||||
- name: merge-endpoints
|
||||
type: bash
|
||||
command: |
|
||||
cat {{endpoints_dir}}/*.txt | sort -u > {{discovery_dir}}/all-endpoints.txt
|
||||
grep -E '\.(php|asp|aspx|jsp|cgi)' {{discovery_dir}}/all-endpoints.txt > {{discovery_dir}}/dynamic-endpoints.txt || true
|
||||
exports:
|
||||
all_endpoints: "{{discovery_dir}}/all-endpoints.txt"
|
||||
dynamic_endpoints: "{{discovery_dir}}/dynamic-endpoints.txt"
|
||||
|
||||
# Module 2: Scanning - Run vulnerability scanners
|
||||
- name: scanning
|
||||
depends_on:
|
||||
- discovery
|
||||
condition: "fileLength('{{all_endpoints}}') > 0"
|
||||
steps:
|
||||
- name: init-scanning
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/vulns
|
||||
- mkdir -p {{Output}}/findings
|
||||
exports:
|
||||
vulns_dir: "{{Output}}/vulns"
|
||||
findings_dir: "{{Output}}/findings"
|
||||
|
||||
- name: nuclei-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/nuclei -l {{all_endpoints}} -t {{templates}} -severity {{severity}} -c {{threads}} -o {{vulns_dir}}/nuclei.json -jsonl"
|
||||
timeout: 7200
|
||||
on_error: continue
|
||||
exports:
|
||||
nuclei_results: "{{vulns_dir}}/nuclei.json"
|
||||
|
||||
- name: additional-scans
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: xss-scan
|
||||
type: bash
|
||||
command: "cat {{dynamic_endpoints}} | {{Binaries}}/dalfox pipe -o {{vulns_dir}}/xss.txt"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: sqli-check
|
||||
type: bash
|
||||
command: "{{Binaries}}/sqlmap -m {{dynamic_endpoints}} --batch --output-dir={{vulns_dir}}/sqli"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
|
||||
# Module 3: Reporting - Generate vulnerability reports
|
||||
- name: reporting
|
||||
depends_on:
|
||||
- scanning
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-reports
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/reports
|
||||
|
||||
- name: aggregate-findings
|
||||
type: function
|
||||
script: |
|
||||
var findings = [];
|
||||
var nucleiFile = "{{nuclei_results}}";
|
||||
|
||||
if (fileExists(nucleiFile)) {
|
||||
var content = readFile(nucleiFile);
|
||||
var lines = content.split("\n").filter(function(l) { return l.trim().length > 0; });
|
||||
findings = lines.map(function(l) {
|
||||
try { return JSON.parse(l); } catch(e) { return {raw: l}; }
|
||||
});
|
||||
}
|
||||
|
||||
log_info("Total findings aggregated: " + findings.length);
|
||||
writeFile("{{Output}}/reports/findings.json", JSON.stringify(findings, null, 2));
|
||||
return findings.length;
|
||||
exports:
|
||||
finding_count: "{{Result}}"
|
||||
|
||||
- name: generate-report
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/reports/vulnerability-report.md << EOF
|
||||
# Vulnerability Assessment Report
|
||||
|
||||
**Target:** {{Target}}
|
||||
**Date:** $(date)
|
||||
**Severity Filter:** {{severity}}
|
||||
|
||||
## Summary
|
||||
- Total Findings: {{finding_count}}
|
||||
- Endpoints Scanned: $(wc -l < {{all_endpoints}})
|
||||
|
||||
## Detailed Findings
|
||||
See findings.json for complete details.
|
||||
EOF
|
||||
exports:
|
||||
final_report: "{{Output}}/reports/vulnerability-report.md"
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
name: vulnscan
|
||||
kind: module
|
||||
description: Run vulnerability scan on all HTTP hosts using Jaeles and Nuclei scanners
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: httpFile
|
||||
default: "{{Output}}/probing/http-{{Workspace}}.txt"
|
||||
- name: output_dir
|
||||
default: "{{Output}}/vuln"
|
||||
- name: sign
|
||||
default: "~/.jaeles/base-signatures/cves/.*"
|
||||
- name: sign2
|
||||
default: "~/.jaeles/base-signatures/common/.*"
|
||||
- name: sign3
|
||||
default: "~/.jaeles/base-signatures/sensitive/.*"
|
||||
- name: splitLines
|
||||
default: "500"
|
||||
- name: limit
|
||||
default: "25000"
|
||||
- name: extra
|
||||
default: " "
|
||||
- name: enableNuclei
|
||||
default: "true"
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: nucleiThreads
|
||||
default: "{{threads * 10}}"
|
||||
- name: jaelesThreads
|
||||
default: "{{threads * 5}}"
|
||||
- name: nucleiTimeout
|
||||
default: "8h"
|
||||
- name: jaelesTimeout
|
||||
default: "3h"
|
||||
- name: nucleiSeverity
|
||||
default: "critical,high,medium,low,info"
|
||||
- name: defaultUA
|
||||
default: "User-Agent: Mozilla/5.0 (compatible; Osmedeus/v4; +https://github.com/j3ssie/osmedeus)"
|
||||
|
||||
steps:
|
||||
# ============================================================
|
||||
# Phase 1: Validate Dependencies
|
||||
# ============================================================
|
||||
- name: validate-dependencies
|
||||
type: function
|
||||
function: |
|
||||
fileExists("{{Binaries}}/jaeles") &&
|
||||
fileExists("{{Binaries}}/nuclei")
|
||||
exports:
|
||||
deps_valid: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Required binaries (jaeles, nuclei) not found"
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Setup Output Directories
|
||||
# ============================================================
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{output_dir}}
|
||||
- mkdir -p {{output_dir}}/raw
|
||||
- mkdir -p {{output_dir}}/active
|
||||
- mkdir -p {{output_dir}}/sensitive
|
||||
- mkdir -p {{output_dir}}/nuclei
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Validate Input File
|
||||
# ============================================================
|
||||
- name: check-input-exists
|
||||
type: function
|
||||
function: fileExists("{{httpFile}}")
|
||||
exports:
|
||||
input_exists: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Input file {{httpFile}} not found"
|
||||
- action: abort
|
||||
|
||||
- name: count-input-lines
|
||||
type: function
|
||||
function: fileLength("{{httpFile}}")
|
||||
exports:
|
||||
input_count: "output"
|
||||
|
||||
# Decision: Abort if input file exceeds limit
|
||||
- name: check-input-limit
|
||||
type: function
|
||||
function: |
|
||||
var count = parseInt("{{input_count}}");
|
||||
var limit = parseInt("{{limit}}");
|
||||
if (count > limit) {
|
||||
return "exceeds_limit";
|
||||
}
|
||||
return "valid";
|
||||
exports:
|
||||
input_valid: "{{Result}}"
|
||||
decision:
|
||||
switch: "{{input_valid}}"
|
||||
cases:
|
||||
"exceeds_limit":
|
||||
goto: abort-large-input
|
||||
default:
|
||||
goto: split-input-file
|
||||
|
||||
- name: abort-large-input
|
||||
type: function
|
||||
function: printf("ERROR: Input file has {{input_count}} lines, exceeds limit of {{limit}}")
|
||||
on_error:
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: Split Input for Parallel Processing
|
||||
# ============================================================
|
||||
- name: split-input-file
|
||||
type: function
|
||||
function: SplitFile("{{httpFile}}", "{{Workspace}}-index", {{splitLines}}, "{{output_dir}}/raw")
|
||||
exports:
|
||||
split_dir: "{{output_dir}}/raw"
|
||||
|
||||
- name: list-split-files
|
||||
type: bash
|
||||
command: "ls {{output_dir}}/raw/{{Workspace}}-index* 2>/dev/null | head -100 > {{output_dir}}/raw/split-files.txt || touch {{output_dir}}/raw/split-files.txt"
|
||||
exports:
|
||||
split_files: "{{output_dir}}/raw/split-files.txt"
|
||||
|
||||
- name: count-split-files
|
||||
type: function
|
||||
function: fileLength("{{output_dir}}/raw/split-files.txt")
|
||||
exports:
|
||||
split_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Jaeles Vulnerability Scanning
|
||||
# ============================================================
|
||||
- name: jaeles-active-scan
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{split_count}}") > 0'
|
||||
input: "{{output_dir}}/raw/split-files.txt"
|
||||
variable: splitfile
|
||||
threads: 1
|
||||
step:
|
||||
name: run-jaeles-active
|
||||
type: bash
|
||||
command: |
|
||||
echo "Running Jaeles active scan on [[splitfile]]..."
|
||||
timeout -k 1m {{jaelesTimeout}} {{Binaries}}/jaeles scan -c {{jaelesThreads}} -s '{{sign}}' -s '{{sign2}}' -U [[splitfile]] -o {{output_dir}}/active/ {{extra}} 2>/dev/null || true
|
||||
timeout: 14400
|
||||
|
||||
- name: jaeles-sensitive-scan
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{split_count}}") > 0'
|
||||
input: "{{output_dir}}/raw/split-files.txt"
|
||||
variable: splitfile
|
||||
threads: 1
|
||||
step:
|
||||
name: run-jaeles-sensitive
|
||||
type: bash
|
||||
command: |
|
||||
echo "Running Jaeles sensitive scan on [[splitfile]]..."
|
||||
timeout -k 1m {{jaelesTimeout}} {{Binaries}}/jaeles scan --fi -c {{jaelesThreads}} -s '{{sign3}}' -L 2 -U [[splitfile]] -o {{output_dir}}/sensitive/ {{extra}} 2>/dev/null || true
|
||||
timeout: 14400
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Generate Jaeles Reports
|
||||
# ============================================================
|
||||
- name: generate-jaeles-reports
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: generate-active-report
|
||||
type: bash
|
||||
command: "{{Binaries}}/jaeles report -o {{output_dir}}/active/ -R {{output_dir}}/active/{{Workspace}}-report.html 2>/dev/null || true"
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
- name: generate-sensitive-report
|
||||
type: bash
|
||||
command: "{{Binaries}}/jaeles report -o {{output_dir}}/sensitive/ -R {{output_dir}}/sensitive/{{Workspace}}-sensitive.html 2>/dev/null || true"
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: Process Jaeles Results
|
||||
# ============================================================
|
||||
- name: copy-active-summary
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{output_dir}}/active/jaeles-summary.txt")'
|
||||
command: "cp {{output_dir}}/active/jaeles-summary.txt {{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt"
|
||||
exports:
|
||||
active_summary: "{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt"
|
||||
|
||||
- name: notify-active-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")'
|
||||
parallel_functions:
|
||||
- TeleMessByFile("#report", "{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")
|
||||
- Cat("{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")
|
||||
- TotalVulnerability("{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to notify active scan results"
|
||||
- action: continue
|
||||
|
||||
- name: copy-sensitive-summary
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{output_dir}}/sensitive/jaeles-summary.txt")'
|
||||
command: "cp {{output_dir}}/sensitive/jaeles-summary.txt {{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt"
|
||||
exports:
|
||||
sensitive_summary: "{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt"
|
||||
|
||||
- name: notify-sensitive-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")'
|
||||
parallel_functions:
|
||||
- TeleMessByFile("#sensitive", "{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")
|
||||
- Cat("{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")
|
||||
- TotalVulnerability("{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to notify sensitive scan results"
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Nuclei Vulnerability Scanning
|
||||
# ============================================================
|
||||
- name: nuclei-scan
|
||||
type: bash
|
||||
pre_condition: '"{{enableNuclei}}" == "true" && fileExists("{{httpFile}}")'
|
||||
command: |
|
||||
timeout -k 1m {{nucleiTimeout}} {{Binaries}}/nuclei \
|
||||
-H '{{defaultUA}}' \
|
||||
-silent \
|
||||
-c {{nucleiThreads}} \
|
||||
-jsonl \
|
||||
-severity '{{nucleiSeverity}}' \
|
||||
-t ~/nuclei-templates/ \
|
||||
-l {{httpFile}} \
|
||||
-irr \
|
||||
-o {{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt
|
||||
timeout: 28800
|
||||
exports:
|
||||
nuclei_json: "{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Nuclei scan failed or timed out"
|
||||
- action: continue
|
||||
|
||||
- name: count-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt")'
|
||||
function: fileLength("{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt")
|
||||
exports:
|
||||
nuclei_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: Process Nuclei Results
|
||||
# ============================================================
|
||||
- name: generate-nuclei-report
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{nuclei_count}}") > 0'
|
||||
function: GenNucleiReport("{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt", "{{output_dir}}/nuclei/{{Workspace}}-nuclei.html")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to generate Nuclei HTML report"
|
||||
- action: continue
|
||||
|
||||
- name: parse-nuclei-json
|
||||
type: bash
|
||||
pre_condition: 'parseInt("{{nuclei_count}}") > 0'
|
||||
command: |
|
||||
cat {{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt | \
|
||||
jq -r '[.info.severity,.\"template-id\",.\"matched-at\",.\"matched-name\"] | join(\" - \")' \
|
||||
> {{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt 2>/dev/null || true
|
||||
exports:
|
||||
nuclei_parsed: "{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt"
|
||||
|
||||
- name: sort-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")'
|
||||
function: SortU("{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")
|
||||
|
||||
- name: notify-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{nuclei_count}}") > 0'
|
||||
parallel_functions:
|
||||
- TeleMessByFile("#sensitive", "{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")
|
||||
- Cat("{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to notify Nuclei results"
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 10: Generate Final Report
|
||||
# ============================================================
|
||||
- name: generate-final-report
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{Data}}/markdown/general-template.md")'
|
||||
function: GenMarkdownReport("{{Data}}/markdown/general-template.md", "{{Output}}/summary.html")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Final report generation skipped - template not found"
|
||||
- action: continue
|
||||
|
||||
- name: generate-vuln-summary
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "=== Vulnerability Scan Report ===" > {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Workspace: {{Workspace}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Date: $(date)" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== Statistics ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Input Hosts: {{input_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Nuclei Findings: {{nuclei_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== Reports Generated ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "- Active Scan: {{output_dir}}/active/{{Workspace}}-report.html" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "- Sensitive Scan: {{output_dir}}/sensitive/{{Workspace}}-sensitive.html" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "- Nuclei Scan: {{output_dir}}/nuclei/{{Workspace}}-nuclei.html" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
|
||||
- name: notify-completion
|
||||
type: function
|
||||
function: printf("Vulnerability scan complete: {{input_count}} hosts scanned, {{nuclei_count}} nuclei findings")
|
||||
@@ -0,0 +1,263 @@
|
||||
name: web-reconnaissance
|
||||
kind: module
|
||||
desc: Comprehensive web reconnaissance module demonstrating advanced workflow features
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: output_dir
|
||||
default: /tmp/osm-web-recon
|
||||
- name: threads
|
||||
default: "5"
|
||||
- name: subfinderThreads
|
||||
default: "{{threads * 4}}"
|
||||
- name: httpxThreads
|
||||
default: "{{threads * 2}}"
|
||||
- name: nucleiThreads
|
||||
default: "{{threads}}"
|
||||
- name: screenshotThreads
|
||||
default: "5"
|
||||
- name: httpxTimeout
|
||||
default: "10"
|
||||
- name: nucleiTimeout
|
||||
default: "3600"
|
||||
- name: enableScreenshots
|
||||
default: "true"
|
||||
- name: enableNuclei
|
||||
default: "true"
|
||||
- name: nucleiSeverity
|
||||
default: "critical,high,medium"
|
||||
- name: subfinderConfig
|
||||
default: "{{Data}}/external-configs/subfinder-provider.yaml"
|
||||
|
||||
steps:
|
||||
# ============================================================
|
||||
# Phase 1: Validate Dependencies
|
||||
# ============================================================
|
||||
- name: validate-dependencies
|
||||
type: function
|
||||
function: |
|
||||
fileExists("{{Binaries}}/subfinder") &&
|
||||
fileExists("{{Binaries}}/assetfinder") &&
|
||||
fileExists("{{Binaries}}/httpx")
|
||||
exports:
|
||||
deps_valid: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Required binaries not found"
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Parallel Subdomain Enumeration
|
||||
# ============================================================
|
||||
- name: subdomain-enumeration
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: run-subfinder
|
||||
type: bash
|
||||
command: "{{Binaries}}/subfinder -d {{Target}} -provider-config {{subfinderConfig}} -t {{subfinderThreads}} -o {{Output}}/web-recon/subdomains/{{Workspace}}-subfinder.txt -silent"
|
||||
timeout: 600
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Subfinder failed, continuing with other tools"
|
||||
- action: continue
|
||||
|
||||
- name: run-assetfinder
|
||||
type: bash
|
||||
command: "{{Binaries}}/assetfinder -subs-only {{Target}} > {{Output}}/web-recon/subdomains/{{Workspace}}-assetfinder.txt"
|
||||
timeout: 300
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
- name: run-findomain
|
||||
type: bash
|
||||
command: "{{Binaries}}/findomain -u {{Output}}/web-recon/subdomains/{{Workspace}}-findomain.txt -t {{Target}} 2>/dev/null"
|
||||
timeout: 300
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Merge and Deduplicate Subdomains
|
||||
# ============================================================
|
||||
- name: merge-subdomains
|
||||
type: bash
|
||||
commands:
|
||||
- "cat {{Output}}/web-recon/subdomains/{{Workspace}}-*.txt 2>/dev/null | sort -u > {{Output}}/web-recon/subdomains/all-{{Workspace}}.txt"
|
||||
- "cat {{Output}}/web-recon/subdomains/all-{{Workspace}}.txt | {{Binaries}}/cleansub -t '{{Target}}' > {{Output}}/web-recon/subdomains/final-{{Workspace}}.txt 2>/dev/null || cp {{Output}}/web-recon/subdomains/all-{{Workspace}}.txt {{Output}}/web-recon/subdomains/final-{{Workspace}}.txt"
|
||||
exports:
|
||||
subdomains_file: "{{Output}}/web-recon/subdomains/final-{{Workspace}}.txt"
|
||||
|
||||
- name: count-subdomains
|
||||
type: function
|
||||
function: |
|
||||
var count = fileLength("{{subdomains_file}}");
|
||||
return count > 0 ? "true" : "false";
|
||||
exports:
|
||||
subdomain_count: "{{Result}}"
|
||||
has_subdomains: "{{Result}}"
|
||||
|
||||
# Decision: Skip remaining steps if no subdomains found
|
||||
- name: check-subdomain-results
|
||||
type: bash
|
||||
command: "echo {{subdomain_count}}"
|
||||
decision:
|
||||
switch: "{{has_subdomains}}"
|
||||
cases:
|
||||
"false":
|
||||
goto: generate-empty-report
|
||||
default:
|
||||
goto: http-probing
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: HTTP Probing
|
||||
# ============================================================
|
||||
- name: http-probing
|
||||
type: bash
|
||||
command: "{{Binaries}}/httpx -l {{subdomains_file}} -threads {{httpxThreads}} -timeout {{httpxTimeout}} -silent -o {{Output}}/web-recon/probing/live-{{Workspace}}.txt -json -output {{Output}}/web-recon/probing/httpx-{{Workspace}}.json"
|
||||
timeout: 900
|
||||
exports:
|
||||
live_hosts_file: "{{Output}}/web-recon/probing/live-{{Workspace}}.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "HTTP probing failed"
|
||||
- action: run
|
||||
step: fallback-probing
|
||||
|
||||
- name: fallback-probing
|
||||
type: bash
|
||||
pre_condition: "!fileExists('{{Output}}/web-recon/probing/live-{{Workspace}}.txt')"
|
||||
command: "cat {{subdomains_file}} | xargs -I {} curl -s -o /dev/null -w '%{http_code} {}\\n' http://{} 2>/dev/null | grep '^200' | awk '{print $2}' > {{Output}}/web-recon/probing/live-{{Workspace}}.txt"
|
||||
exports:
|
||||
live_hosts_file: "{{Output}}/web-recon/probing/live-{{Workspace}}.txt"
|
||||
|
||||
- name: count-live-hosts
|
||||
type: function
|
||||
function: fileLength("{{live_hosts_file}}")
|
||||
exports:
|
||||
live_host_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Parallel Analysis (Screenshots + Nuclei)
|
||||
# ============================================================
|
||||
- name: parallel-analysis
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
# Screenshot capture using Docker
|
||||
- name: capture-screenshots
|
||||
type: remote-bash
|
||||
pre_condition: '"{{enableScreenshots}}" == "true" && parseInt("{{live_host_count}}") > 0'
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: projectdiscovery/katana:latest
|
||||
volumes:
|
||||
- "{{Output}}/web-recon:/output"
|
||||
workdir: /output
|
||||
env:
|
||||
TARGETS_FILE: "/output/probing/live-{{Workspace}}.txt"
|
||||
command: |
|
||||
echo "Capturing screenshots for live hosts..."
|
||||
cat $TARGETS_FILE | head -20
|
||||
timeout: 1800
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Screenshot capture failed"
|
||||
- action: continue
|
||||
|
||||
# Nuclei vulnerability scanning using Docker
|
||||
- name: nuclei-scan
|
||||
type: remote-bash
|
||||
pre_condition: '"{{enableNuclei}}" == "true" && parseInt("{{live_host_count}}") > 0'
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: projectdiscovery/nuclei:latest
|
||||
volumes:
|
||||
- "{{Output}}/web-recon:/output"
|
||||
workdir: /output
|
||||
env:
|
||||
SEVERITY: "{{nucleiSeverity}}"
|
||||
THREADS: "{{nucleiThreads}}"
|
||||
command: |
|
||||
nuclei -l /output/probing/live-{{Workspace}}.txt \
|
||||
-severity $SEVERITY \
|
||||
-c $THREADS \
|
||||
-json-export /output/nuclei/results-{{Workspace}}.json \
|
||||
-silent
|
||||
timeout: 3600
|
||||
exports:
|
||||
nuclei_results: "{{Output}}/web-recon/nuclei/results-{{Workspace}}.json"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Nuclei scan failed"
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Foreach - Detailed Host Analysis
|
||||
# ============================================================
|
||||
- name: detailed-host-analysis
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{live_host_count}}") > 0 && parseInt("{{live_host_count}}") < 50'
|
||||
input: "{{live_hosts_file}}"
|
||||
variable: host
|
||||
threads: 5
|
||||
step:
|
||||
name: analyze-single-host
|
||||
type: bash
|
||||
command: |
|
||||
echo "Analyzing [[host]]..."
|
||||
curl -s -I "[[host]]" 2>/dev/null | head -20 >> {{Output}}/web-recon/probing/headers-{{Workspace}}.txt
|
||||
echo "---" >> {{Output}}/web-recon/probing/headers-{{Workspace}}.txt
|
||||
timeout: 30
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: Result Processing and Reporting
|
||||
# ============================================================
|
||||
- name: process-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")'
|
||||
parallel_functions:
|
||||
- db_vuln_critical("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")
|
||||
- db_vuln_high("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")
|
||||
- db_vuln_medium("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")
|
||||
exports:
|
||||
vuln_stats: "output"
|
||||
|
||||
- name: generate-report
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "=== Web Reconnaissance Report ===" > {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Workspace: {{Workspace}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Date: $(date)" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "=== Statistics ===" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Total Subdomains: {{subdomain_count}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Live Hosts: {{live_host_count}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "=== Live Hosts ===" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
cat {{live_hosts_file}} >> {{Output}}/web-recon/final-report-{{Workspace}}.txt 2>/dev/null || echo "No live hosts found"
|
||||
- "cp {{live_hosts_file}} {{Output}}/web-recon/live-hosts-{{Workspace}}.txt 2>/dev/null || touch {{Output}}/web-recon/live-hosts-{{Workspace}}.txt"
|
||||
- "cp {{Output}}/web-recon/nuclei/results-{{Workspace}}.json {{Output}}/web-recon/vulnerabilities-{{Workspace}}.json 2>/dev/null || echo '[]' > {{Output}}/web-recon/vulnerabilities-{{Workspace}}.json"
|
||||
|
||||
- name: generate-empty-report
|
||||
type: bash
|
||||
pre_condition: '"{{has_subdomains}}" == "false"'
|
||||
commands:
|
||||
- |
|
||||
echo "=== Web Reconnaissance Report ===" > {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "No subdomains found for target." >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
- "touch {{Output}}/web-recon/live-hosts-{{Workspace}}.txt"
|
||||
- "echo '[]' > {{Output}}/web-recon/vulnerabilities-{{Workspace}}.json"
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Cleanup and Notifications
|
||||
# ============================================================
|
||||
- name: final-cleanup
|
||||
type: function
|
||||
function: SortU("{{Output}}/web-recon/live-hosts-{{Workspace}}.txt")
|
||||
|
||||
- name: notify-completion
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{subdomain_count}}") > 0'
|
||||
function: printf("Scan complete: {{subdomain_count}} subdomains, {{live_host_count}} live hosts")
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
kind: module
|
||||
name: demo-bash
|
||||
description: Demo bash steps with functions and exports
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: threads
|
||||
default: "5"
|
||||
steps:
|
||||
- name: setup
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/demo && echo "{{Target}}" > {{Output}}/demo/target.txt
|
||||
exports:
|
||||
target_file: "{{Output}}/demo/target.txt"
|
||||
- name: run-parallel
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- 'echo "Thread 1: {{Target}}" >> {{Output}}/demo/results.txt'
|
||||
- 'echo "Thread 2: {{Target}}" >> {{Output}}/demo/results.txt'
|
||||
- name: check-result
|
||||
type: function
|
||||
function: 'fileLength("{{Output}}/demo/results.txt")'
|
||||
exports:
|
||||
line_count: "output"
|
||||
- name: summary
|
||||
type: bash
|
||||
command: 'echo "Processed {{Target}} with {{line_count}} lines"'
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
kind: module
|
||||
name: demo-docker
|
||||
description: Demo Docker runner with remote-bash
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
steps:
|
||||
- name: docker-single
|
||||
type: remote-bash
|
||||
log: "Running in Alpine container"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: 'echo "Target: {{Target}}" > /output/docker-out.txt'
|
||||
- name: docker-parallel
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
parallel_commands:
|
||||
- 'echo "Scan A: {{Target}}"'
|
||||
- 'echo "Scan B: {{Target}}"'
|
||||
exports:
|
||||
docker_done: "true"
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
kind: flow
|
||||
name: demo-flow
|
||||
description: Demo flow orchestrating modules with decision routing
|
||||
params:
|
||||
- name: threads
|
||||
default: "5"
|
||||
- name: mode
|
||||
default: "full"
|
||||
modules:
|
||||
- name: bash-module
|
||||
path: demo-bash.yaml
|
||||
params:
|
||||
threads: "{{threads}}"
|
||||
- name: docker-module
|
||||
path: demo-docker.yaml
|
||||
depends_on: [bash-module]
|
||||
condition: "{{mode}} == 'full'"
|
||||
on_success:
|
||||
- action: export
|
||||
name: scan_status
|
||||
value: "complete"
|
||||
decision:
|
||||
switch: "{{scan_status}}"
|
||||
cases:
|
||||
"complete": { goto: ssh-module }
|
||||
default: { goto: _end }
|
||||
- name: ssh-module
|
||||
path: demo-ssh.yaml
|
||||
depends_on: [docker-module]
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
kind: module
|
||||
name: demo-ssh
|
||||
description: Demo SSH runner with remote-bash
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: ssh_host
|
||||
default: "localhost"
|
||||
- name: ssh_user
|
||||
default: "testuser"
|
||||
- name: ssh_password
|
||||
default: "testpass"
|
||||
steps:
|
||||
- name: ssh-connect
|
||||
type: remote-bash
|
||||
log: "Executing via SSH"
|
||||
step_runner: ssh
|
||||
step_runner_config:
|
||||
host: "{{ssh_host}}"
|
||||
port: 2222
|
||||
user: "{{ssh_user}}"
|
||||
password: "{{ssh_password}}"
|
||||
commands:
|
||||
- 'echo "Target: {{Target}}"'
|
||||
- 'hostname && whoami'
|
||||
exports:
|
||||
ssh_done: "true"
|
||||
@@ -0,0 +1,345 @@
|
||||
# =============================================================================
|
||||
# Flow Workflow: Comprehensive Example
|
||||
# =============================================================================
|
||||
# This file demonstrates ALL fields available in a flow-kind workflow.
|
||||
# Flows orchestrate multiple modules with dependencies, conditions, and routing.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WORKFLOW-LEVEL FIELDS
|
||||
# Same as module workflows (kind, name, description, tags, params, etc.)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# kind: Workflow type - "flow" orchestrates multiple modules
|
||||
kind: flow
|
||||
|
||||
# name: Unique identifier for this workflow (required)
|
||||
name: comprehensive-flow-example
|
||||
|
||||
# description: Human-readable description
|
||||
description: Demonstrates all flow-specific fields including modules, dependencies, conditions, and decisions
|
||||
|
||||
# tags: Comma-separated tags for filtering
|
||||
tags: flow, comprehensive, example
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PARAMS SECTION
|
||||
# Parameters available to all modules in this flow
|
||||
# -----------------------------------------------------------------------------
|
||||
params:
|
||||
- name: threads
|
||||
default: "10"
|
||||
|
||||
- name: timeout
|
||||
default: "3600"
|
||||
|
||||
- name: scan_depth
|
||||
default: "normal"
|
||||
|
||||
- name: output_format
|
||||
default: "json"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# DEPENDENCIES SECTION
|
||||
# Flow-level dependencies checked before any module executes
|
||||
# -----------------------------------------------------------------------------
|
||||
dependencies:
|
||||
commands:
|
||||
- nmap
|
||||
- nuclei
|
||||
- httpx
|
||||
|
||||
files:
|
||||
- /tmp
|
||||
|
||||
target_types:
|
||||
- domain
|
||||
- url
|
||||
|
||||
variables:
|
||||
- name: Target
|
||||
type: domain
|
||||
required: true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# REPORTS SECTION
|
||||
# Reports aggregated from all modules in this flow
|
||||
# -----------------------------------------------------------------------------
|
||||
reports:
|
||||
- name: flow-summary
|
||||
path: "{{Output}}/flow-summary.json"
|
||||
type: json
|
||||
description: Aggregated results from all modules
|
||||
|
||||
- name: vulnerabilities
|
||||
path: "{{Output}}/vulnerabilities.txt"
|
||||
type: text
|
||||
description: All discovered vulnerabilities
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PREFERENCES SECTION
|
||||
# Flow-level preferences apply to all module executions
|
||||
# -----------------------------------------------------------------------------
|
||||
preferences:
|
||||
disable_notifications: false
|
||||
heuristics_check: 'basic'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# MODULES SECTION (Flow-specific)
|
||||
# Ordered list of module references to execute
|
||||
# =============================================================================
|
||||
modules:
|
||||
# ===========================================================================
|
||||
# Module Reference: Basic Configuration
|
||||
# ===========================================================================
|
||||
- # name: Display name for this module execution (required)
|
||||
name: reconnaissance
|
||||
|
||||
# path: Path to the module YAML file (required)
|
||||
# Can be relative to workflows directory or absolute
|
||||
path: modules/recon.yaml
|
||||
|
||||
# params: Parameters to pass to this module
|
||||
# Overrides module defaults and flow-level params
|
||||
params:
|
||||
threads: "20" # Override flow-level threads
|
||||
output_dir: "{{Output}}/recon"
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: With Dependencies (depends_on)
|
||||
# ===========================================================================
|
||||
- name: port-scanning
|
||||
path: modules/portscan.yaml
|
||||
|
||||
# depends_on: List of module names that must complete before this module runs
|
||||
# Creates a DAG (Directed Acyclic Graph) for execution order
|
||||
depends_on:
|
||||
- reconnaissance
|
||||
|
||||
params:
|
||||
target_list: "{{Output}}/recon/subdomains.txt"
|
||||
threads: "{{threads}}"
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: With Condition
|
||||
# ===========================================================================
|
||||
- name: web-scanning
|
||||
path: modules/webscan.yaml
|
||||
|
||||
depends_on:
|
||||
- port-scanning
|
||||
|
||||
# condition: JavaScript expression - module only runs if evaluates to true
|
||||
# Can reference exported variables from previous modules
|
||||
condition: 'fileLength("{{Output}}/portscan/http-services.txt") > 0'
|
||||
|
||||
params:
|
||||
input: "{{Output}}/portscan/http-services.txt"
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: With on_success Handler
|
||||
# ===========================================================================
|
||||
- name: vulnerability-scanning
|
||||
path: modules/vuln-scan.yaml
|
||||
|
||||
depends_on:
|
||||
- web-scanning
|
||||
|
||||
condition: 'fileExists("{{Output}}/webscan/endpoints.txt")'
|
||||
|
||||
params:
|
||||
endpoints: "{{Output}}/webscan/endpoints.txt"
|
||||
timeout: "{{timeout}}"
|
||||
|
||||
# on_success: Actions to execute when this module completes successfully
|
||||
on_success:
|
||||
# action: log - Log a message
|
||||
- action: log
|
||||
message: "Vulnerability scanning completed for {{Target}}"
|
||||
|
||||
# action: export - Export a variable for subsequent modules
|
||||
- action: export
|
||||
name: vuln_scan_complete
|
||||
value: "true"
|
||||
|
||||
# action: notify - Send a notification
|
||||
- action: notify
|
||||
notify: "Vulnerability scan finished for {{Target}}"
|
||||
|
||||
# action: run - Execute a follow-up step
|
||||
- action: run
|
||||
type: bash
|
||||
command: 'echo "Vuln scan done" >> {{Output}}/flow-log.txt'
|
||||
|
||||
# action: run with functions
|
||||
- action: run
|
||||
type: function
|
||||
functions:
|
||||
- 'log_info("Module completed successfully")'
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: With on_error Handler
|
||||
# ===========================================================================
|
||||
- name: exploit-verification
|
||||
path: modules/exploit-verify.yaml
|
||||
|
||||
depends_on:
|
||||
- vulnerability-scanning
|
||||
|
||||
condition: '{{vuln_scan_complete}} == "true"'
|
||||
|
||||
params:
|
||||
vulns_file: "{{Output}}/vuln-scan/vulnerabilities.json"
|
||||
|
||||
# on_error: Actions to execute when this module fails
|
||||
on_error:
|
||||
# action: log - Log error message
|
||||
- action: log
|
||||
message: "Exploit verification failed for {{Target}}"
|
||||
# condition: Only execute if this condition is true
|
||||
condition: 'true'
|
||||
|
||||
# action: continue - Allow flow to continue despite error
|
||||
- action: continue
|
||||
message: "Continuing flow despite exploit verification failure"
|
||||
|
||||
# action: abort - Stop the entire flow
|
||||
# (Usually with a condition so it doesn't always abort)
|
||||
- action: abort
|
||||
message: "Critical failure - aborting flow"
|
||||
condition: 'false' # Only abort under specific conditions
|
||||
|
||||
# action: notify - Alert on failure
|
||||
- action: notify
|
||||
notify: "Module failed: exploit-verification for {{Target}}"
|
||||
|
||||
# action: export - Export error state
|
||||
- action: export
|
||||
name: exploit_verify_failed
|
||||
value: "true"
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: With Decision Routing
|
||||
# ===========================================================================
|
||||
- name: deep-scan
|
||||
path: modules/deep-scan.yaml
|
||||
|
||||
depends_on:
|
||||
- vulnerability-scanning
|
||||
|
||||
params:
|
||||
scan_depth: "{{scan_depth}}"
|
||||
|
||||
# on_success exports severity_level for decision routing
|
||||
on_success:
|
||||
- action: export
|
||||
name: severity_level
|
||||
# This would be set by the module based on vuln-scan results
|
||||
value: "{{vuln_severity}}"
|
||||
|
||||
# decision: Conditional routing using switch/case syntax
|
||||
# Determines which module to execute next based on severity
|
||||
decision:
|
||||
# switch: Variable to match against cases
|
||||
switch: "{{severity_level}}"
|
||||
# cases: Map severity levels to notification modules
|
||||
cases:
|
||||
"critical":
|
||||
goto: notification-critical
|
||||
"high":
|
||||
goto: notification-high
|
||||
# default: Fallback to cleanup if no critical/high findings
|
||||
default:
|
||||
goto: cleanup
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: Notification branches (targets of decision routing)
|
||||
# ===========================================================================
|
||||
- name: notification-critical
|
||||
path: modules/notify.yaml
|
||||
|
||||
# Note: This module can be jumped to via decision routing
|
||||
# It won't run in normal sequential flow unless explicitly in depends_on
|
||||
|
||||
params:
|
||||
severity: critical
|
||||
message: "Critical vulnerabilities found for {{Target}}"
|
||||
channel: security-alerts
|
||||
|
||||
on_success:
|
||||
- action: export
|
||||
name: notification_sent
|
||||
value: "critical"
|
||||
|
||||
- name: notification-high
|
||||
path: modules/notify.yaml
|
||||
|
||||
params:
|
||||
severity: high
|
||||
message: "High severity vulnerabilities found for {{Target}}"
|
||||
channel: security-team
|
||||
|
||||
on_success:
|
||||
- action: export
|
||||
name: notification_sent
|
||||
value: "high"
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: Parallel Module Execution
|
||||
# Modules with same depends_on and no inter-dependencies run in parallel
|
||||
# ===========================================================================
|
||||
- name: ssl-analysis
|
||||
path: modules/ssl-check.yaml
|
||||
|
||||
depends_on:
|
||||
- port-scanning # Same dependency as web-scanning
|
||||
|
||||
params:
|
||||
input: "{{Output}}/portscan/ssl-services.txt"
|
||||
|
||||
- name: dns-analysis
|
||||
path: modules/dns-check.yaml
|
||||
|
||||
depends_on:
|
||||
- reconnaissance # Can run in parallel with port-scanning
|
||||
|
||||
params:
|
||||
domains: "{{Output}}/recon/subdomains.txt"
|
||||
|
||||
# ===========================================================================
|
||||
# Module Reference: Cleanup/Final Module
|
||||
# ===========================================================================
|
||||
- name: cleanup
|
||||
path: modules/cleanup.yaml
|
||||
|
||||
# depends_on multiple modules - waits for all to complete
|
||||
depends_on:
|
||||
- vulnerability-scanning
|
||||
- exploit-verification
|
||||
- ssl-analysis
|
||||
- dns-analysis
|
||||
|
||||
# condition with multiple checks
|
||||
condition: 'true' # Always run cleanup
|
||||
|
||||
params:
|
||||
output_dir: "{{Output}}"
|
||||
format: "{{output_format}}"
|
||||
|
||||
on_success:
|
||||
- action: log
|
||||
message: "Flow completed successfully for {{Target}}"
|
||||
|
||||
- action: notify
|
||||
notify: "Security scan flow completed for {{Target}}"
|
||||
|
||||
- action: export
|
||||
name: flow_status
|
||||
value: "completed"
|
||||
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Cleanup failed but flow results are preserved"
|
||||
|
||||
- action: continue
|
||||
message: "Flow complete despite cleanup issues"
|
||||
@@ -0,0 +1,257 @@
|
||||
# =============================================================================
|
||||
# Flow Workflow: All Trigger Types Example
|
||||
# =============================================================================
|
||||
# This file demonstrates ALL trigger types available in osmedeus workflows.
|
||||
# Triggers define when/how a workflow should automatically execute.
|
||||
# Trigger types: cron, event, watch, manual
|
||||
# =============================================================================
|
||||
|
||||
kind: flow
|
||||
name: triggers-example
|
||||
description: Demonstrates all trigger types with comprehensive field documentation
|
||||
tags: triggers, automation, scheduled
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TRIGGERS SECTION
|
||||
# Define automatic execution triggers for this workflow
|
||||
# Multiple triggers can be defined; any triggered condition will start execution
|
||||
# =============================================================================
|
||||
trigger:
|
||||
# ===========================================================================
|
||||
# TRIGGER TYPE: cron
|
||||
# Schedule-based execution using cron expressions
|
||||
# ===========================================================================
|
||||
- # name: Identifier for this trigger (for logging and management)
|
||||
name: daily-scan
|
||||
|
||||
# on: Trigger type - cron, event, watch, or manual
|
||||
on: cron
|
||||
|
||||
# schedule: Cron expression defining when to run
|
||||
# Format: minute hour day-of-month month day-of-week
|
||||
# Examples:
|
||||
# "0 0 * * *" - Every day at midnight
|
||||
# "0 */6 * * *" - Every 6 hours
|
||||
# "0 9 * * 1-5" - 9 AM on weekdays
|
||||
# "0 0 1 * *" - First day of every month at midnight
|
||||
schedule: "0 2 * * *" # Every day at 2 AM
|
||||
|
||||
# input: Defines where the target input comes from for scheduled runs
|
||||
input:
|
||||
# type: Input source type - file, event_data, function, or param
|
||||
type: file
|
||||
|
||||
# path: For "file" type - path to file containing targets (one per line)
|
||||
path: "/data/targets/active-targets.txt"
|
||||
|
||||
# enabled: Whether this trigger is active
|
||||
# true = trigger is active and will fire
|
||||
# false = trigger is defined but disabled
|
||||
enabled: true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cron trigger with function-based input
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: weekly-full-scan
|
||||
on: cron
|
||||
schedule: "0 0 * * 0" # Every Sunday at midnight
|
||||
|
||||
input:
|
||||
# type: function - Generate input dynamically using a function
|
||||
type: function
|
||||
|
||||
# function: JavaScript function to generate/retrieve targets
|
||||
# Can use built-in functions like db queries, API calls, etc.
|
||||
function: 'get_targets_from_db("scope:production")'
|
||||
|
||||
enabled: true
|
||||
|
||||
# ===========================================================================
|
||||
# TRIGGER TYPE: event
|
||||
# Event-driven execution based on system events
|
||||
# Events follow topic format: <component>.<event_type>
|
||||
# ===========================================================================
|
||||
- name: webhook-trigger
|
||||
on: event
|
||||
|
||||
# event: Event configuration for event triggers
|
||||
event:
|
||||
# topic: Event topic to subscribe to
|
||||
# Common topics:
|
||||
# webhook.received - External webhook received
|
||||
# assets.new - New asset discovered
|
||||
# assets.changed - Asset data changed
|
||||
# db.change - Database record changed
|
||||
# watch.files - File system change detected
|
||||
topic: webhook.received
|
||||
|
||||
# filters: JavaScript expressions to filter events
|
||||
# Event data available as 'event' object with fields:
|
||||
# event.name - Event name
|
||||
# event.source - Event source
|
||||
# event.data - JSON payload (string)
|
||||
# event.data_type - Type of data
|
||||
# All filters must evaluate to true for trigger to fire
|
||||
filters:
|
||||
- 'event.source == "github"'
|
||||
- 'event.name == "push"'
|
||||
|
||||
# input: How to extract target from event data
|
||||
input:
|
||||
# type: event_data - Extract from event payload
|
||||
type: event_data
|
||||
|
||||
# field: JSON path to extract from event.data
|
||||
# Uses dot notation for nested fields
|
||||
field: "repository.html_url"
|
||||
|
||||
enabled: true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Event trigger for new asset discovery
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: new-asset-scan
|
||||
on: event
|
||||
|
||||
event:
|
||||
topic: assets.new
|
||||
|
||||
filters:
|
||||
# Filter for specific asset types
|
||||
- 'event.data_type == "subdomain"'
|
||||
# Filter by source tool
|
||||
- 'event.source == "subfinder" || event.source == "amass"'
|
||||
|
||||
input:
|
||||
type: event_data
|
||||
field: "hostname"
|
||||
|
||||
enabled: true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Event trigger with function-based input extraction
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: vuln-alert-trigger
|
||||
on: event
|
||||
|
||||
event:
|
||||
topic: webhook.received
|
||||
|
||||
filters:
|
||||
- 'event.name == "vulnerability_alert"'
|
||||
- 'JSON.parse(event.data).severity == "critical"'
|
||||
|
||||
input:
|
||||
# type: function - Use function to parse/transform event data
|
||||
type: function
|
||||
|
||||
# function: Transform event data to target format
|
||||
function: 'jq("{{event.data}}", ".affected_host")'
|
||||
|
||||
enabled: true
|
||||
|
||||
# ===========================================================================
|
||||
# TRIGGER TYPE: watch
|
||||
# File system watch - triggers when files change
|
||||
# ===========================================================================
|
||||
- name: targets-file-watch
|
||||
on: watch
|
||||
|
||||
# path: File or directory path to watch for changes
|
||||
# Supports glob patterns in some implementations
|
||||
path: "/data/targets/new-targets.txt"
|
||||
|
||||
# input: How to get targets when file changes
|
||||
input:
|
||||
type: file
|
||||
path: "/data/targets/new-targets.txt"
|
||||
|
||||
enabled: true
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Watch trigger on directory
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: input-directory-watch
|
||||
on: watch
|
||||
|
||||
path: "/data/incoming/"
|
||||
|
||||
input:
|
||||
# type: function - Process newly added files
|
||||
type: function
|
||||
function: 'get_new_files("/data/incoming/", "*.txt")'
|
||||
|
||||
enabled: true
|
||||
|
||||
# ===========================================================================
|
||||
# TRIGGER TYPE: manual
|
||||
# Explicit manual trigger control
|
||||
# Used to enable/disable CLI execution for this workflow
|
||||
# ===========================================================================
|
||||
- name: manual-execution
|
||||
on: manual
|
||||
|
||||
# For manual triggers, enabled controls whether CLI can run this workflow
|
||||
# enabled: true - Allow: osmedeus run -f triggers-example -t target
|
||||
# enabled: false - Block CLI execution (only scheduled/event triggers work)
|
||||
enabled: true
|
||||
|
||||
# input: Default input for manual execution
|
||||
# This is optional; CLI -t flag overrides this
|
||||
input:
|
||||
# type: param - Use a parameter as input
|
||||
type: param
|
||||
|
||||
# name: Parameter name to use as target
|
||||
name: Target
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Disabled manual trigger example
|
||||
# This workflow can ONLY be triggered via cron/events, not CLI
|
||||
# ---------------------------------------------------------------------------
|
||||
# Uncomment to see the effect:
|
||||
# - name: block-manual
|
||||
# on: manual
|
||||
# enabled: false
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PARAMS SECTION
|
||||
# -----------------------------------------------------------------------------
|
||||
params:
|
||||
- name: scan_type
|
||||
default: "standard"
|
||||
|
||||
- name: threads
|
||||
default: "10"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# MODULES SECTION
|
||||
# The actual workflow steps to execute when any trigger fires
|
||||
# -----------------------------------------------------------------------------
|
||||
modules:
|
||||
- name: initial-recon
|
||||
path: modules/recon.yaml
|
||||
params:
|
||||
threads: "{{threads}}"
|
||||
|
||||
- name: scanning
|
||||
path: modules/scan.yaml
|
||||
depends_on:
|
||||
- initial-recon
|
||||
params:
|
||||
scan_type: "{{scan_type}}"
|
||||
|
||||
- name: reporting
|
||||
path: modules/report.yaml
|
||||
depends_on:
|
||||
- scanning
|
||||
|
||||
on_success:
|
||||
- action: notify
|
||||
notify: "Triggered scan completed for {{Target}}"
|
||||
# condition: Only notify for certain triggers
|
||||
condition: 'true'
|
||||
|
||||
- action: export
|
||||
name: completed_at
|
||||
value: "{{currentDate()}}"
|
||||
@@ -0,0 +1,483 @@
|
||||
# =============================================================================
|
||||
# Module Workflow: All Step Types Example
|
||||
# =============================================================================
|
||||
# This file demonstrates ALL fields available in a module-kind workflow,
|
||||
# showcasing every step type with comprehensive comments.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WORKFLOW-LEVEL FIELDS
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# kind: Workflow type - either "module" (single unit with steps) or "flow" (orchestrates modules)
|
||||
kind: module
|
||||
|
||||
# name: Unique identifier for this workflow (required)
|
||||
name: all-step-types-example
|
||||
|
||||
# description: Human-readable description of what this workflow does
|
||||
description: Demonstrates all step types and their fields with detailed comments
|
||||
|
||||
# tags: Comma-separated tags for filtering and categorization (parsed as []string)
|
||||
tags: example, comprehensive, demo
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PARAMS SECTION
|
||||
# Define workflow parameters that can be passed via CLI or referenced in templates
|
||||
# -----------------------------------------------------------------------------
|
||||
params:
|
||||
# name: Parameter identifier used in templates as {{param_name}}
|
||||
# default: Default value if not provided via CLI
|
||||
# required: If true, workflow fails without this value
|
||||
# generator: Function to generate value, e.g., uuid(), currentDate(), getEnvVar("KEY")
|
||||
- name: message
|
||||
default: "Hello World"
|
||||
required: false
|
||||
|
||||
- name: output_dir
|
||||
default: "{{Output}}/results" # Can reference built-in variables
|
||||
required: false
|
||||
|
||||
- name: threads
|
||||
default: "10"
|
||||
required: false
|
||||
|
||||
- name: run_id
|
||||
generator: uuid() # Generates a unique ID automatically
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# DEPENDENCIES SECTION
|
||||
# Validate requirements before workflow execution
|
||||
# -----------------------------------------------------------------------------
|
||||
dependencies:
|
||||
# commands: List of binaries/commands that must exist in PATH
|
||||
commands:
|
||||
- echo
|
||||
- curl
|
||||
|
||||
# files: List of files/directories that must exist
|
||||
files:
|
||||
- /tmp
|
||||
|
||||
# variables: Define variable requirements with type validation
|
||||
# Types: domain, path, number, file, string
|
||||
variables:
|
||||
- name: Target
|
||||
type: string
|
||||
required: true
|
||||
|
||||
# functions_conditions: JavaScript expressions that must evaluate to true
|
||||
functions_conditions:
|
||||
- '1 + 1 == 2'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# REPORTS SECTION
|
||||
# Define output files produced by this workflow
|
||||
# -----------------------------------------------------------------------------
|
||||
reports:
|
||||
# name: Display name for the report
|
||||
# path: File path (can use templates like {{Output}})
|
||||
# type: Format type - text, csv, json, markdown, etc.
|
||||
# description: Human-readable description
|
||||
- name: main-output
|
||||
path: "{{Output}}/main-results.txt"
|
||||
type: text
|
||||
description: Main output file from the workflow
|
||||
|
||||
- name: json-results
|
||||
path: "{{Output}}/results.json"
|
||||
type: json
|
||||
description: Structured JSON output
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PREFERENCES SECTION (Optional)
|
||||
# Set CLI-like flags directly in the workflow. CLI flags always take precedence.
|
||||
# -----------------------------------------------------------------------------
|
||||
preferences:
|
||||
# disable_notifications: Equivalent to --disable-notification
|
||||
disable_notifications: true
|
||||
|
||||
# disable_logging: Equivalent to --disable-logging
|
||||
disable_logging: false
|
||||
|
||||
# heuristics_check: Equivalent to --heuristics-check (none, basic, advanced)
|
||||
heuristics_check: 'basic'
|
||||
|
||||
# ci_output_format: Equivalent to --ci-output-format
|
||||
ci_output_format: false
|
||||
|
||||
# silent: Equivalent to --silent
|
||||
silent: false
|
||||
|
||||
# repeat: Equivalent to --repeat
|
||||
repeat: false
|
||||
|
||||
# repeat_wait_time: Equivalent to --repeat-wait-time (e.g., 30s, 1h, 2h30m)
|
||||
repeat_wait_time: '60s'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# STEPS SECTION
|
||||
# The ordered list of execution steps for this module
|
||||
# -----------------------------------------------------------------------------
|
||||
steps:
|
||||
# ===========================================================================
|
||||
# STEP TYPE: bash
|
||||
# Execute shell commands on the host (or configured runner)
|
||||
# ===========================================================================
|
||||
- name: bash-single-command
|
||||
# type: Step type - bash, function, parallel-steps, foreach, remote-bash, http, llm
|
||||
type: bash
|
||||
|
||||
# pre_condition: JavaScript expression - step only runs if this evaluates to true
|
||||
pre_condition: 'true'
|
||||
|
||||
# log: Custom log message displayed when step starts (supports templates)
|
||||
log: "Executing single bash command for {{Target}}"
|
||||
|
||||
# timeout: Maximum execution time in seconds (0 = no timeout)
|
||||
timeout: 60
|
||||
|
||||
# command: Single command to execute
|
||||
command: 'echo "Processing target: {{Target}} with message: {{message}}"'
|
||||
|
||||
# std_file: File path to save stdout/stderr output
|
||||
std_file: "{{Output}}/step1-output.txt"
|
||||
|
||||
# exports: Variables to export for subsequent steps
|
||||
# Key = variable name, Value = extraction pattern or literal value
|
||||
exports:
|
||||
step1_result: "completed"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bash step with multiple sequential commands
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: bash-multiple-commands
|
||||
type: bash
|
||||
log: "Running multiple sequential commands"
|
||||
|
||||
# commands: List of commands executed sequentially
|
||||
commands:
|
||||
- 'echo "First command"'
|
||||
- 'echo "Second command"'
|
||||
- 'echo "Third command"'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bash step with parallel commands
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: bash-parallel-commands
|
||||
type: bash
|
||||
log: "Running commands in parallel"
|
||||
|
||||
# parallel_commands: List of commands executed concurrently
|
||||
parallel_commands:
|
||||
- 'echo "Parallel A" && sleep 1'
|
||||
- 'echo "Parallel B" && sleep 1'
|
||||
- 'echo "Parallel C" && sleep 1'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bash step with structured arguments
|
||||
# Arguments are joined in order: command + speed + config + input + output
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: bash-structured-args
|
||||
type: bash
|
||||
log: "Using structured argument fields"
|
||||
|
||||
command: 'echo'
|
||||
|
||||
# speed_args: Performance-related arguments (e.g., thread count, rate limits)
|
||||
speed_args: '-n'
|
||||
|
||||
# config_args: Configuration arguments (e.g., config file paths)
|
||||
config_args: ''
|
||||
|
||||
# input_args: Input-related arguments (e.g., input file, target)
|
||||
input_args: '"Structured arguments test"'
|
||||
|
||||
# output_args: Output-related arguments (e.g., output file, format)
|
||||
output_args: ''
|
||||
|
||||
# ===========================================================================
|
||||
# STEP TYPE: function
|
||||
# Execute built-in utility functions via Otto JavaScript runtime
|
||||
# ===========================================================================
|
||||
- name: function-single
|
||||
type: function
|
||||
log: "Executing single function"
|
||||
|
||||
# function: Single function call (JavaScript expression)
|
||||
function: 'log_info("Processing {{Target}} in function step")'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Function step with multiple sequential functions
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: function-multiple
|
||||
type: function
|
||||
log: "Executing multiple functions sequentially"
|
||||
|
||||
# functions: List of functions executed sequentially
|
||||
functions:
|
||||
- 'log_info("Function 1")'
|
||||
- 'log_info("Function 2")'
|
||||
- 'log_info("Function 3")'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Function step with parallel functions
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: function-parallel
|
||||
type: function
|
||||
log: "Executing functions in parallel"
|
||||
|
||||
# parallel_functions: List of functions executed concurrently
|
||||
parallel_functions:
|
||||
- 'log_info("Parallel Function A")'
|
||||
- 'log_info("Parallel Function B")'
|
||||
- 'log_info("Parallel Function C")'
|
||||
|
||||
# ===========================================================================
|
||||
# STEP TYPE: parallel-steps
|
||||
# Execute multiple complete steps in parallel
|
||||
# ===========================================================================
|
||||
- name: parallel-step-container
|
||||
type: parallel-steps
|
||||
log: "Running multiple steps in parallel"
|
||||
|
||||
# parallel_steps: List of Step objects executed concurrently
|
||||
parallel_steps:
|
||||
- name: parallel-inner-1
|
||||
type: bash
|
||||
command: 'echo "Inner parallel step 1"'
|
||||
|
||||
- name: parallel-inner-2
|
||||
type: function
|
||||
function: 'log_info("Inner parallel step 2")'
|
||||
|
||||
- name: parallel-inner-3
|
||||
type: bash
|
||||
command: 'echo "Inner parallel step 3"'
|
||||
|
||||
# ===========================================================================
|
||||
# STEP TYPE: foreach
|
||||
# Iterate over input lines, executing inner step for each
|
||||
# ===========================================================================
|
||||
- name: foreach-example
|
||||
type: foreach
|
||||
log: "Iterating over items"
|
||||
|
||||
# input: File path or direct content to iterate over (one item per line)
|
||||
input: "{{Output}}/items.txt"
|
||||
|
||||
# variable: Name for the loop variable, accessed as [[variable]] in inner step
|
||||
variable: item
|
||||
|
||||
# threads: Number of concurrent iterations (default: 1 = sequential)
|
||||
threads: 5
|
||||
|
||||
# step: The inner step to execute for each item (single Step object)
|
||||
step:
|
||||
name: process-item
|
||||
type: bash
|
||||
command: 'echo "Processing [[item]]"'
|
||||
exports:
|
||||
processed_item: "[[item]]"
|
||||
|
||||
# ===========================================================================
|
||||
# STEP TYPE: http
|
||||
# Make HTTP requests to external APIs
|
||||
# ===========================================================================
|
||||
- name: http-request
|
||||
type: http
|
||||
log: "Making HTTP request"
|
||||
timeout: 30
|
||||
|
||||
# url: Target URL for the request (required for http type)
|
||||
url: "https://httpbin.org/post"
|
||||
|
||||
# method: HTTP method - GET, POST, PUT, DELETE, PATCH, etc.
|
||||
method: POST
|
||||
|
||||
# headers: Map of HTTP headers to send
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
Authorization: "Bearer {{api_token}}"
|
||||
X-Custom-Header: custom-value
|
||||
|
||||
# request_body: Request body content (typically JSON for POST/PUT)
|
||||
request_body: |
|
||||
{
|
||||
"target": "{{Target}}",
|
||||
"message": "{{message}}"
|
||||
}
|
||||
|
||||
exports:
|
||||
http_response: "{{response.body}}"
|
||||
|
||||
# ===========================================================================
|
||||
# STEP TYPE: llm
|
||||
# Make LLM API calls for AI-powered processing
|
||||
# ===========================================================================
|
||||
- name: llm-chat-completion
|
||||
type: llm
|
||||
log: "Calling LLM for analysis"
|
||||
timeout: 120
|
||||
|
||||
# messages: Conversation messages for chat completion
|
||||
# role: system, user, assistant, or tool
|
||||
# content: Message text (can be string or multimodal array)
|
||||
messages:
|
||||
- role: system
|
||||
content: "You are a security analysis assistant."
|
||||
|
||||
- role: user
|
||||
# content can be a simple string or complex multimodal content
|
||||
content: "Analyze this target: {{Target}}"
|
||||
|
||||
# tools: Function tools available to the LLM
|
||||
tools:
|
||||
- type: function # Currently only "function" type supported
|
||||
function:
|
||||
name: analyze_target
|
||||
description: Analyzes a target for security vulnerabilities
|
||||
# parameters: JSON Schema defining function parameters
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
target:
|
||||
type: string
|
||||
description: The target to analyze
|
||||
depth:
|
||||
type: string
|
||||
enum: [shallow, deep]
|
||||
required:
|
||||
- target
|
||||
|
||||
# tool_choice: How the model should choose tools
|
||||
# Can be: "auto", "none", "required", or {"type": "function", "function": {"name": "fn_name"}}
|
||||
tool_choice: auto
|
||||
|
||||
# llm_config: Step-level LLM configuration overrides
|
||||
llm_config:
|
||||
# provider: Specific provider to use (overrides rotation)
|
||||
provider: openai
|
||||
|
||||
# model: Model override for this step
|
||||
model: gpt-4
|
||||
|
||||
# Generation parameters
|
||||
max_tokens: 1000
|
||||
temperature: 0.7
|
||||
top_p: 1.0
|
||||
|
||||
# Request settings
|
||||
timeout: "60s"
|
||||
max_retries: 3
|
||||
stream: false
|
||||
|
||||
# response_format: Control output format
|
||||
# type: "text", "json_object", or "json_schema"
|
||||
response_format:
|
||||
type: json_object
|
||||
|
||||
# extra_llm_parameters: Additional provider-specific parameters
|
||||
extra_llm_parameters:
|
||||
seed: 42
|
||||
presence_penalty: 0.0
|
||||
|
||||
exports:
|
||||
llm_analysis: "{{response.content}}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# LLM step for embeddings
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: llm-embedding
|
||||
type: llm
|
||||
log: "Generating text embeddings"
|
||||
|
||||
# is_embedding: Flag to indicate this is an embedding request
|
||||
is_embedding: true
|
||||
|
||||
# embedding_input: List of texts to generate embeddings for
|
||||
embedding_input:
|
||||
- "Security vulnerability in {{Target}}"
|
||||
- "Network reconnaissance results"
|
||||
- "Port scan findings"
|
||||
|
||||
llm_config:
|
||||
model: text-embedding-3-small
|
||||
|
||||
exports:
|
||||
embeddings: "{{response.embeddings}}"
|
||||
|
||||
# ===========================================================================
|
||||
# COMMON STEP FIELDS: on_success, on_error, decision
|
||||
# These fields are available on ALL step types
|
||||
# ===========================================================================
|
||||
- name: step-with-handlers
|
||||
type: bash
|
||||
log: "Step demonstrating success/error handlers and decision routing"
|
||||
command: 'echo "Running step with all handler types"'
|
||||
|
||||
# on_success: Actions to execute when step succeeds
|
||||
on_success:
|
||||
# action: Handler type - log, abort, continue, export, run, notify
|
||||
- action: log
|
||||
message: "Step completed successfully for {{Target}}"
|
||||
|
||||
- action: export
|
||||
# name: Variable name to export
|
||||
name: success_flag
|
||||
# value: Value to export (can be string, number, or template)
|
||||
value: "true"
|
||||
|
||||
- action: notify
|
||||
# notify: Notification message
|
||||
notify: "Step succeeded for {{Target}}"
|
||||
|
||||
- action: run
|
||||
# type: Step type to run (bash or function)
|
||||
type: bash
|
||||
command: 'echo "Running follow-up command"'
|
||||
|
||||
- action: run
|
||||
type: function
|
||||
functions:
|
||||
- 'log_info("Running follow-up function")'
|
||||
|
||||
# on_error: Actions to execute when step fails
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Step failed for {{Target}}"
|
||||
# condition: Only execute this action if condition evaluates to true
|
||||
condition: 'true'
|
||||
|
||||
- action: notify
|
||||
notify: "Error in workflow for {{Target}}"
|
||||
|
||||
# abort: Stops workflow execution immediately
|
||||
- action: abort
|
||||
message: "Aborting due to critical failure"
|
||||
condition: 'false' # Only abort under specific conditions
|
||||
|
||||
# continue: Allows workflow to continue despite error
|
||||
- action: continue
|
||||
message: "Continuing despite error"
|
||||
|
||||
# decision: Conditional routing to other steps or workflow end
|
||||
# Uses switch/case syntax for clear, maintainable routing
|
||||
decision:
|
||||
# switch: Variable or expression to match against cases
|
||||
switch: "{{success_flag}}"
|
||||
# cases: Map of values to step targets
|
||||
cases:
|
||||
"true":
|
||||
goto: final-step
|
||||
# default: Fallback if no case matches (use "_end" to finish workflow)
|
||||
default:
|
||||
goto: _end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Final step
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: final-step
|
||||
type: function
|
||||
log: "Final step - workflow complete"
|
||||
function: 'log_info("All step types demonstrated for {{Target}}")'
|
||||
@@ -0,0 +1,213 @@
|
||||
# =============================================================================
|
||||
# Module Workflow: Docker Runner Configuration Example
|
||||
# =============================================================================
|
||||
# This file demonstrates all Docker runner configuration fields at both
|
||||
# the workflow level (for all steps) and step level (per-step override).
|
||||
# =============================================================================
|
||||
|
||||
kind: module
|
||||
name: docker-runner-example
|
||||
description: Demonstrates Docker runner configuration with all available fields
|
||||
tags: docker, runner, container
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# RUNNER CONFIGURATION (Workflow-Level)
|
||||
# Applies to all steps unless overridden at step level
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# runner: Execution environment for this workflow
|
||||
# Options: host (default - local machine), docker, ssh
|
||||
runner: docker
|
||||
|
||||
# runner_config: Configuration for the selected runner type
|
||||
runner_config:
|
||||
# -------------------------------------------------------------------------
|
||||
# DOCKER-SPECIFIC CONFIGURATION
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# image: Docker image to use (required for docker runner)
|
||||
# Format: registry/image:tag or just image:tag
|
||||
image: ubuntu:22.04
|
||||
|
||||
# env: Environment variables to set inside the container
|
||||
# Map of VAR_NAME: value
|
||||
env:
|
||||
MY_VAR: my-value
|
||||
API_KEY: "{{api_key}}" # Can use template variables
|
||||
THREADS: "{{threads}}"
|
||||
|
||||
# volumes: Volume mounts in docker format
|
||||
# Format: host_path:container_path[:options]
|
||||
# Options: ro (read-only), rw (read-write)
|
||||
volumes:
|
||||
- "/tmp/osmedeus:/data"
|
||||
- "{{Output}}:/output"
|
||||
- "/etc/hosts:/etc/hosts:ro"
|
||||
|
||||
# network: Docker network mode
|
||||
# Options: bridge (default), host, none, container:<name>, or network name
|
||||
network: host
|
||||
|
||||
# persistent: Container lifecycle mode
|
||||
# true = reuse the same container across steps (faster, state preserved)
|
||||
# false = ephemeral, create new container per step (isolated, clean state)
|
||||
persistent: true
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COMMON CONFIGURATION (applies to docker and ssh)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# workdir: Working directory inside the container/remote
|
||||
# Commands will execute in this directory
|
||||
workdir: /app
|
||||
|
||||
params:
|
||||
- name: api_key
|
||||
default: "demo-key"
|
||||
|
||||
- name: threads
|
||||
default: "5"
|
||||
|
||||
steps:
|
||||
# ===========================================================================
|
||||
# Step using workflow-level runner (docker with ubuntu:22.04)
|
||||
# ===========================================================================
|
||||
- name: use-workflow-runner
|
||||
type: bash
|
||||
log: "Running in workflow-level Docker container"
|
||||
command: 'echo "Running inside ubuntu:22.04 container"'
|
||||
|
||||
# ===========================================================================
|
||||
# Step with per-step Docker runner override
|
||||
# Uses different image than workflow-level config
|
||||
# ===========================================================================
|
||||
- name: step-with-runner-override
|
||||
type: bash
|
||||
log: "Running in step-specific Docker container"
|
||||
|
||||
# step_runner: Override runner type for this step only
|
||||
# Options: host, docker, ssh
|
||||
step_runner: docker
|
||||
|
||||
# step_runner_config: Override runner configuration for this step
|
||||
# Same structure as runner_config but applies only to this step
|
||||
step_runner_config:
|
||||
# Use a different image for this specific step
|
||||
image: python:3.11-slim
|
||||
|
||||
env:
|
||||
PYTHONPATH: /app
|
||||
|
||||
volumes:
|
||||
- "{{Output}}:/output:rw"
|
||||
|
||||
network: bridge
|
||||
|
||||
persistent: false
|
||||
|
||||
workdir: /app
|
||||
|
||||
command: 'python3 -c "print(\"Running in Python container\")"'
|
||||
|
||||
# ===========================================================================
|
||||
# Remote-bash step type with Docker (explicit remote-bash type)
|
||||
# remote-bash is specifically for executing commands in remote environments
|
||||
# ===========================================================================
|
||||
- name: remote-bash-docker
|
||||
# type: remote-bash is specifically for remote execution (docker/ssh)
|
||||
type: remote-bash
|
||||
log: "Remote bash execution in Docker"
|
||||
|
||||
# step_runner: Required for remote-bash type - specifies execution environment
|
||||
# Must be "docker" or "ssh"
|
||||
step_runner: docker
|
||||
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
workdir: /tmp
|
||||
|
||||
# command/commands/parallel_commands: Same as bash step
|
||||
command: 'echo "Hello from Alpine container" > /tmp/output.txt'
|
||||
|
||||
# step_remote_file: File path on remote (inside container) to copy after execution
|
||||
# This file will be copied from the container to the host
|
||||
step_remote_file: /tmp/output.txt
|
||||
|
||||
# host_output_file: Local path where the remote file will be copied
|
||||
# Template variables are supported
|
||||
host_output_file: "{{Output}}/docker-output.txt"
|
||||
|
||||
# ===========================================================================
|
||||
# Parallel commands in Docker container
|
||||
# ===========================================================================
|
||||
- name: docker-parallel-commands
|
||||
type: bash
|
||||
log: "Running parallel commands in Docker"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: ubuntu:22.04
|
||||
persistent: true
|
||||
|
||||
parallel_commands:
|
||||
- 'sleep 2 && echo "Parallel job A completed"'
|
||||
- 'sleep 1 && echo "Parallel job B completed"'
|
||||
- 'sleep 3 && echo "Parallel job C completed"'
|
||||
|
||||
# ===========================================================================
|
||||
# Foreach loop executing in Docker
|
||||
# ===========================================================================
|
||||
- name: docker-foreach
|
||||
type: foreach
|
||||
log: "Processing items in Docker containers"
|
||||
input: "{{Output}}/targets.txt"
|
||||
variable: target
|
||||
threads: 3
|
||||
|
||||
step:
|
||||
name: process-in-docker
|
||||
type: bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: curlimages/curl:latest
|
||||
network: host
|
||||
command: 'curl -s -o /dev/null -w "%{http_code}" "[[target]]"'
|
||||
exports:
|
||||
http_status: "{{stdout}}"
|
||||
|
||||
# ===========================================================================
|
||||
# Step running on host (override workflow's docker runner)
|
||||
# ===========================================================================
|
||||
- name: run-on-host
|
||||
type: bash
|
||||
log: "Running on host machine (overriding workflow runner)"
|
||||
|
||||
# Override to run locally instead of in container
|
||||
step_runner: host
|
||||
|
||||
command: 'echo "This runs directly on the host machine"'
|
||||
|
||||
# ===========================================================================
|
||||
# Docker step with all structured arguments
|
||||
# ===========================================================================
|
||||
- name: docker-with-args
|
||||
type: bash
|
||||
log: "Docker step with structured arguments"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: nuclei:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
- "/root/nuclei-templates:/templates:ro"
|
||||
workdir: /output
|
||||
|
||||
command: nuclei
|
||||
speed_args: '-rate-limit 100 -c {{threads}}'
|
||||
config_args: '-t /templates/cves/'
|
||||
input_args: '-u {{Target}}'
|
||||
output_args: '-o /output/nuclei-results.txt'
|
||||
|
||||
step_remote_file: /output/nuclei-results.txt
|
||||
host_output_file: "{{Output}}/nuclei-results.txt"
|
||||
|
||||
exports:
|
||||
nuclei_output: "{{Output}}/nuclei-results.txt"
|
||||
@@ -0,0 +1,247 @@
|
||||
# =============================================================================
|
||||
# Module Workflow: SSH Runner Configuration Example
|
||||
# =============================================================================
|
||||
# This file demonstrates all SSH runner configuration fields at both
|
||||
# the workflow level (for all steps) and step level (per-step override).
|
||||
# =============================================================================
|
||||
|
||||
kind: module
|
||||
name: ssh-runner-example
|
||||
description: Demonstrates SSH runner configuration with all available fields
|
||||
tags: ssh, runner, remote
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# RUNNER CONFIGURATION (Workflow-Level)
|
||||
# Applies to all steps unless overridden at step level
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# runner: Execution environment for this workflow
|
||||
# Options: host (default - local machine), docker, ssh
|
||||
runner: ssh
|
||||
|
||||
# runner_config: Configuration for the selected runner type
|
||||
runner_config:
|
||||
# -------------------------------------------------------------------------
|
||||
# SSH-SPECIFIC CONFIGURATION
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# host: SSH hostname or IP address (required for ssh runner)
|
||||
# Can use template variables for dynamic targeting
|
||||
host: "{{ssh_host}}"
|
||||
|
||||
# port: SSH port number
|
||||
# Default: 22
|
||||
port: 22
|
||||
|
||||
# user: SSH username for authentication
|
||||
user: "{{ssh_user}}"
|
||||
|
||||
# key_file: Path to SSH private key file for key-based authentication
|
||||
# Preferred over password authentication for security
|
||||
key_file: "{{ssh_key_path}}"
|
||||
|
||||
# password: SSH password for password-based authentication
|
||||
# WARNING: Not recommended - use key_file instead when possible
|
||||
# Can use template variables or environment references
|
||||
# password: "{{ssh_password}}"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# COMMON CONFIGURATION (applies to docker and ssh)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# workdir: Working directory on the remote machine
|
||||
# Commands will execute in this directory
|
||||
workdir: /home/scanner/workspace
|
||||
|
||||
params:
|
||||
- name: ssh_host
|
||||
default: "192.168.1.100"
|
||||
required: true
|
||||
|
||||
- name: ssh_user
|
||||
default: "scanner"
|
||||
required: true
|
||||
|
||||
- name: ssh_key_path
|
||||
default: "~/.ssh/id_rsa"
|
||||
|
||||
- name: threads
|
||||
default: "10"
|
||||
|
||||
steps:
|
||||
# ===========================================================================
|
||||
# Step using workflow-level SSH runner
|
||||
# ===========================================================================
|
||||
- name: setup-remote-workspace
|
||||
type: bash
|
||||
log: "Setting up workspace on remote SSH server"
|
||||
command: 'mkdir -p /home/scanner/workspace/results && echo "Workspace ready"'
|
||||
|
||||
# ===========================================================================
|
||||
# Remote-bash step type with SSH (explicit remote-bash type)
|
||||
# remote-bash is specifically designed for remote execution scenarios
|
||||
# ===========================================================================
|
||||
- name: remote-bash-ssh
|
||||
# type: remote-bash is explicitly for remote execution (docker/ssh)
|
||||
type: remote-bash
|
||||
log: "Remote bash execution via SSH"
|
||||
|
||||
# step_runner: Required for remote-bash type - must be "docker" or "ssh"
|
||||
step_runner: ssh
|
||||
|
||||
# step_runner_config: SSH configuration (inherits from workflow if not set)
|
||||
# Omitting this uses workflow-level runner_config
|
||||
step_runner_config:
|
||||
host: "{{ssh_host}}"
|
||||
port: 22
|
||||
user: "{{ssh_user}}"
|
||||
key_file: "{{ssh_key_path}}"
|
||||
workdir: /tmp
|
||||
|
||||
# command: Command to execute on remote server
|
||||
command: 'hostname && whoami && pwd > /tmp/remote-info.txt'
|
||||
|
||||
# step_remote_file: File on remote server to copy back to local host
|
||||
# This is useful for retrieving results from remote execution
|
||||
step_remote_file: /tmp/remote-info.txt
|
||||
|
||||
# host_output_file: Local path where remote file will be copied
|
||||
host_output_file: "{{Output}}/remote-info.txt"
|
||||
|
||||
exports:
|
||||
remote_file: "{{Output}}/remote-info.txt"
|
||||
|
||||
# ===========================================================================
|
||||
# Step overriding SSH connection to different server
|
||||
# ===========================================================================
|
||||
- name: connect-to-secondary-server
|
||||
type: bash
|
||||
log: "Connecting to secondary server"
|
||||
|
||||
# Override workflow runner with different SSH target
|
||||
step_runner: ssh
|
||||
|
||||
step_runner_config:
|
||||
host: "192.168.1.101" # Different server
|
||||
port: 2222 # Non-standard port
|
||||
user: admin
|
||||
key_file: "~/.ssh/secondary_key"
|
||||
workdir: /opt/scanner
|
||||
|
||||
command: 'echo "Connected to secondary server" && uptime'
|
||||
|
||||
# ===========================================================================
|
||||
# Multiple sequential commands via SSH
|
||||
# ===========================================================================
|
||||
- name: ssh-multiple-commands
|
||||
type: bash
|
||||
log: "Running multiple commands on remote"
|
||||
|
||||
# commands: List of commands executed sequentially on remote
|
||||
commands:
|
||||
- 'echo "Step 1: Checking system"'
|
||||
- 'df -h'
|
||||
- 'echo "Step 2: Checking memory"'
|
||||
- 'free -m'
|
||||
- 'echo "Step 3: Checking processes"'
|
||||
- 'ps aux | head -10'
|
||||
|
||||
std_file: "{{Output}}/system-check.txt"
|
||||
|
||||
# ===========================================================================
|
||||
# Parallel commands on SSH (run concurrently on remote)
|
||||
# ===========================================================================
|
||||
- name: ssh-parallel-commands
|
||||
type: bash
|
||||
log: "Running parallel commands on remote SSH server"
|
||||
|
||||
parallel_commands:
|
||||
- 'nmap -sS -p 80 {{Target}} > /tmp/port80.txt'
|
||||
- 'nmap -sS -p 443 {{Target}} > /tmp/port443.txt'
|
||||
- 'nmap -sS -p 22 {{Target}} > /tmp/port22.txt'
|
||||
|
||||
# ===========================================================================
|
||||
# Run tool with structured arguments via SSH
|
||||
# ===========================================================================
|
||||
- name: ssh-nuclei-scan
|
||||
type: bash
|
||||
log: "Running nuclei scan via SSH"
|
||||
timeout: 3600
|
||||
|
||||
command: nuclei
|
||||
speed_args: '-rate-limit 50 -c {{threads}}'
|
||||
config_args: '-t ~/nuclei-templates/cves/'
|
||||
input_args: '-u {{Target}}'
|
||||
output_args: '-o /home/scanner/workspace/nuclei-results.json -json'
|
||||
|
||||
step_remote_file: /home/scanner/workspace/nuclei-results.json
|
||||
host_output_file: "{{Output}}/nuclei-results.json"
|
||||
|
||||
exports:
|
||||
scan_results: "{{Output}}/nuclei-results.json"
|
||||
|
||||
# ===========================================================================
|
||||
# Foreach loop with SSH execution
|
||||
# Processes multiple targets on remote server
|
||||
# ===========================================================================
|
||||
- name: ssh-foreach-targets
|
||||
type: foreach
|
||||
log: "Processing targets via SSH"
|
||||
|
||||
# input: File containing targets (one per line)
|
||||
input: "{{Output}}/targets.txt"
|
||||
|
||||
# variable: Loop variable accessed as [[variable]] in inner step
|
||||
variable: current_target
|
||||
|
||||
# threads: Number of concurrent SSH executions
|
||||
threads: 5
|
||||
|
||||
step:
|
||||
name: probe-target
|
||||
type: bash
|
||||
# Inner step inherits workflow-level SSH runner
|
||||
command: 'curl -s -o /dev/null -w "%{http_code}" "[[current_target]]" 2>/dev/null || echo "failed"'
|
||||
exports:
|
||||
probe_result: "{{stdout}}"
|
||||
|
||||
# ===========================================================================
|
||||
# Step running on local host (override workflow's SSH runner)
|
||||
# Useful for local processing of results retrieved from remote
|
||||
# ===========================================================================
|
||||
- name: process-results-locally
|
||||
type: bash
|
||||
log: "Processing results on local host"
|
||||
|
||||
# Override to run locally instead of via SSH
|
||||
step_runner: host
|
||||
|
||||
command: 'cat "{{Output}}/nuclei-results.json" | jq -r ".info.severity" | sort | uniq -c'
|
||||
|
||||
exports:
|
||||
severity_summary: "{{stdout}}"
|
||||
|
||||
# ===========================================================================
|
||||
# Function step (always runs locally, regardless of workflow runner)
|
||||
# Note: Function steps execute on the host running osmedeus, not remote
|
||||
# ===========================================================================
|
||||
- name: log-completion
|
||||
type: function
|
||||
log: "Logging scan completion"
|
||||
function: 'log_info("SSH scan completed for {{Target}}")'
|
||||
|
||||
# ===========================================================================
|
||||
# Cleanup step on remote server
|
||||
# ===========================================================================
|
||||
- name: cleanup-remote
|
||||
type: bash
|
||||
log: "Cleaning up remote workspace"
|
||||
command: 'rm -rf /home/scanner/workspace/temp/* 2>/dev/null; echo "Cleanup complete"'
|
||||
|
||||
on_success:
|
||||
- action: log
|
||||
message: "Remote cleanup completed successfully"
|
||||
|
||||
on_error:
|
||||
- action: continue
|
||||
message: "Cleanup failed but continuing workflow"
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
|
||||
{"template":"dns/txt-fingerprint.yaml","template-url":"https://cloud.projectdiscovery.io/public/txt-fingerprint","template-id":"txt-fingerprint","template-path":"/root/nuclei-templates/dns/txt-fingerprint.yaml","info":{"name":"DNS TXT Record Detected","author":["pdteam"],"tags":["dns","txt","discovery"],"description":"A DNS TXT record was detected. The TXT record lets a domain admin leave notes on a DNS server.","reference":["https://www.netspi.com/blog/technical/network-penetration-testing/analyzing-dns-txt-records-to-fingerprint-service-providers/"],"severity":"info","metadata":{"max-request":1},"classification":{"cve-id":null,"cwe-id":["cwe-200"]}},"type":"dns","host":"www.hackerone.com","matched-at":"www.hackerone.com","extracted-results":["\"v=spf1 -all\"","\"70gn9hp69jzpn3nkp42r8n9jwwtd1d70\""],"request":";; opcode: QUERY, status: NOERROR, id: 5375\n;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;www.hackerone.com.\tIN\t TXT\n","response":";; opcode: QUERY, status: NOERROR, id: 5375\n;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;www.hackerone.com.\tIN\t TXT\n\n;; ANSWER SECTION:\nwww.hackerone.com.\t300\tIN\tTXT\t\"v=spf1 -all\"\nwww.hackerone.com.\t300\tIN\tTXT\t\"70gn9hp69jzpn3nkp42r8n9jwwtd1d70\"\n","timestamp":"2026-01-17T15:39:25.320333481Z","matcher-status":true}
|
||||
{"template":"dns/spf-record-detect.yaml","template-url":"https://cloud.projectdiscovery.io/public/spf-record-detect","template-id":"spf-record-detect","template-path":"/root/nuclei-templates/dns/spf-record-detect.yaml","info":{"name":"SPF Record - Detection","author":["rxerium"],"tags":["dns","spf","discovery"],"description":"An SPF TXT record was detected\n","reference":["https://www.mimecast.com/content/how-to-create-an-spf-txt-record"],"severity":"info","metadata":{"max-request":1}},"type":"dns","host":"support.hackerone.com","matched-at":"support.hackerone.com","extracted-results":["v=spf1 -all\""],"request":";; opcode: QUERY, status: NOERROR, id: 26653\n;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;support.hackerone.com.\tIN\t TXT\n","response":";; opcode: QUERY, status: NOERROR, id: 26653\n;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 512\n\n;; QUESTION SECTION:\n;support.hackerone.com.\tIN\t TXT\n\n;; ANSWER SECTION:\nsupport.hackerone.com.\t300\tIN\tTXT\t\"v=spf1 -all\"\n","timestamp":"2026-01-17T15:39:25.321816734Z","matcher-status":true}
|
||||
{"template":"dns/txt-fingerprint.yaml","template-url":"https://cloud.projectdiscovery.io/public/txt-fingerprint","template-id":"txt-fingerprint","template-path":"/root/nuclei-templates/dns/txt-fingerprint.yaml","info":{"name":"DNS TXT Record Detected","author":["pdteam"],"tags":["dns","txt","discovery"],"description":"A DNS TXT record was detected. The TXT record lets a domain admin leave notes on a DNS server.","reference":["https://www.netspi.com/blog/technical/network-penetration-testing/analyzing-dns-txt-records-to-fingerprint-service-providers/"],"severity":"info","metadata":{"max-request":1},"classification":{"cve-id":null,"cwe-id":["cwe-200"]}},"type":"dns","host":"support.hackerone.com","matched-at":"support.hackerone.com","extracted-results":["\"v=spf1 -all\""],"request":";; opcode: QUERY, status: NOERROR, id: 26653\n;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;support.hackerone.com.\tIN\t TXT\n","response":";; opcode: QUERY, status: NOERROR, id: 26653\n;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 512\n\n;; QUESTION SECTION:\n;support.hackerone.com.\tIN\t TXT\n\n;; ANSWER SECTION:\nsupport.hackerone.com.\t300\tIN\tTXT\t\"v=spf1 -all\"\n","timestamp":"2026-01-17T15:39:25.321890195Z","matcher-status":true}
|
||||
{"template":"dns/nameserver-fingerprint.yaml","template-url":"https://cloud.projectdiscovery.io/public/nameserver-fingerprint","template-id":"nameserver-fingerprint","template-path":"/root/nuclei-templates/dns/nameserver-fingerprint.yaml","info":{"name":"NS Record Detection","author":["pdteam"],"tags":["dns","ns","discovery"],"description":"An NS record was detected. An NS record delegates a subdomain to a set of name servers.","severity":"info","metadata":{"max-request":1},"classification":{"cve-id":null,"cwe-id":["cwe-200"]}},"type":"dns","host":"hackerone.com","matched-at":"hackerone.com","extracted-results":["b.ns.hackerone.com.","a.ns.hackerone.com."],"request":";; opcode: QUERY, status: NOERROR, id: 5635\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;hackerone.com.\tIN\t NS\n","response":";; opcode: QUERY, status: NOERROR, id: 5635\n;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 512\n\n;; QUESTION SECTION:\n;hackerone.com.\tIN\t NS\n\n;; ANSWER SECTION:\nhackerone.com.\t21600\tIN\tNS\tb.ns.hackerone.com.\nhackerone.com.\t21600\tIN\tNS\ta.ns.hackerone.com.\n","timestamp":"2026-01-17T15:39:25.372727197Z","matcher-status":true}
|
||||
{"template":"dns/dns-saas-service-detection.yaml","template-url":"https://cloud.projectdiscovery.io/public/dns-saas-service-detection","template-id":"dns-saas-service-detection","template-path":"/root/nuclei-templates/dns/dns-saas-service-detection.yaml","info":{"name":"DNS SaaS Service Detection","author":["noah @thesubtlety","pdteam"],"tags":["dns","service","discovery"],"description":"A CNAME DNS record was discovered","reference":["https://ns1.com/resources/cname","https://www.theregister.com/2021/02/24/dns_cname_tracking/","https://www.ionos.com/digitalguide/hosting/technical-matters/cname-record/"],"severity":"info","metadata":{"max-request":1}},"type":"dns","host":"support.hackerone.com","matched-at":"support.hackerone.com","extracted-results":["2fe254e58a0ea8096400b2fda121ee35.freshdesk.com"],"request":";; opcode: QUERY, status: NOERROR, id: 63584\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;support.hackerone.com.\tIN\t CNAME\n","response":";; opcode: QUERY, status: NOERROR, id: 63584\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 512\n\n;; QUESTION SECTION:\n;support.hackerone.com.\tIN\t CNAME\n\n;; ANSWER SECTION:\nsupport.hackerone.com.\t60\tIN\tCNAME\t2fe254e58a0ea8096400b2fda121ee35.freshdesk.com.\n","timestamp":"2026-01-17T15:39:25.375637661Z","matcher-status":true}
|
||||
{"template":"dns/dns-saas-service-detection.yaml","template-url":"https://cloud.projectdiscovery.io/public/dns-saas-service-detection","template-id":"dns-saas-service-detection","template-path":"/root/nuclei-templates/dns/dns-saas-service-detection.yaml","info":{"name":"DNS SaaS Service Detection","author":["noah @thesubtlety","pdteam"],"tags":["dns","service","discovery"],"description":"A CNAME DNS record was discovered","reference":["https://ns1.com/resources/cname","https://www.theregister.com/2021/02/24/dns_cname_tracking/","https://www.ionos.com/digitalguide/hosting/technical-matters/cname-record/"],"severity":"info","metadata":{"max-request":1}},"type":"dns","host":"pmbounces.hackerone.com","matched-at":"pmbounces.hackerone.com","extracted-results":["pm.mtasv.net"],"request":";; opcode: QUERY, status: NOERROR, id: 32589\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;pmbounces.hackerone.com.\tIN\t CNAME\n","response":";; opcode: QUERY, status: NOERROR, id: 32589\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;pmbounces.hackerone.com.\tIN\t CNAME\n\n;; ANSWER SECTION:\npmbounces.hackerone.com.\t300\tIN\tCNAME\tpm.mtasv.net.\n","timestamp":"2026-01-17T15:39:25.378949386Z","matcher-status":true}
|
||||
{"template":"dns/spf-record-detect.yaml","template-url":"https://cloud.projectdiscovery.io/public/spf-record-detect","template-id":"spf-record-detect","template-path":"/root/nuclei-templates/dns/spf-record-detect.yaml","info":{"name":"SPF Record - Detection","author":["rxerium"],"tags":["dns","spf","discovery"],"description":"An SPF TXT record was detected\n","reference":["https://www.mimecast.com/content/how-to-create-an-spf-txt-record"],"severity":"info","metadata":{"max-request":1}},"type":"dns","host":"api.hackerone.com","matched-at":"api.hackerone.com","extracted-results":["v=spf1 -all\""],"request":";; opcode: QUERY, status: NOERROR, id: 53390\n;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;api.hackerone.com.\tIN\t TXT\n","response":";; opcode: QUERY, status: NOERROR, id: 53390\n;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;api.hackerone.com.\tIN\t TXT\n\n;; ANSWER SECTION:\napi.hackerone.com.\t300\tIN\tTXT\t\"v=spf1 -all\"\napi.hackerone.com.\t300\tIN\tTXT\t\"70gn9hp69jzpn3nkp42r8n9jwwtd1d70\"\n","timestamp":"2026-01-17T15:39:25.379088515Z","matcher-status":true}
|
||||
{"template":"dns/txt-fingerprint.yaml","template-url":"https://cloud.projectdiscovery.io/public/txt-fingerprint","template-id":"txt-fingerprint","template-path":"/root/nuclei-templates/dns/txt-fingerprint.yaml","info":{"name":"DNS TXT Record Detected","author":["pdteam"],"tags":["dns","txt","discovery"],"description":"A DNS TXT record was detected. The TXT record lets a domain admin leave notes on a DNS server.","reference":["https://www.netspi.com/blog/technical/network-penetration-testing/analyzing-dns-txt-records-to-fingerprint-service-providers/"],"severity":"info","metadata":{"max-request":1},"classification":{"cve-id":null,"cwe-id":["cwe-200"]}},"type":"dns","host":"api.hackerone.com","matched-at":"api.hackerone.com","extracted-results":["\"v=spf1 -all\"","\"70gn9hp69jzpn3nkp42r8n9jwwtd1d70\""],"request":";; opcode: QUERY, status: NOERROR, id: 53390\n;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;api.hackerone.com.\tIN\t TXT\n","response":";; opcode: QUERY, status: NOERROR, id: 53390\n;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;api.hackerone.com.\tIN\t TXT\n\n;; ANSWER SECTION:\napi.hackerone.com.\t300\tIN\tTXT\t\"v=spf1 -all\"\napi.hackerone.com.\t300\tIN\tTXT\t\"70gn9hp69jzpn3nkp42r8n9jwwtd1d70\"\n","timestamp":"2026-01-17T15:39:25.379165976Z","matcher-status":true}
|
||||
{"template":"dns/dnssec-detection.yaml","template-url":"https://cloud.projectdiscovery.io/public/dnssec-detection","template-id":"dnssec-detection","template-path":"/root/nuclei-templates/dns/dnssec-detection.yaml","info":{"name":"DNSSEC Detection","author":["pdteam"],"tags":["dns","dnssec","discovery"],"description":"Domain Name System Security Extensions (DNSSEC) are enabled. The Delegation of Signing (DS) record provides information about a signed zone file when DNSSEC enabled.","reference":["https://www.icann.org/resources/pages/dnssec-what-is-it-why-important-2019-03-05-en","https://www.cyberciti.biz/faq/unix-linux-test-and-validate-dnssec-using-dig-command-line/"],"severity":"info","metadata":{"max-request":1},"classification":{"cve-id":null,"cwe-id":["cwe-200"]}},"type":"dns","host":"hackerone.com","matched-at":"hackerone.com","request":";; opcode: QUERY, status: NOERROR, id: 60257\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;hackerone.com.\tIN\t DS\n","response":";; opcode: QUERY, status: NOERROR, id: 60257\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;hackerone.com.\tIN\t DS\n\n;; ANSWER SECTION:\nhackerone.com.\t86400\tIN\tDS\t2371 13 2 5BB3CF845BAE1692299CCE6623AF80AC8B8AB20434796D754FD20634C7282D87\n","timestamp":"2026-01-17T15:39:25.38375499Z","matcher-status":true}
|
||||
{"template":"dns/dmarc-detect.yaml","template-url":"https://cloud.projectdiscovery.io/public/dmarc-detect","template-id":"dmarc-detect","template-path":"/root/nuclei-templates/dns/dmarc-detect.yaml","info":{"name":"DNS DMARC - Detect","author":["juliosmelo"],"tags":["dns","dmarc","discovery"],"description":"DNS DMARC information was detected.\n","reference":["https://dmarc.org/","https://dmarc.org/wiki/FAQ#Why_is_DMARC_important.3F"],"severity":"info","metadata":{"max-request":1},"classification":{"cve-id":null,"cwe-id":["cwe-200"],"cvss-metrics":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N"}},"type":"dns","host":"hackerone.com","matched-at":"_dmarc.hackerone.com","extracted-results":["\"v=DMARC1; p=reject; fo=1; ri=3600; rua=mailto:fgunarop@ag.dmarcian.com,mailto:dmarc-reports@hackerone.com; ruf=mailto:fgunarop@fr.dmarcian.com;\""],"request":";; opcode: QUERY, status: NOERROR, id: 36637\n;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;_dmarc.hackerone.com.\tIN\t TXT\n","response":";; opcode: QUERY, status: NOERROR, id: 36637\n;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 512\n\n;; QUESTION SECTION:\n;_dmarc.hackerone.com.\tIN\t TXT\n\n;; ANSWER SECTION:\n_dmarc.hackerone.com.\t300\tIN\tTXT\t\"v=DMARC1; p=reject; fo=1; ri=3600; rua=mailto:fgunarop@ag.dmarcian.com,mailto:dmarc-reports@hackerone.com; ruf=mailto:fgunarop@fr.dmarcian.com;\"\n","timestamp":"2026-01-17T15:39:25.385050322Z","matcher-status":true}
|
||||
{"template":"dns/dns-saas-service-detection.yaml","template-url":"https://cloud.projectdiscovery.io/public/dns-saas-service-detection","template-id":"dns-saas-service-detection","template-path":"/root/nuclei-templates/dns/dns-saas-service-detection.yaml","info":{"name":"DNS SaaS Service Detection","author":["noah @thesubtlety","pdteam"],"tags":["dns","service","discovery"],"description":"A CNAME DNS record was discovered","reference":["https://ns1.com/resources/cname","https://www.theregister.com/2021/02/24/dns_cname_tracking/","https://www.ionos.com/digitalguide/hosting/technical-matters/cname-record/"],"severity":"info","metadata":{"max-request":1}},"matcher-name":"github","type":"dns","host":"mta-sts.forwarding.hackerone.com","matched-at":"mta-sts.forwarding.hackerone.com","extracted-results":["hacker0x01.github.io"],"request":";; opcode: QUERY, status: NOERROR, id: 521\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;mta-sts.forwarding.hackerone.com.\tIN\t CNAME\n","response":";; opcode: QUERY, status: NOERROR, id: 521\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;mta-sts.forwarding.hackerone.com.\tIN\t CNAME\n\n;; ANSWER SECTION:\nmta-sts.forwarding.hackerone.com.\t300\tIN\tCNAME\thacker0x01.github.io.\n","timestamp":"2026-01-17T15:39:25.386388112Z","matcher-status":true}
|
||||
{"template":"dns/dns-saas-service-detection.yaml","template-url":"https://cloud.projectdiscovery.io/public/dns-saas-service-detection","template-id":"dns-saas-service-detection","template-path":"/root/nuclei-templates/dns/dns-saas-service-detection.yaml","info":{"name":"DNS SaaS Service Detection","author":["noah @thesubtlety","pdteam"],"tags":["dns","service","discovery"],"description":"A CNAME DNS record was discovered","reference":["https://ns1.com/resources/cname","https://www.theregister.com/2021/02/24/dns_cname_tracking/","https://www.ionos.com/digitalguide/hosting/technical-matters/cname-record/"],"severity":"info","metadata":{"max-request":1}},"matcher-name":"github","type":"dns","host":"mta-sts.hackerone.com","matched-at":"mta-sts.hackerone.com","extracted-results":["hacker0x01.github.io"],"request":";; opcode: QUERY, status: NOERROR, id: 3273\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;mta-sts.hackerone.com.\tIN\t CNAME\n","response":";; opcode: QUERY, status: NOERROR, id: 3273\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 1232\n\n;; QUESTION SECTION:\n;mta-sts.hackerone.com.\tIN\t CNAME\n\n;; ANSWER SECTION:\nmta-sts.hackerone.com.\t300\tIN\tCNAME\thacker0x01.github.io.\n","timestamp":"2026-01-17T15:39:25.407774804Z","matcher-status":true}
|
||||
{"template":"dns/dns-saas-service-detection.yaml","template-url":"https://cloud.projectdiscovery.io/public/dns-saas-service-detection","template-id":"dns-saas-service-detection","template-path":"/root/nuclei-templates/dns/dns-saas-service-detection.yaml","info":{"name":"DNS SaaS Service Detection","author":["noah @thesubtlety","pdteam"],"tags":["dns","service","discovery"],"description":"A CNAME DNS record was discovered","reference":["https://ns1.com/resources/cname","https://www.theregister.com/2021/02/24/dns_cname_tracking/","https://www.ionos.com/digitalguide/hosting/technical-matters/cname-record/"],"severity":"info","metadata":{"max-request":1}},"matcher-name":"github","type":"dns","host":"mta-sts.managed.hackerone.com","matched-at":"mta-sts.managed.hackerone.com","extracted-results":["hacker0x01.github.io"],"request":";; opcode: QUERY, status: NOERROR, id: 42972\n;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 4096\n\n;; QUESTION SECTION:\n;mta-sts.managed.hackerone.com.\tIN\t CNAME\n","response":";; opcode: QUERY, status: NOERROR, id: 42972\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 512\n\n;; QUESTION SECTION:\n;mta-sts.managed.hackerone.com.\tIN\t CNAME\n\n;; ANSWER SECTION:\nmta-sts.managed.hackerone.com.\t300\tIN\tCNAME\thacker0x01.github.io.\n","timestamp":"2026-01-17T15:39:25.409639902Z","matcher-status":true}
|
||||
@@ -0,0 +1,66 @@
|
||||
# Vulnerability Report Generator Workflow
|
||||
# Generates a formatted security report using the sample template.
|
||||
#
|
||||
# Usage:
|
||||
# osmedeus run -m generate-vuln-report -t example.com
|
||||
#
|
||||
# This workflow assumes you have:
|
||||
# 1. Run a scan that populated the database with assets/vulnerabilities
|
||||
# 2. The sample-report-template.md exists in your Data directory
|
||||
#
|
||||
# Template location: {{Data}}/templates/sample-report-template.md
|
||||
# Output location: {{Output}}/reports/vulnerability-report.md
|
||||
|
||||
name: generate-vuln-report
|
||||
kind: module
|
||||
description: Generate vulnerability report from scan results
|
||||
tags: report,vulnerability,markdown
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
description: Target that was scanned
|
||||
|
||||
steps:
|
||||
# Ensure reports and templates directories exist
|
||||
- name: setup-dirs
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/reports {{Output}}/templates
|
||||
|
||||
# Generate a summary report with just high-severity findings
|
||||
- name: create-high-severity-template
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/templates/high-severity-report.md << 'EOF'
|
||||
# High Severity Findings - {{Workspace}}
|
||||
|
||||
**Target**: {{Target}}
|
||||
**Date**: {{TaskDate}}
|
||||
|
||||
## Critical & High Vulnerabilities
|
||||
|
||||
```osm-func
|
||||
db_select_vulnerabilities_filtered("{{Workspace}}", "critical", "", "markdown")
|
||||
```
|
||||
|
||||
```osm-func
|
||||
db_select_vulnerabilities_filtered("{{Workspace}}", "high", "", "markdown")
|
||||
```
|
||||
|
||||
---
|
||||
*Report generated by Osmedeus*
|
||||
EOF
|
||||
|
||||
- name: generate-high-severity-report
|
||||
type: function
|
||||
function: 'render_markdown_report("{{Output}}/templates/high-severity-report.md", "{{Output}}/reports/high-severity-findings.md")'
|
||||
|
||||
# Log completion
|
||||
- name: report-complete
|
||||
type: function
|
||||
function: 'log_info("Report generated at: {{Output}}/reports/high-severity-findings.md")'
|
||||
|
||||
# Show preview
|
||||
- name: preview-report
|
||||
type: bash
|
||||
command: cat {{Output}}/reports/high-severity-findings.md
|
||||
@@ -0,0 +1,27 @@
|
||||
kind: module
|
||||
name: nested-module-1
|
||||
description: First nested module for testing param and target sharing
|
||||
|
||||
params:
|
||||
- name: paramFromFlowFile
|
||||
default: "not-set"
|
||||
|
||||
- name: anotherParam1
|
||||
default: "{{Output}}/another1-{{TargetSpace}}.txt"
|
||||
|
||||
|
||||
steps:
|
||||
- name: echo-target-and-param
|
||||
type: bash
|
||||
command: |
|
||||
echo "Module 1: Target={{Target}}" >> {{anotherParam1}}
|
||||
echo "Module 1: paramFromFlowFile={{paramFromFlowFile}}" >> {{anotherParam1}}
|
||||
exports:
|
||||
module1_completed: "true"
|
||||
|
||||
- name: echo-target-and-param
|
||||
type: bash
|
||||
command: |
|
||||
echo "Module 1: Target={{Target}}" >> {{anotherParam1}}
|
||||
exports:
|
||||
module1_with_target: "module1-{{Target}}"
|
||||
@@ -0,0 +1,32 @@
|
||||
kind: module
|
||||
name: nested-module-2
|
||||
description: Second nested module for testing export propagation and param inheritance
|
||||
|
||||
params:
|
||||
- name: paramFromFlowFile
|
||||
default: "not-set"
|
||||
|
||||
- name: anotherParam2
|
||||
default: "{{Output}}/another2-{{Workspace}}.txt"
|
||||
|
||||
- name: anotherParam3as1
|
||||
default: "{{Output}}/another1-{{TargetSpace}}.txt"
|
||||
|
||||
|
||||
steps:
|
||||
- name: verify-exports-and-params
|
||||
type: bash
|
||||
command: |
|
||||
echo "Module 2: Target={{Target}}"
|
||||
echo "Module 2: module1_with_target={{module1_with_target}}"
|
||||
echo "Module 2: paramFromFlowFile={{paramFromFlowFile}}"
|
||||
echo "Module 2: module1_completed={{module1_completed}}"
|
||||
|
||||
- name: verify-exports-and-params
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{anotherParam3as1}}")'
|
||||
command: |
|
||||
echo "Module 2: paramFromFlowFile={{paramFromFlowFile}}"
|
||||
echo "Module 2: module1_completed={{module1_completed}}"
|
||||
echo "Module 2: anotherParam3as1={{anotherParam3as1}}"
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
kind: module
|
||||
name: template-foreach-module
|
||||
description: Test template rendering in foreach steps
|
||||
|
||||
params:
|
||||
- name: inputFile
|
||||
default: "{{Output}}/items-{{TargetSpace}}.txt"
|
||||
- name: outputDir
|
||||
default: "{{Output}}/processed-{{TargetSpace}}"
|
||||
|
||||
steps:
|
||||
# Create test input file
|
||||
- name: create-input
|
||||
type: bash
|
||||
command: |
|
||||
mkdir -p {{outputDir}}
|
||||
echo -e "item1\nitem2\nitem3" > {{inputFile}}
|
||||
exports:
|
||||
input_ready: "true"
|
||||
|
||||
# Foreach with templated input path
|
||||
- name: foreach-with-templates
|
||||
type: foreach
|
||||
pre_condition: 'fileExists("{{inputFile}}")'
|
||||
input: "{{inputFile}}"
|
||||
variable: item
|
||||
threads: 2
|
||||
step:
|
||||
name: process-item
|
||||
type: bash
|
||||
command: |
|
||||
echo "Processing [[item]] for {{Target}}" >> {{outputDir}}/[[item]].txt
|
||||
|
||||
# Verify results
|
||||
- name: verify-foreach
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{outputDir}}/item1.txt")'
|
||||
command: |
|
||||
echo "=== Foreach Verification ==="
|
||||
echo "Foreach completed for {{Target}}"
|
||||
echo "inputFile={{inputFile}}"
|
||||
echo "outputDir={{outputDir}}"
|
||||
echo "=== Output Directory ==="
|
||||
ls -la {{outputDir}}/
|
||||
echo "=== Item1 Contents ==="
|
||||
cat {{outputDir}}/item1.txt
|
||||
exports:
|
||||
foreach_verified: "true"
|
||||
@@ -0,0 +1,51 @@
|
||||
kind: module
|
||||
name: template-parallel-module
|
||||
description: Test template rendering in parallel-steps
|
||||
|
||||
params:
|
||||
- name: parallelOutput
|
||||
default: "{{Output}}/parallel-{{TargetSpace}}"
|
||||
|
||||
steps:
|
||||
- name: setup-parallel
|
||||
type: bash
|
||||
command: mkdir -p {{parallelOutput}}
|
||||
exports:
|
||||
parallel_dir: "{{parallelOutput}}"
|
||||
|
||||
- name: parallel-steps-with-templates
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: parallel-bash-1
|
||||
type: bash
|
||||
command: 'echo "Bash 1: {{Target}}" > {{parallelOutput}}/bash1.txt'
|
||||
exports:
|
||||
p1_done: "true"
|
||||
|
||||
- name: parallel-bash-2
|
||||
type: bash
|
||||
command: 'echo "Bash 2: {{TargetSpace}}" > {{parallelOutput}}/bash2.txt'
|
||||
exports:
|
||||
p2_done: "true"
|
||||
|
||||
- name: parallel-function
|
||||
type: function
|
||||
function: 'log_info("Parallel function for {{Target}}")'
|
||||
exports:
|
||||
p3_done: "true"
|
||||
|
||||
- name: verify-parallel
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{parallelOutput}}/bash1.txt")'
|
||||
command: |
|
||||
echo "=== Parallel Steps Verification ==="
|
||||
echo "parallel_dir={{parallel_dir}}"
|
||||
echo "p1_done={{p1_done}}"
|
||||
echo "p2_done={{p2_done}}"
|
||||
echo "p3_done={{p3_done}}"
|
||||
echo "=== Bash1 Contents ==="
|
||||
cat {{parallelOutput}}/bash1.txt
|
||||
echo "=== Bash2 Contents ==="
|
||||
cat {{parallelOutput}}/bash2.txt
|
||||
exports:
|
||||
all_parallel_done: "{{p1_done}}-{{p2_done}}-{{p3_done}}"
|
||||
@@ -0,0 +1,30 @@
|
||||
kind: flow
|
||||
name: template-rendering-flow
|
||||
description: Flow to test template rendering across all step types and fields
|
||||
tags: test,template,rendering
|
||||
|
||||
params:
|
||||
- name: flowParam
|
||||
default: "flow-{{Target}}"
|
||||
|
||||
dependencies:
|
||||
variables:
|
||||
- name: Target
|
||||
type: domain
|
||||
required: true
|
||||
|
||||
modules:
|
||||
- name: basic-rendering
|
||||
path: nested/template-rendering-module.yaml
|
||||
params:
|
||||
customPrefix: "{{flowParam}}-custom"
|
||||
|
||||
- name: foreach-rendering
|
||||
path: nested/template-foreach-module.yaml
|
||||
depends_on:
|
||||
- basic-rendering
|
||||
|
||||
- name: parallel-rendering
|
||||
path: nested/template-parallel-module.yaml
|
||||
depends_on:
|
||||
- foreach-rendering
|
||||
@@ -0,0 +1,64 @@
|
||||
kind: module
|
||||
name: template-rendering-module
|
||||
description: Test template rendering in all step fields
|
||||
|
||||
params:
|
||||
- name: customPath
|
||||
default: "{{Output}}/custom-{{TargetSpace}}.txt"
|
||||
- name: customPrefix
|
||||
default: "prefix-{{Target}}"
|
||||
- name: customTimeout
|
||||
default: "30"
|
||||
|
||||
steps:
|
||||
# 1. Bash step with all templated fields
|
||||
- name: bash-with-templates
|
||||
type: bash
|
||||
pre_condition: 'true'
|
||||
command: |
|
||||
echo "Target: {{Target}}" > {{customPath}}
|
||||
echo "Prefix: {{customPrefix}}" >> {{customPath}}
|
||||
log: "Executing bash with target {{Target}}"
|
||||
exports:
|
||||
bash_output_path: "{{customPath}}"
|
||||
bash_target: "{{Target}}"
|
||||
|
||||
# 2. Function step with templated function calls
|
||||
- name: function-with-templates
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{bash_output_path}}")'
|
||||
function: 'log_info("Processing {{Target}} with path {{customPath}}")'
|
||||
exports:
|
||||
function_result: "processed-{{Target}}"
|
||||
|
||||
# 3. Parallel commands with templates
|
||||
- name: parallel-with-templates
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- 'echo "Parallel 1: {{Target}}" >> {{customPath}}'
|
||||
- 'echo "Parallel 2: {{customPrefix}}" >> {{customPath}}'
|
||||
exports:
|
||||
parallel_done: "true"
|
||||
|
||||
# 4. Step with structured args using templates
|
||||
- name: structured-args-step
|
||||
type: bash
|
||||
command: cat {{customPath}}
|
||||
exports:
|
||||
structured_output: "{{Output}}/combined-{{TargetSpace}}.txt"
|
||||
|
||||
# 5. Final verification step
|
||||
- name: verify-all-templates
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{customPath}}")'
|
||||
command: |
|
||||
echo "=== Template Rendering Verification ==="
|
||||
echo "bash_output_path={{bash_output_path}}"
|
||||
echo "bash_target={{bash_target}}"
|
||||
echo "function_result={{function_result}}"
|
||||
echo "parallel_done={{parallel_done}}"
|
||||
echo "structured_output={{structured_output}}"
|
||||
echo "=== File Contents ==="
|
||||
cat {{customPath}}
|
||||
exports:
|
||||
all_verified: "true"
|
||||
@@ -0,0 +1,23 @@
|
||||
kind: flow
|
||||
name: testing-nested-flow
|
||||
description: Test flow for nested workflow execution with param and target sharing
|
||||
tags: test,nested
|
||||
|
||||
params:
|
||||
- name: paramFromFlowFile
|
||||
default: "flow-value"
|
||||
|
||||
dependencies:
|
||||
variables:
|
||||
- name: Target
|
||||
type: domain
|
||||
required: true
|
||||
|
||||
modules:
|
||||
- name: nested-module-one
|
||||
path: nested/nested-module-1.yaml
|
||||
|
||||
- name: nested-module-two
|
||||
path: nested/nested-module-2.yaml
|
||||
depends_on:
|
||||
- nested-module-one
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
# Sample Workflow: Generate Security Report
|
||||
# This workflow demonstrates how to use render_markdown_report() function
|
||||
# to generate customized security reports from markdown templates.
|
||||
#
|
||||
# Usage:
|
||||
# osmedeus run -m sample-report-workflow -t example.com
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Template file should exist at the specified path
|
||||
# - Database should have scan data (assets, vulnerabilities)
|
||||
|
||||
name: sample-report-workflow
|
||||
kind: module
|
||||
description: Generate security report from markdown template
|
||||
tags: report,markdown,utility
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
description: Target domain for the report
|
||||
|
||||
# Variables available in templates:
|
||||
# {{Workspace}} - Current workspace name (usually the target)
|
||||
# {{Target}} - Target domain
|
||||
# {{Output}} - Output directory path
|
||||
# {{TaskID}} - Current task/scan ID
|
||||
# {{TaskDate}} - Current date
|
||||
# {{Data}} - External data directory
|
||||
# {{Binaries}} - External binaries directory
|
||||
|
||||
steps:
|
||||
# Step 1: Create the template directory if it doesn't exist
|
||||
- name: setup-template-dir
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/templates
|
||||
|
||||
# Step 2: Create a simple inline template for demonstration
|
||||
# In production, you would use a pre-existing template file
|
||||
- name: create-demo-template
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/templates/demo-report.md << 'TEMPLATE'
|
||||
# Security Scan Report
|
||||
|
||||
**Workspace**: {{Workspace}}
|
||||
**Target**: {{Target}}
|
||||
**Generated**: {{TaskDate}}
|
||||
**Task ID**: {{TaskID}}
|
||||
|
||||
---
|
||||
|
||||
## String Functions Demo
|
||||
|
||||
```osm-func
|
||||
"Uppercase target: " + toUpperCase("{{Target}}")
|
||||
```
|
||||
|
||||
```osm-func
|
||||
"Trimmed text: [" + trim(" hello world ") + "]"
|
||||
```
|
||||
|
||||
```osm-func
|
||||
"Target length: " + len("{{Target}}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Content
|
||||
|
||||
| Function | Result |
|
||||
|----------|--------|
|
||||
| UUID | ```osm-func
|
||||
uuid()
|
||||
``` |
|
||||
| Random String | ```osm-func
|
||||
randomString(8)
|
||||
``` |
|
||||
| Contains 'example' | ```osm-func
|
||||
contains("{{Target}}", "example")
|
||||
``` |
|
||||
|
||||
---
|
||||
|
||||
## Conditional Logic
|
||||
|
||||
```osm-func
|
||||
contains("{{Target}}", ".com") ? "Target is a .com domain" : "Target is not a .com domain"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Report generated by Osmedeus*
|
||||
TEMPLATE
|
||||
|
||||
# Step 3: Render the markdown report
|
||||
- name: generate-report
|
||||
type: function
|
||||
function: 'render_markdown_report("{{Output}}/templates/demo-report.md", "{{Output}}/security-report.md")'
|
||||
|
||||
# Step 4: Verify the report was created
|
||||
- name: verify-report
|
||||
type: function
|
||||
function: 'fileExists("{{Output}}/security-report.md")'
|
||||
|
||||
# Step 5: Display report path
|
||||
- name: show-report-path
|
||||
type: function
|
||||
function: 'log_info("Report generated at: {{Output}}/security-report.md")'
|
||||
|
||||
# Step 6: Print report preview (first 50 lines)
|
||||
- name: preview-report
|
||||
type: bash
|
||||
command: head -50 {{Output}}/security-report.md
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
name: test-30s-module
|
||||
kind: module
|
||||
description: Simple module with a single 30 second sleep
|
||||
tags: test,sleep,long-running
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: start
|
||||
type: bash
|
||||
command: echo "[$(date +%H:%M:%S)] Starting 30s sleep test for {{target}}"
|
||||
|
||||
- name: long-sleep
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Sleeping for 30 seconds..."
|
||||
sleep 30
|
||||
echo "[$(date +%H:%M:%S)] Sleep complete!"
|
||||
|
||||
- name: finish
|
||||
type: bash
|
||||
command: echo "[$(date +%H:%M:%S)] Done!"
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
name: test-bash
|
||||
kind: module
|
||||
description: Test basic bash execution
|
||||
tags: test,bash,quick
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: echo-test
|
||||
type: bash
|
||||
command: echo "Hello {{target}}"
|
||||
@@ -0,0 +1,50 @@
|
||||
name: test-bool-precondition
|
||||
kind: module
|
||||
description: Test workflow for boolean params in pre_condition
|
||||
tags: test,params,boolean,precondition
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: run_scan
|
||||
type: bool
|
||||
default: true
|
||||
- name: enable_debug
|
||||
type: bool
|
||||
default: false
|
||||
|
||||
steps:
|
||||
- name: always-runs
|
||||
type: bash
|
||||
command: echo "This step always runs for {{target}}"
|
||||
|
||||
# This step only runs when run_scan is true (native boolean check)
|
||||
- name: scan-step
|
||||
type: bash
|
||||
pre_condition: "run_scan"
|
||||
command: echo "SCAN RUNNING - run_scan is true"
|
||||
|
||||
# This step only runs when run_scan is false (negation)
|
||||
- name: skip-scan-step
|
||||
type: bash
|
||||
pre_condition: "!run_scan"
|
||||
command: echo "SCAN SKIPPED - run_scan is false"
|
||||
|
||||
# This step only runs when enable_debug is true
|
||||
- name: debug-step
|
||||
type: bash
|
||||
pre_condition: "enable_debug"
|
||||
command: echo "DEBUG ENABLED"
|
||||
|
||||
# Compound condition: both must be true
|
||||
- name: scan-with-debug
|
||||
type: bash
|
||||
pre_condition: "run_scan && enable_debug"
|
||||
command: echo "SCAN WITH DEBUG MODE"
|
||||
|
||||
- name: summary
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Pre-condition Test Summary ==="
|
||||
echo "run_scan: {{run_scan}}"
|
||||
echo "enable_debug: {{enable_debug}}"
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
name: test-boolean-params
|
||||
kind: module
|
||||
description: Test workflow for validating boolean parameter handling
|
||||
tags: test,params,boolean,validation
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: enable_scan
|
||||
type: bool
|
||||
default: true
|
||||
- name: skip_notify
|
||||
type: bool
|
||||
default: false
|
||||
- name: verbose_mode
|
||||
type: bool
|
||||
default: true
|
||||
|
||||
steps:
|
||||
# Step 1: Echo all boolean params for verification
|
||||
- name: print-params
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Boolean Params Test ==="
|
||||
echo "enable_scan={{enable_scan}}"
|
||||
echo "skip_notify={{skip_notify}}"
|
||||
echo "verbose_mode={{verbose_mode}}"
|
||||
echo "target={{target}}"
|
||||
|
||||
# Step 2: Test enable_scan boolean
|
||||
- name: check-enable-scan
|
||||
type: bash
|
||||
command: "echo 'Checking enable_scan value: {{enable_scan}}'"
|
||||
exports:
|
||||
scan_enabled: "{{enable_scan}}"
|
||||
decision:
|
||||
switch: "{{enable_scan}}"
|
||||
cases:
|
||||
"true":
|
||||
goto: scan-enabled-branch
|
||||
"false":
|
||||
goto: scan-disabled-branch
|
||||
|
||||
# Step 3a: Scan is enabled (enable_scan=true)
|
||||
- name: scan-enabled-branch
|
||||
type: bash
|
||||
command: |
|
||||
echo "SCAN IS ENABLED"
|
||||
echo "Running scan for {{target}}..."
|
||||
exports:
|
||||
scan_status: "ENABLED"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: check-skip-notify
|
||||
|
||||
# Step 3b: Scan is disabled (enable_scan=false)
|
||||
- name: scan-disabled-branch
|
||||
type: bash
|
||||
command: |
|
||||
echo "SCAN IS DISABLED"
|
||||
echo "Skipping scan for {{target}}"
|
||||
exports:
|
||||
scan_status: "DISABLED"
|
||||
|
||||
# Step 4: Test skip_notify boolean
|
||||
- name: check-skip-notify
|
||||
type: bash
|
||||
command: "echo 'Checking skip_notify value: {{skip_notify}}'"
|
||||
exports:
|
||||
notify_skipped: "{{skip_notify}}"
|
||||
decision:
|
||||
switch: "{{skip_notify}}"
|
||||
cases:
|
||||
"true":
|
||||
goto: notify-skipped-branch
|
||||
"false":
|
||||
goto: notify-enabled-branch
|
||||
|
||||
# Step 5a: Notifications skipped (skip_notify=true)
|
||||
- name: notify-skipped-branch
|
||||
type: bash
|
||||
command: echo "NOTIFICATIONS SKIPPED"
|
||||
exports:
|
||||
notify_status: "SKIPPED"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: check-verbose-mode
|
||||
|
||||
# Step 5b: Notifications enabled (skip_notify=false)
|
||||
- name: notify-enabled-branch
|
||||
type: bash
|
||||
command: echo "NOTIFICATIONS ENABLED"
|
||||
exports:
|
||||
notify_status: "ENABLED"
|
||||
|
||||
# Step 6: Test verbose_mode boolean
|
||||
- name: check-verbose-mode
|
||||
type: bash
|
||||
command: "echo 'Checking verbose_mode value: {{verbose_mode}}'"
|
||||
exports:
|
||||
is_verbose: "{{verbose_mode}}"
|
||||
decision:
|
||||
switch: "{{verbose_mode}}"
|
||||
cases:
|
||||
"true":
|
||||
goto: verbose-on-branch
|
||||
"false":
|
||||
goto: verbose-off-branch
|
||||
|
||||
# Step 7a: Verbose mode on
|
||||
- name: verbose-on-branch
|
||||
type: bash
|
||||
command: "echo 'VERBOSE MODE: ON'"
|
||||
exports:
|
||||
verbose_status: "ON"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: final-summary
|
||||
|
||||
# Step 7b: Verbose mode off
|
||||
- name: verbose-off-branch
|
||||
type: bash
|
||||
command: "echo 'VERBOSE MODE: OFF'"
|
||||
exports:
|
||||
verbose_status: "OFF"
|
||||
|
||||
# Step 8: Final summary with all boolean results
|
||||
- name: final-summary
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Boolean Params Summary ==="
|
||||
echo "Target: {{target}}"
|
||||
echo ""
|
||||
echo "Input Params:"
|
||||
echo " enable_scan: {{enable_scan}}"
|
||||
echo " skip_notify: {{skip_notify}}"
|
||||
echo " verbose_mode: {{verbose_mode}}"
|
||||
echo ""
|
||||
echo "Decision Results:"
|
||||
echo " Scan Status: {{scan_status}}"
|
||||
echo " Notify Status: {{notify_status}}"
|
||||
echo " Verbose Status: {{verbose_status}}"
|
||||
echo ""
|
||||
echo "Boolean Exports:"
|
||||
echo " scan_enabled: {{scan_enabled}}"
|
||||
echo " notify_skipped: {{notify_skipped}}"
|
||||
echo " is_verbose: {{is_verbose}}"
|
||||
echo "=== Test Complete ==="
|
||||
@@ -0,0 +1,218 @@
|
||||
name: test-complex-docker-workflow
|
||||
kind: module
|
||||
description: Complex workflow demonstrating bash, function steps with docker step_runner
|
||||
tags: test,docker,comprehensive
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: output_dir
|
||||
default: /tmp/osm-complex-test
|
||||
- name: threads
|
||||
default: "5"
|
||||
|
||||
steps:
|
||||
# Step 1: Setup - Create directories using function
|
||||
- name: setup-workspace
|
||||
type: function
|
||||
log: "Setting up workspace for {{target}}"
|
||||
function: createDir("{{output_dir}}")
|
||||
exports:
|
||||
workspace_created: "output"
|
||||
|
||||
# Step 2: Create input file with bash
|
||||
- name: create-target-list
|
||||
type: bash
|
||||
log: "Creating target list for {{target}}"
|
||||
commands:
|
||||
- mkdir -p {{output_dir}}/targets
|
||||
- |
|
||||
cat > {{output_dir}}/targets/hosts.txt << 'EOF'
|
||||
sub1.{{target}}
|
||||
sub2.{{target}}
|
||||
api.{{target}}
|
||||
www.{{target}}
|
||||
admin.{{target}}
|
||||
EOF
|
||||
exports:
|
||||
target_file: "{{output_dir}}/targets/hosts.txt"
|
||||
|
||||
# Step 3: Docker-based DNS resolution simulation
|
||||
- name: dns-resolve
|
||||
type: remote-bash
|
||||
log: "Resolving DNS for targets in Docker"
|
||||
timeout: 60
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
env:
|
||||
TARGET_DOMAIN: "{{target}}"
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
workdir: /workspace
|
||||
command: |
|
||||
echo "Resolving DNS for $TARGET_DOMAIN"
|
||||
cat /workspace/targets/hosts.txt | while read host; do
|
||||
echo "$host -> 127.0.0.1" >> /workspace/dns-resolved.txt
|
||||
done
|
||||
echo "DNS resolution complete"
|
||||
exports:
|
||||
dns_output: "{{output_dir}}/dns-resolved.txt"
|
||||
|
||||
# Step 4: Parallel docker commands - simulating port scanning
|
||||
- name: parallel-port-scan
|
||||
type: remote-bash
|
||||
log: "Running parallel port scans in Docker"
|
||||
timeout: 120
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
parallel_commands:
|
||||
- 'echo "Scanning ports 1-1000 on {{target}}" && sleep 1 && echo "Port 80 open" > /workspace/ports-1.txt'
|
||||
- 'echo "Scanning ports 1001-2000 on {{target}}" && sleep 1 && echo "Port 443 open" > /workspace/ports-2.txt'
|
||||
- 'echo "Scanning ports 2001-3000 on {{target}}" && sleep 1 && echo "Port 8080 open" > /workspace/ports-3.txt'
|
||||
- 'echo "Scanning ports 3001-4000 on {{target}}" && sleep 1 && echo "Port 3306 open" > /workspace/ports-4.txt'
|
||||
|
||||
# Step 5: Merge port scan results
|
||||
- name: merge-port-results
|
||||
type: bash
|
||||
log: "Merging port scan results"
|
||||
command: cat {{output_dir}}/ports-*.txt > {{output_dir}}/all-ports.txt
|
||||
exports:
|
||||
ports_file: "{{output_dir}}/all-ports.txt"
|
||||
|
||||
# Step 6: Function to check file existence
|
||||
- name: verify-ports-file
|
||||
type: function
|
||||
log: "Verifying ports file exists"
|
||||
function: fileExists("{{ports_file}}")
|
||||
exports:
|
||||
ports_verified: "output"
|
||||
|
||||
# Step 7: Docker-based HTTP probing with parallel steps
|
||||
- name: http-probe-parallel
|
||||
type: parallel-steps
|
||||
log: "Running parallel HTTP probes"
|
||||
parallel_steps:
|
||||
- name: probe-http
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
command: |
|
||||
echo "Probing HTTP on port 80"
|
||||
echo "http://{{target}}:80 [200]" > /workspace/http-80.txt
|
||||
- name: probe-https
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
command: |
|
||||
echo "Probing HTTPS on port 443"
|
||||
echo "https://{{target}}:443 [200]" > /workspace/https-443.txt
|
||||
- name: probe-alt
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
command: |
|
||||
echo "Probing alternate port 8080"
|
||||
echo "http://{{target}}:8080 [404]" > /workspace/http-8080.txt
|
||||
|
||||
# Step 8: Foreach loop with docker - process each subdomain
|
||||
- name: process-subdomains
|
||||
type: foreach
|
||||
log: "Processing each subdomain"
|
||||
input: "{{output_dir}}/targets/hosts.txt"
|
||||
variable: subdomain
|
||||
threads: 3
|
||||
step:
|
||||
name: scan-subdomain
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
command: |
|
||||
echo "Scanning [[subdomain]]..."
|
||||
echo "[[subdomain]]: status=200, title=Example" >> /workspace/subdomain-results.txt
|
||||
|
||||
# Step 9: Read results with function
|
||||
- name: read-subdomain-results
|
||||
type: function
|
||||
log: "Reading subdomain scan results"
|
||||
function: readFile("{{output_dir}}/subdomain-results.txt")
|
||||
exports:
|
||||
scan_results: "output"
|
||||
|
||||
# Step 10: Decision based routing
|
||||
- name: check-results
|
||||
type: bash
|
||||
log: "Checking scan results"
|
||||
command: wc -l < {{output_dir}}/subdomain-results.txt
|
||||
exports:
|
||||
result_count: "output"
|
||||
decision:
|
||||
switch: "{{result_count}}"
|
||||
cases:
|
||||
"0":
|
||||
goto: _end
|
||||
default:
|
||||
goto: generate-report
|
||||
|
||||
# Step 11: Generate final report in docker
|
||||
- name: generate-report
|
||||
type: remote-bash
|
||||
log: "Generating final report"
|
||||
timeout: 30
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{output_dir}}:/workspace"
|
||||
commands:
|
||||
- echo "=== Scan Report for {{target}} ===" > /workspace/report.txt
|
||||
- echo "" >> /workspace/report.txt
|
||||
- echo "--- DNS Results ---" >> /workspace/report.txt
|
||||
- cat /workspace/dns-resolved.txt >> /workspace/report.txt 2>/dev/null || echo "No DNS results" >> /workspace/report.txt
|
||||
- echo "" >> /workspace/report.txt
|
||||
- echo "--- Open Ports ---" >> /workspace/report.txt
|
||||
- cat /workspace/all-ports.txt >> /workspace/report.txt 2>/dev/null || echo "No ports found" >> /workspace/report.txt
|
||||
- echo "" >> /workspace/report.txt
|
||||
- echo "--- Subdomain Results ---" >> /workspace/report.txt
|
||||
- cat /workspace/subdomain-results.txt >> /workspace/report.txt 2>/dev/null || echo "No subdomain results" >> /workspace/report.txt
|
||||
- echo "" >> /workspace/report.txt
|
||||
- echo "Report generated at $(date)" >> /workspace/report.txt
|
||||
exports:
|
||||
report_file: "{{output_dir}}/report.txt"
|
||||
|
||||
# Step 12: Parallel functions to get file stats
|
||||
- name: get-file-stats
|
||||
type: function
|
||||
log: "Getting file statistics"
|
||||
parallel_functions:
|
||||
- fileLength("{{output_dir}}/report.txt")
|
||||
- fileExists("{{output_dir}}/all-ports.txt")
|
||||
- trim(" {{target}} ")
|
||||
exports:
|
||||
file_stats: "output"
|
||||
|
||||
# Step 13: Cleanup (optional - controlled by pre_condition)
|
||||
- name: cleanup-temp-files
|
||||
type: bash
|
||||
log: "Cleaning up temporary files"
|
||||
pre_condition: "false"
|
||||
command: rm -rf {{output_dir}}/ports-*.txt
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Cleanup failed but continuing"
|
||||
- action: continue
|
||||
@@ -0,0 +1,68 @@
|
||||
name: test-decision-switch
|
||||
kind: module
|
||||
description: Test workflow demonstrating switch/case decision syntax
|
||||
|
||||
params:
|
||||
- name: target_type
|
||||
type: string
|
||||
default: "domain"
|
||||
|
||||
steps:
|
||||
- name: detect-type
|
||||
type: bash
|
||||
command: echo "{{target_type}}"
|
||||
exports:
|
||||
detected_type: "{{target_type}}"
|
||||
decision:
|
||||
switch: "{{detected_type}}"
|
||||
cases:
|
||||
"domain":
|
||||
goto: subdomain-enum
|
||||
"ip":
|
||||
goto: port-scan
|
||||
"cidr":
|
||||
goto: network-scan
|
||||
"url":
|
||||
goto: web-scan
|
||||
default:
|
||||
goto: generic-recon
|
||||
|
||||
- name: subdomain-enum
|
||||
type: bash
|
||||
command: echo "Running subdomain enumeration for {{target}}"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: _end
|
||||
|
||||
- name: port-scan
|
||||
type: bash
|
||||
command: echo "Running port scan for {{target}}"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: _end
|
||||
|
||||
- name: network-scan
|
||||
type: bash
|
||||
command: echo "Running network scan for {{target}}"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: _end
|
||||
|
||||
- name: web-scan
|
||||
type: bash
|
||||
command: echo "Running web scan for {{target}}"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: _end
|
||||
|
||||
- name: generic-recon
|
||||
type: bash
|
||||
command: echo "Running generic reconnaissance for {{target}}"
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
name: test-decision
|
||||
kind: module
|
||||
description: Test conditional step routing with decision
|
||||
tags: test,decision,conditional
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: check-condition
|
||||
type: bash
|
||||
command: echo "{{target}}"
|
||||
exports:
|
||||
target_value: "output"
|
||||
decision:
|
||||
switch: "{{target_value}}"
|
||||
cases:
|
||||
"skip":
|
||||
goto: _end
|
||||
"jump":
|
||||
goto: final-step
|
||||
|
||||
- name: middle-step
|
||||
type: bash
|
||||
command: echo "middle executed"
|
||||
exports:
|
||||
middle_output: "output"
|
||||
|
||||
- name: final-step
|
||||
type: bash
|
||||
command: echo "final executed"
|
||||
exports:
|
||||
final_output: "output"
|
||||
@@ -0,0 +1,62 @@
|
||||
name: test-docker-file-outputs
|
||||
kind: module
|
||||
description: Test std_file, step_remote_file, and host_output_file with Docker runner
|
||||
tags: test,docker,file-output
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: output_dir
|
||||
default: /tmp/osm-docker-file-test
|
||||
|
||||
steps:
|
||||
# Test 1: std_file - capture stdout to local file
|
||||
- name: test-std-file
|
||||
type: remote-bash
|
||||
log: "Testing std_file output capture"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
command: 'echo "stdout from docker: {{target}}" && echo "line 2"'
|
||||
std_file: "{{output_dir}}/std_file_output.txt"
|
||||
|
||||
# Test 2: step_remote_file + host_output_file - copy file from container
|
||||
- name: test-remote-file-copy
|
||||
type: remote-bash
|
||||
log: "Testing file copy from Docker container"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
persistent: true
|
||||
commands:
|
||||
- 'echo "created in container: {{target}}" > /tmp/container-output.txt'
|
||||
- 'cat /tmp/container-output.txt'
|
||||
step_remote_file: /tmp/container-output.txt
|
||||
host_output_file: "{{output_dir}}/copied_from_container.txt"
|
||||
|
||||
# Test 3: Combined - both std_file and remote file copy
|
||||
- name: test-combined-outputs
|
||||
type: remote-bash
|
||||
log: "Testing combined std_file and remote file copy"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
persistent: true
|
||||
commands:
|
||||
- 'echo "Processing target: {{target}}"'
|
||||
- 'echo "result-data-{{target}}" > /tmp/result.txt'
|
||||
std_file: "{{output_dir}}/combined_stdout.txt"
|
||||
step_remote_file: /tmp/result.txt
|
||||
host_output_file: "{{output_dir}}/combined_result.txt"
|
||||
|
||||
# Test 4: Verify files exist on host
|
||||
- name: verify-outputs
|
||||
type: bash
|
||||
log: "Verifying all output files exist"
|
||||
commands:
|
||||
- 'test -f "{{output_dir}}/std_file_output.txt" && echo "std_file: OK"'
|
||||
- 'test -f "{{output_dir}}/copied_from_container.txt" && echo "remote_copy: OK"'
|
||||
- 'test -f "{{output_dir}}/combined_stdout.txt" && echo "combined_stdout: OK"'
|
||||
- 'test -f "{{output_dir}}/combined_result.txt" && echo "combined_result: OK"'
|
||||
- 'cat "{{output_dir}}/std_file_output.txt"'
|
||||
- 'cat "{{output_dir}}/copied_from_container.txt"'
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
name: test-docker-flow
|
||||
kind: flow
|
||||
description: Flow orchestrating multiple Docker-based security scanning modules
|
||||
tags: test,flow,docker
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: Output
|
||||
default: /tmp/osm-docker-flow
|
||||
- name: mode
|
||||
default: "full"
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: skip_vuln_scan
|
||||
default: "false"
|
||||
|
||||
modules:
|
||||
# Module 1: Initial reconnaissance
|
||||
- name: recon-module
|
||||
path: modules/test-docker-recon
|
||||
params:
|
||||
target: "{{target}}"
|
||||
output_dir: "{{Output}}/recon"
|
||||
threads: "{{threads}}"
|
||||
on_success:
|
||||
- action: log
|
||||
message: "Reconnaissance completed for {{target}}"
|
||||
- action: export
|
||||
key: recon_complete
|
||||
value: "true"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Reconnaissance failed for {{target}}"
|
||||
- action: abort
|
||||
|
||||
# Module 2: Subdomain enumeration (depends on recon)
|
||||
- name: subdomain-module
|
||||
path: modules/test-docker-subdomain
|
||||
depends_on:
|
||||
- recon-module
|
||||
params:
|
||||
target: "{{target}}"
|
||||
output_dir: "{{Output}}/subdomains"
|
||||
wordlist: "/usr/share/wordlists/subdomains.txt"
|
||||
condition: "mode == 'full' || mode == 'subdomain'"
|
||||
on_success:
|
||||
- action: export
|
||||
key: subdomains_file
|
||||
value: "{{Output}}/subdomains/all.txt"
|
||||
|
||||
# Module 3: Port scanning (parallel with subdomain)
|
||||
- name: portscan-module
|
||||
path: modules/test-docker-portscan
|
||||
depends_on:
|
||||
- recon-module
|
||||
params:
|
||||
target: "{{target}}"
|
||||
output_dir: "{{Output}}/ports"
|
||||
port_range: "1-10000"
|
||||
rate: "1000"
|
||||
condition: "mode == 'full' || mode == 'portscan'"
|
||||
|
||||
# Module 4: HTTP probing (depends on subdomain results)
|
||||
- name: httpx-module
|
||||
path: modules/test-docker-httpx
|
||||
depends_on:
|
||||
- subdomain-module
|
||||
params:
|
||||
input: "{{subdomains_file}}"
|
||||
output_dir: "{{Output}}/http"
|
||||
threads: "{{threads}}"
|
||||
on_success:
|
||||
- action: export
|
||||
key: alive_hosts
|
||||
value: "{{Output}}/http/alive.txt"
|
||||
- action: export
|
||||
key: httpx_json
|
||||
value: "{{Output}}/http/httpx.json"
|
||||
decision:
|
||||
switch: "{{alive_count}}"
|
||||
cases:
|
||||
"0":
|
||||
goto: report-module
|
||||
|
||||
# Module 5: Technology detection (depends on HTTP probe)
|
||||
- name: tech-detect-module
|
||||
path: modules/test-docker-techdetect
|
||||
depends_on:
|
||||
- httpx-module
|
||||
params:
|
||||
input: "{{alive_hosts}}"
|
||||
output_dir: "{{Output}}/tech"
|
||||
|
||||
# Module 6: Screenshot capture (parallel with tech detection)
|
||||
- name: screenshot-module
|
||||
path: modules/test-docker-screenshot
|
||||
depends_on:
|
||||
- httpx-module
|
||||
params:
|
||||
input: "{{alive_hosts}}"
|
||||
output_dir: "{{Output}}/screenshots"
|
||||
threads: "5"
|
||||
|
||||
# Module 7: Vulnerability scanning (conditional)
|
||||
- name: vulnscan-module
|
||||
path: modules/test-docker-scanning
|
||||
depends_on:
|
||||
- httpx-module
|
||||
- tech-detect-module
|
||||
params:
|
||||
target: "{{target}}"
|
||||
Output: "{{Output}}/vulns"
|
||||
severity: "critical,high,medium"
|
||||
threads: "{{threads}}"
|
||||
condition: "skip_vuln_scan != 'true'"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Vulnerability scan encountered errors but continuing"
|
||||
- action: continue
|
||||
|
||||
# Module 8: Directory bruteforcing (optional - depends on mode)
|
||||
- name: dirbrute-module
|
||||
path: modules/test-docker-dirbrute
|
||||
depends_on:
|
||||
- httpx-module
|
||||
params:
|
||||
input: "{{alive_hosts}}"
|
||||
output_dir: "{{Output}}/dirs"
|
||||
wordlist: "/usr/share/wordlists/common.txt"
|
||||
threads: "20"
|
||||
condition: "mode == 'full'"
|
||||
|
||||
# Module 9: JavaScript analysis (depends on dir results)
|
||||
- name: js-analysis-module
|
||||
path: modules/test-docker-jsanalysis
|
||||
depends_on:
|
||||
- dirbrute-module
|
||||
params:
|
||||
input: "{{Output}}/dirs/js-files.txt"
|
||||
output_dir: "{{Output}}/js"
|
||||
condition: "mode == 'full'"
|
||||
|
||||
# Module 10: Final report generation
|
||||
- name: report-module
|
||||
path: modules/test-docker-report
|
||||
depends_on:
|
||||
- screenshot-module
|
||||
- vulnscan-module
|
||||
- tech-detect-module
|
||||
params:
|
||||
target: "{{target}}"
|
||||
input_dir: "{{Output}}"
|
||||
output_dir: "{{Output}}/reports"
|
||||
format: "html,json,markdown"
|
||||
on_success:
|
||||
- action: log
|
||||
message: "Flow completed successfully for {{target}}"
|
||||
- action: notify
|
||||
message: "Security assessment complete: {{target}}"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
name: test-docker-runner
|
||||
kind: module
|
||||
description: Test Docker runner execution
|
||||
tags: test,runner,docker
|
||||
|
||||
runner: docker
|
||||
runner_config:
|
||||
image: alpine:latest
|
||||
persistent: false
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: check-alpine
|
||||
type: bash
|
||||
command: cat /etc/os-release | grep -i alpine
|
||||
+319
@@ -0,0 +1,319 @@
|
||||
name: test-docker-scanning
|
||||
kind: module
|
||||
description: Realistic security scanning simulation with Docker-based tools
|
||||
tags: test,docker,scanning
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: Output
|
||||
default: /tmp/osm-docker-scan
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: severity
|
||||
default: "critical,high,medium"
|
||||
- name: rate_limit
|
||||
default: "100"
|
||||
|
||||
steps:
|
||||
# Phase 1: Initialization
|
||||
- name: init-workspace
|
||||
type: function
|
||||
log: "Initializing workspace for {{target}}"
|
||||
function: createDir("{{Output}}")
|
||||
|
||||
- name: create-subdirs
|
||||
type: bash
|
||||
log: "Creating output subdirectories"
|
||||
commands:
|
||||
- mkdir -p {{Output}}/recon
|
||||
- mkdir -p {{Output}}/enumeration
|
||||
- mkdir -p {{Output}}/vulnerabilities
|
||||
- mkdir -p {{Output}}/screenshots
|
||||
- mkdir -p {{Output}}/reports
|
||||
|
||||
# Phase 2: Subdomain Enumeration (Docker-based)
|
||||
- name: subdomain-enum
|
||||
type: parallel-steps
|
||||
log: "Running subdomain enumeration tools"
|
||||
parallel_steps:
|
||||
- name: subfinder-scan
|
||||
type: remote-bash
|
||||
timeout: 300
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
env:
|
||||
TARGET: "{{target}}"
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
workdir: /output
|
||||
command: |
|
||||
echo "Running subfinder for $TARGET"
|
||||
# Simulating subfinder output
|
||||
cat > /output/recon/subfinder.txt << EOF
|
||||
www.$TARGET
|
||||
api.$TARGET
|
||||
admin.$TARGET
|
||||
mail.$TARGET
|
||||
dev.$TARGET
|
||||
staging.$TARGET
|
||||
test.$TARGET
|
||||
EOF
|
||||
echo "Subfinder found $(wc -l < /output/recon/subfinder.txt) subdomains"
|
||||
exports:
|
||||
subfinder_output: "{{Output}}/recon/subfinder.txt"
|
||||
|
||||
- name: amass-scan
|
||||
type: remote-bash
|
||||
timeout: 600
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
echo "Running amass for {{target}}"
|
||||
# Simulating amass output
|
||||
cat > /output/recon/amass.txt << EOF
|
||||
www.{{target}}
|
||||
api.{{target}}
|
||||
cdn.{{target}}
|
||||
assets.{{target}}
|
||||
portal.{{target}}
|
||||
EOF
|
||||
echo "Amass found $(wc -l < /output/recon/amass.txt) subdomains"
|
||||
exports:
|
||||
amass_output: "{{Output}}/recon/amass.txt"
|
||||
|
||||
- name: crtsh-lookup
|
||||
type: remote-bash
|
||||
timeout: 120
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
echo "Querying crt.sh for {{target}}"
|
||||
# Simulating crt.sh output
|
||||
cat > /output/recon/crtsh.txt << EOF
|
||||
*.{{target}}
|
||||
www.{{target}}
|
||||
secure.{{target}}
|
||||
EOF
|
||||
exports:
|
||||
crtsh_output: "{{Output}}/recon/crtsh.txt"
|
||||
|
||||
# Phase 3: Merge and deduplicate
|
||||
- name: merge-subdomains
|
||||
type: function
|
||||
log: "Merging subdomain results"
|
||||
function: sortUnique("{{Output}}/recon/*.txt", "{{Output}}/recon/all-subdomains.txt")
|
||||
exports:
|
||||
all_subdomains: "{{Output}}/recon/all-subdomains.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to merge subdomains, attempting fallback"
|
||||
- action: run
|
||||
step: fallback-merge
|
||||
|
||||
- name: fallback-merge
|
||||
type: bash
|
||||
log: "Fallback merge using bash"
|
||||
pre_condition: "false"
|
||||
command: cat {{Output}}/recon/*.txt | sort -u > {{Output}}/recon/all-subdomains.txt
|
||||
|
||||
# Phase 4: DNS Resolution
|
||||
- name: dns-resolution
|
||||
type: foreach
|
||||
log: "Resolving DNS for discovered subdomains"
|
||||
input: "{{Output}}/recon/all-subdomains.txt"
|
||||
variable: host
|
||||
threads: 5
|
||||
step:
|
||||
name: resolve-host
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
echo "[[host]] -> 127.0.0.1" >> /output/recon/resolved.txt
|
||||
|
||||
# Phase 5: HTTP Probing (Docker-based httpx simulation)
|
||||
- name: http-probe
|
||||
type: remote-bash
|
||||
log: "Probing HTTP endpoints"
|
||||
timeout: 300
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
env:
|
||||
THREADS: "{{threads}}"
|
||||
RATE: "{{rate_limit}}"
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
commands:
|
||||
- echo "Running httpx with $THREADS threads at rate $RATE"
|
||||
- |
|
||||
while read subdomain; do
|
||||
echo "{\"url\":\"https://$subdomain\",\"status_code\":200,\"title\":\"Example\",\"tech\":[\"nginx\"]}" >> /output/enumeration/httpx.json
|
||||
done < /output/recon/all-subdomains.txt
|
||||
- echo "HTTP probing complete"
|
||||
exports:
|
||||
httpx_output: "{{Output}}/enumeration/httpx.json"
|
||||
|
||||
# Phase 6: Check results and decide
|
||||
- name: check-alive-hosts
|
||||
type: function
|
||||
log: "Checking alive hosts count"
|
||||
function: fileLength("{{Output}}/enumeration/httpx.json")
|
||||
exports:
|
||||
alive_count: "output"
|
||||
decision:
|
||||
switch: "{{alive_count}}"
|
||||
cases:
|
||||
"0":
|
||||
goto: no-hosts-found
|
||||
default:
|
||||
goto: extract-urls
|
||||
|
||||
- name: no-hosts-found
|
||||
type: bash
|
||||
log: "No alive hosts found"
|
||||
command: echo "No alive hosts found for {{target}}" > {{Output}}/reports/summary.txt
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: _end
|
||||
|
||||
# Phase 7: Extract URLs for scanning
|
||||
- name: extract-urls
|
||||
type: bash
|
||||
log: "Extracting URLs from httpx output"
|
||||
command: |
|
||||
grep -o '"url":"[^"]*"' {{Output}}/enumeration/httpx.json | cut -d'"' -f4 > {{Output}}/enumeration/urls.txt
|
||||
exports:
|
||||
urls_file: "{{Output}}/enumeration/urls.txt"
|
||||
|
||||
# Phase 8: Vulnerability Scanning (Parallel Docker nuclei simulation)
|
||||
- name: vuln-scan
|
||||
type: parallel-steps
|
||||
log: "Running vulnerability scans"
|
||||
parallel_steps:
|
||||
- name: nuclei-critical
|
||||
type: remote-bash
|
||||
timeout: 600
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
env:
|
||||
SEVERITY: critical
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
echo "Running nuclei with severity=$SEVERITY"
|
||||
echo "[CRITICAL] CVE-2021-44228 - Log4Shell - https://api.{{target}}" > /output/vulnerabilities/nuclei-critical.txt
|
||||
echo "Critical scan complete"
|
||||
exports:
|
||||
nuclei_critical: "{{Output}}/vulnerabilities/nuclei-critical.txt"
|
||||
|
||||
- name: nuclei-high
|
||||
type: remote-bash
|
||||
timeout: 600
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
env:
|
||||
SEVERITY: high
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
echo "Running nuclei with severity=high"
|
||||
cat > /output/vulnerabilities/nuclei-high.txt << EOF
|
||||
[HIGH] SQL Injection - https://admin.{{target}}/login
|
||||
[HIGH] XSS Reflected - https://www.{{target}}/search
|
||||
EOF
|
||||
echo "High severity scan complete"
|
||||
exports:
|
||||
nuclei_high: "{{Output}}/vulnerabilities/nuclei-high.txt"
|
||||
|
||||
- name: nuclei-medium
|
||||
type: remote-bash
|
||||
timeout: 600
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
echo "Running nuclei with severity=medium"
|
||||
cat > /output/vulnerabilities/nuclei-medium.txt << EOF
|
||||
[MEDIUM] Missing Security Headers - https://www.{{target}}
|
||||
[MEDIUM] Directory Listing - https://dev.{{target}}/static/
|
||||
[MEDIUM] Outdated Software - https://api.{{target}}
|
||||
EOF
|
||||
exports:
|
||||
nuclei_medium: "{{Output}}/vulnerabilities/nuclei-medium.txt"
|
||||
|
||||
# Phase 9: Screenshot capture (Docker-based)
|
||||
- name: take-screenshots
|
||||
type: foreach
|
||||
log: "Capturing screenshots"
|
||||
input: "{{Output}}/enumeration/urls.txt"
|
||||
variable: url
|
||||
threads: 3
|
||||
step:
|
||||
name: capture-screenshot
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
command: |
|
||||
# Simulate screenshot capture
|
||||
hash=$(echo "[[url]]" | md5sum | cut -c1-8)
|
||||
echo "Screenshot captured: [[url]]" > /output/screenshots/$hash.txt
|
||||
|
||||
# Phase 10: Generate final report
|
||||
- name: generate-report
|
||||
type: remote-bash
|
||||
log: "Generating comprehensive report"
|
||||
timeout: 60
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
commands:
|
||||
- |
|
||||
cat > /output/reports/scan-report.md << 'REPORT'
|
||||
# Security Scan Report
|
||||
## Target: {{target}}
|
||||
## Generated: $(date)
|
||||
|
||||
### Summary
|
||||
REPORT
|
||||
- 'echo "- Subdomains Found: $(wc -l < /output/recon/all-subdomains.txt 2>/dev/null || echo 0)" >> /output/reports/scan-report.md'
|
||||
- 'echo "- Alive Hosts: $(wc -l < /output/enumeration/httpx.json 2>/dev/null || echo 0)" >> /output/reports/scan-report.md'
|
||||
- 'echo "" >> /output/reports/scan-report.md'
|
||||
- 'echo "### Vulnerabilities" >> /output/reports/scan-report.md'
|
||||
- 'echo "#### Critical" >> /output/reports/scan-report.md'
|
||||
- 'cat /output/vulnerabilities/nuclei-critical.txt >> /output/reports/scan-report.md 2>/dev/null || echo "None" >> /output/reports/scan-report.md'
|
||||
- 'echo "" >> /output/reports/scan-report.md'
|
||||
- 'echo "#### High" >> /output/reports/scan-report.md'
|
||||
- 'cat /output/vulnerabilities/nuclei-high.txt >> /output/reports/scan-report.md 2>/dev/null || echo "None" >> /output/reports/scan-report.md'
|
||||
- 'echo "" >> /output/reports/scan-report.md'
|
||||
- 'echo "#### Medium" >> /output/reports/scan-report.md'
|
||||
- 'cat /output/vulnerabilities/nuclei-medium.txt >> /output/reports/scan-report.md 2>/dev/null || echo "None" >> /output/reports/scan-report.md'
|
||||
exports:
|
||||
final_report: "{{Output}}/reports/scan-report.md"
|
||||
on_success:
|
||||
- action: log
|
||||
message: "Scan completed successfully for {{target}}"
|
||||
- action: notify
|
||||
message: "Security scan complete: {{target}}"
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
name: test-echo
|
||||
kind: module
|
||||
description: Simple echo test module
|
||||
tags: test,bash,quick
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: message
|
||||
default: "Hello from Osmedeus"
|
||||
|
||||
steps:
|
||||
- name: echo-target
|
||||
type: bash
|
||||
command: echo "Target is {{target}}"
|
||||
|
||||
- name: echo-message
|
||||
type: bash
|
||||
command: echo "Message is {{message}}"
|
||||
|
||||
- name: echo-basefolder
|
||||
type: bash
|
||||
command: echo "BaseFolder is {{BaseFolder}}"
|
||||
|
||||
- name: echo-output
|
||||
type: bash
|
||||
command: echo "Output is {{Output}}"
|
||||
|
||||
- name: echo-threads
|
||||
type: bash
|
||||
command: echo "threads={{threads}} baseThreads={{baseThreads}}"
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
name: test-example-report
|
||||
kind: module
|
||||
description: Test example report with nested parallel steps with mixed types
|
||||
tags: test,parallel,nested
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
reports:
|
||||
- name: main-output
|
||||
path: "{{Output}}/sub1.txt"
|
||||
type: text
|
||||
description: Main output file from the workflow
|
||||
|
||||
- name: http-json
|
||||
path: "{{Output}}/http.json"
|
||||
type: json
|
||||
description: Structured JSON output
|
||||
|
||||
- name: markdown-report-report
|
||||
path: "{{Output}}/reports/sample-markdown-report.md"
|
||||
type: markdown
|
||||
description: Markdown report output
|
||||
|
||||
steps:
|
||||
- name: setup
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/templates/
|
||||
- 'echo "sub 1: {{target}}" > {{Output}}/sub1.txt'
|
||||
- 'echo "sub 2: {{target}}" > {{Output}}/sub2.txt'
|
||||
|
||||
# Generate a summary report with just high-severity findings
|
||||
- name: create-sample-repo-template
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/templates/sample-markdown-report.md << 'EOF'
|
||||
# Sample Repository Findings - {{Workspace}}
|
||||
|
||||
**Target**: {{Target}}
|
||||
**Date**: {{TaskDate}}
|
||||
|
||||
|
||||
```markdown
|
||||
| ID | Name | Score |
|
||||
| --- | --- | --- |
|
||||
| 1 | Alice | 90 |
|
||||
| 2 | Bob | 85 |
|
||||
| 3 | Charlie | 88 |
|
||||
```
|
||||
|
||||
---
|
||||
*Report generated by Osmedeus {{Version}}*
|
||||
EOF
|
||||
|
||||
- name: generate-sample-markdown-report
|
||||
type: function
|
||||
function: 'render_markdown_report("{{Output}}/templates/sample-markdown-report.md", "{{Output}}/reports/sample-markdown-report.md")'
|
||||
|
||||
# Generate a summary report with just high-severity findings
|
||||
- name: create-sample-repo-template
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/reports/sample-markdown-report.html << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 50%;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #333;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Sample Image with JavaScript</h1>
|
||||
|
||||
<h2>Example of <pre> Tag</h2>
|
||||
<pre>
|
||||
Name: Roman
|
||||
Course: Web Development
|
||||
Code Sample:
|
||||
function hello() {
|
||||
console.log("Hello, world!");
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2>Example of <table> Tag</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Score</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Alice</td>
|
||||
<td>90</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Bob</td>
|
||||
<td>85</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Charlie</td>
|
||||
<td>88</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<p id="message">Click the image 👆</p>
|
||||
|
||||
<img src="https://via.placeholder.com/150" id="img">
|
||||
|
||||
<script>
|
||||
document.getElementById("img").onclick = () => alert("Image clicked!");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
- name: store-html-report
|
||||
type: function
|
||||
function: 'store_artifact("{{Output}}/reports/sample-markdown-report.html", "html")'
|
||||
|
||||
- name: generate-http-json
|
||||
type: bash
|
||||
command: curl -s http://httpbin.org/get > {{Output}}/http.json
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
name: test-exports-functions
|
||||
kind: module
|
||||
description: Test exports with utility functions like fileLength, contains, fileExists, replace
|
||||
tags: test,exports,functions,utility
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: outputFile
|
||||
type: string
|
||||
default: "{{Output}}/test-output.txt"
|
||||
- name: trimTestFile
|
||||
type: string
|
||||
default: "{{Output}}/trim-test.txt"
|
||||
- name: replaceTestFile
|
||||
type: string
|
||||
default: "{{Output}}/replace-test.txt"
|
||||
|
||||
steps:
|
||||
- name: setup
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}
|
||||
- printf 'line1\nline2\nline3\nline4\nline5\n' > {{outputFile}}
|
||||
- printf ' trimmed_value ' > {{trimTestFile}}
|
||||
- printf 'hello,world,test' > {{replaceTestFile}}
|
||||
|
||||
- name: test-filelength
|
||||
type: bash
|
||||
command: echo "Checking file length"
|
||||
exports:
|
||||
line_count: "fileLength('{{outputFile}}')"
|
||||
|
||||
- name: test-trim
|
||||
type: bash
|
||||
command: echo "Checking trim"
|
||||
exports:
|
||||
trimmed_output: "trim(readFile('{{trimTestFile}}'))"
|
||||
|
||||
- name: test-contains-success
|
||||
type: bash
|
||||
command: echo "Checking contains"
|
||||
exports:
|
||||
has_success: "contains('success_completed', 'success')"
|
||||
|
||||
- name: test-contains-failure
|
||||
type: bash
|
||||
command: echo "Checking contains"
|
||||
exports:
|
||||
has_failure: "contains('success_completed', 'failure')"
|
||||
|
||||
- name: test-fileexists-true
|
||||
type: bash
|
||||
command: echo "Checking existence"
|
||||
exports:
|
||||
file_exists: "fileExists('{{outputFile}}')"
|
||||
|
||||
- name: test-fileexists-false
|
||||
type: bash
|
||||
command: echo "Checking nonexistent"
|
||||
exports:
|
||||
missing_file: "fileExists('{{Output}}/nonexistent.txt')"
|
||||
|
||||
- name: test-replace
|
||||
type: bash
|
||||
command: echo "Checking replace"
|
||||
exports:
|
||||
replaced_output: "replace(readFile('{{replaceTestFile}}'), ',', '-')"
|
||||
|
||||
- name: final-summary
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Exports Functions Summary ==="
|
||||
echo "Trimmed: [{{trimmed_output}}]"
|
||||
echo "Line count: {{line_count}}"
|
||||
echo "Has success: {{has_success}}"
|
||||
echo "Has failure: {{has_failure}}"
|
||||
echo "File exists: {{file_exists}}"
|
||||
echo "Missing file: {{missing_file}}"
|
||||
echo "Replaced: {{replaced_output}}"
|
||||
echo "=== All Exports Verified ==="
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
name: test-flow
|
||||
kind: flow
|
||||
description: Simple test flow combining modules
|
||||
tags: test,flow,orchestration
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
modules:
|
||||
- name: echo-module
|
||||
path: modules/test-echo
|
||||
|
||||
- name: loop-module
|
||||
path: modules/test-loop
|
||||
depends_on:
|
||||
- echo-module
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
name: test-foreach
|
||||
kind: module
|
||||
description: Test foreach loop
|
||||
tags: test,foreach,loop
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: create-input
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/osm-test
|
||||
- printf 'one\ntwo\nthree\n' > {{Output}}/osm-test/items.txt
|
||||
|
||||
- name: process-items
|
||||
type: foreach
|
||||
input: "{{Output}}/osm-test/items.txt"
|
||||
variable: item
|
||||
threads: 1
|
||||
step:
|
||||
name: process
|
||||
type: bash
|
||||
command: echo "Processing [[item]]" >> {{Output}}/osm-test/output.txt
|
||||
|
||||
- name: verify-output
|
||||
type: bash
|
||||
command: test -f {{Output}}/osm-test/output.txt && wc -l < {{Output}}/osm-test/output.txt
|
||||
|
||||
- name: cleanup
|
||||
type: bash
|
||||
command: rm -rf {{Output}}/osm-test
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
name: test-functions
|
||||
kind: module
|
||||
description: Test utility functions
|
||||
tags: test,functions,utility
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: create-file
|
||||
type: bash
|
||||
command: |
|
||||
echo "test content" > /tmp/test-{{target}}.txt
|
||||
echo "test content" > /tmp/test-{{target}}.txt
|
||||
echo "test content" > /tmp/test-{{target}}.txt
|
||||
|
||||
- name: check-file
|
||||
type: function
|
||||
function: fileExists("/tmp/test-{{target}}.txt")
|
||||
exports:
|
||||
exists: "output"
|
||||
|
||||
- name: read-file
|
||||
type: function
|
||||
function: readFile("/tmp/test-{{target}}.txt")
|
||||
exports:
|
||||
content: "output"
|
||||
|
||||
- name: cleanup
|
||||
type: bash
|
||||
command: rm -f /tmp/test-{{target}}.txt
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
name: test-heuristics
|
||||
kind: module
|
||||
description: Test heuristic variable detection
|
||||
tags: test,heuristics,variables
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: show-target-type
|
||||
type: bash
|
||||
command: echo "TargetType={{TargetType}}"
|
||||
|
||||
- name: show-url-vars
|
||||
type: bash
|
||||
commands:
|
||||
- echo "TargetBaseURL={{TargetBaseURL}}"
|
||||
- echo "TargetRootURL={{TargetRootURL}}"
|
||||
- echo "TargetHostname={{TargetHostname}}"
|
||||
- echo "TargetRootDomain={{TargetRootDomain}}"
|
||||
- echo "TargetHost={{TargetHost}}"
|
||||
- echo "TargetPort={{TargetPort}}"
|
||||
- echo "TargetPath={{TargetPath}}"
|
||||
- echo "TargetFileExt={{TargetFileExt}}"
|
||||
- echo "TargetScheme={{TargetScheme}}"
|
||||
|
||||
- name: show-domain-vars
|
||||
type: bash
|
||||
commands:
|
||||
- echo "TargetIsWildcard={{TargetIsWildcard}}"
|
||||
- echo "TargetResolvedIP={{TargetResolvedIP}}"
|
||||
|
||||
- name: show-space-vars
|
||||
type: bash
|
||||
commands:
|
||||
- echo "TargetSpace={{TargetSpace}}"
|
||||
- echo "HeuristicsCheck={{HeuristicsCheck}}"
|
||||
- echo "Output={{Output}}"
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
kind: module
|
||||
name: test-http-exports
|
||||
description: Test HTTP step exports with status_code and response_body comparisons using contains and regex_match
|
||||
tags: test,http,exports
|
||||
|
||||
params:
|
||||
- name: target
|
||||
default: "example.com"
|
||||
|
||||
steps:
|
||||
# ==========================================================================
|
||||
# Test 1: Status Code Comparisons
|
||||
# ==========================================================================
|
||||
- name: check-status-200
|
||||
type: http
|
||||
method: GET
|
||||
url: "https://httpbin.org/status/200"
|
||||
timeout: 30
|
||||
log: "Testing status code 200 response"
|
||||
exports:
|
||||
is_200: "check_status_200_http_resp.status_code == 200"
|
||||
is_2xx: "check_status_200_http_resp.status_code >= 200 && check_status_200_http_resp.status_code < 300"
|
||||
not_404: "check_status_200_http_resp.status_code != 404"
|
||||
|
||||
- name: verify-status-exports
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Status Code Export Tests ==="
|
||||
echo "is_200: {{is_200}}"
|
||||
echo "is_2xx: {{is_2xx}}"
|
||||
echo "not_404: {{not_404}}"
|
||||
log: "Verifying status code exports"
|
||||
|
||||
# ==========================================================================
|
||||
# Test 2: Response Body with contains()
|
||||
# ==========================================================================
|
||||
- name: check-get-contains
|
||||
type: http
|
||||
method: GET
|
||||
url: "https://httpbin.org/get?target={{target}}&foo=bar"
|
||||
headers:
|
||||
User-Agent: "Osmedeus/1.0"
|
||||
Accept: "application/json"
|
||||
timeout: 30
|
||||
log: "Testing contains() with response body"
|
||||
exports:
|
||||
has_args: "contains(check_get_contains_http_resp.response_body, 'args')"
|
||||
has_target: "contains(check_get_contains_http_resp.response_body, '{{target}}')"
|
||||
has_foo_bar: "contains(check_get_contains_http_resp.response_body, 'foo')"
|
||||
has_origin: "contains(check_get_contains_http_resp.response_body, 'origin')"
|
||||
body_not_empty: "check_get_contains_http_resp.response_body != ''"
|
||||
|
||||
- name: verify-contains-exports
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Contains Export Tests ==="
|
||||
echo "has_args: {{has_args}}"
|
||||
echo "has_target: {{has_target}}"
|
||||
echo "has_foo_bar: {{has_foo_bar}}"
|
||||
echo "has_origin: {{has_origin}}"
|
||||
echo "body_not_empty: {{body_not_empty}}"
|
||||
log: "Verifying contains exports"
|
||||
|
||||
# ==========================================================================
|
||||
# Test 3: Response Body with regex_match()
|
||||
# ==========================================================================
|
||||
- name: check-json-regex
|
||||
type: http
|
||||
method: GET
|
||||
url: "https://httpbin.org/json"
|
||||
headers:
|
||||
Accept: "application/json"
|
||||
timeout: 30
|
||||
log: "Testing regex_match() with JSON response"
|
||||
exports:
|
||||
has_slideshow: "regex_match('slideshow', check_json_regex_http_resp.response_body)"
|
||||
has_title_field: "regex_match('\"title\"', check_json_regex_http_resp.response_body)"
|
||||
has_json_object: "regex_match('^\\s*\\{', check_json_regex_http_resp.response_body)"
|
||||
has_author: "regex_match('author', check_json_regex_http_resp.response_body)"
|
||||
|
||||
- name: verify-regex-exports
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Regex Match Export Tests ==="
|
||||
echo "has_slideshow: {{has_slideshow}}"
|
||||
echo "has_title_field: {{has_title_field}}"
|
||||
echo "has_json_object: {{has_json_object}}"
|
||||
echo "has_author: {{has_author}}"
|
||||
log: "Verifying regex_match exports"
|
||||
|
||||
# ==========================================================================
|
||||
# Test 4: Combined Conditions
|
||||
# ==========================================================================
|
||||
- name: check-combined
|
||||
type: http
|
||||
method: GET
|
||||
url: "https://httpbin.org/get?scan={{target}}"
|
||||
headers:
|
||||
User-Agent: "Osmedeus/1.0"
|
||||
timeout: 30
|
||||
log: "Testing combined status and body conditions"
|
||||
exports:
|
||||
success_with_args: "check_combined_http_resp.status_code == 200 && contains(check_combined_http_resp.response_body, 'args')"
|
||||
valid_json_response: "check_combined_http_resp.status_code == 200 && regex_match('^\\s*\\{', check_combined_http_resp.response_body)"
|
||||
has_scan_param: "contains(check_combined_http_resp.response_body, 'scan') && contains(check_combined_http_resp.response_body, '{{target}}')"
|
||||
|
||||
- name: verify-combined-exports
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Combined Condition Tests ==="
|
||||
echo "success_with_args: {{success_with_args}}"
|
||||
echo "valid_json_response: {{valid_json_response}}"
|
||||
echo "has_scan_param: {{has_scan_param}}"
|
||||
log: "Verifying combined condition exports"
|
||||
|
||||
# ==========================================================================
|
||||
# Test 5: POST Request with Body Validation
|
||||
# ==========================================================================
|
||||
- name: check-post-echo
|
||||
type: http
|
||||
method: POST
|
||||
url: "https://httpbin.org/post"
|
||||
headers:
|
||||
Content-Type: "application/json"
|
||||
User-Agent: "Osmedeus/1.0"
|
||||
request_body: '{"target": "{{target}}", "action": "scan", "enabled": true}'
|
||||
timeout: 30
|
||||
log: "Testing POST with response body validation"
|
||||
exports:
|
||||
post_success: "check_post_echo_http_resp.status_code == 200"
|
||||
echoed_target: "contains(check_post_echo_http_resp.response_body, '{{target}}')"
|
||||
echoed_action: "contains(check_post_echo_http_resp.response_body, 'scan')"
|
||||
has_json_field: "regex_match('\"json\"\\s*:', check_post_echo_http_resp.response_body)"
|
||||
|
||||
- name: verify-post-exports
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== POST Export Tests ==="
|
||||
echo "post_success: {{post_success}}"
|
||||
echo "echoed_target: {{echoed_target}}"
|
||||
echo "echoed_action: {{echoed_action}}"
|
||||
echo "has_json_field: {{has_json_field}}"
|
||||
log: "Verifying POST exports"
|
||||
|
||||
# ==========================================================================
|
||||
# Final Summary
|
||||
# ==========================================================================
|
||||
- name: test-summary
|
||||
type: bash
|
||||
command: |
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "HTTP Exports Test Summary"
|
||||
echo "========================================="
|
||||
echo "Status Tests: is_200={{is_200}}, is_2xx={{is_2xx}}"
|
||||
echo "Contains Tests: has_args={{has_args}}, has_target={{has_target}}"
|
||||
echo "Regex Tests: has_slideshow={{has_slideshow}}, has_title_field={{has_title_field}}"
|
||||
echo "Combined Tests: success_with_args={{success_with_args}}"
|
||||
echo "POST Tests: post_success={{post_success}}, echoed_target={{echoed_target}}"
|
||||
echo "========================================="
|
||||
log: "Test summary"
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
kind: module
|
||||
name: test-http
|
||||
description: Test workflow for HTTP step type
|
||||
tags: test,http,api
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: api_url
|
||||
default: "https://httpbin.org"
|
||||
|
||||
steps:
|
||||
- name: http-get
|
||||
type: http
|
||||
method: GET
|
||||
url: "{{api_url}}/get?target={{target}}"
|
||||
headers:
|
||||
User-Agent: "Osmedeus/1.0"
|
||||
Accept: "application/json"
|
||||
timeout: 30
|
||||
log: "Making GET request to httpbin"
|
||||
|
||||
- name: verify-get
|
||||
type: bash
|
||||
command: 'echo "GET status: {{http_get_http_resp.status_code}}"'
|
||||
log: "Verifying GET response"
|
||||
|
||||
- name: http-post
|
||||
type: http
|
||||
method: POST
|
||||
url: "{{api_url}}/post"
|
||||
headers:
|
||||
Content-Type: "application/json"
|
||||
User-Agent: "Osmedeus/1.0"
|
||||
request_body: '{"target": "{{target}}", "action": "scan"}'
|
||||
timeout: 30
|
||||
log: "Making POST request to httpbin"
|
||||
|
||||
- name: verify-post
|
||||
type: bash
|
||||
command: 'echo "POST status: {{http_post_http_resp.status_code}}"'
|
||||
log: "Verifying POST response"
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
name: test-jsonl-utils
|
||||
kind: module
|
||||
description: Test JSONL utility functions
|
||||
tags: test,jsonl,utilities
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: create-jsonl
|
||||
type: bash
|
||||
command: |
|
||||
mkdir -p {{Output}}
|
||||
cat > {{Output}}/in.jsonl << 'EOF'
|
||||
{"name":"Alice","age":30,"hash":{"body_sha256":"abc"}}
|
||||
{"name":"Bob","age":25}
|
||||
{"name":"Alice","age":30,"hash":{"body_sha256":"abc"}}
|
||||
EOF
|
||||
|
||||
- name: jsonl-filter
|
||||
type: function
|
||||
function: jsonl_filter("{{Output}}/in.jsonl", "{{Output}}/filtered.jsonl", "name,hash.body_sha256")
|
||||
|
||||
- name: jsonl-to-csv
|
||||
type: function
|
||||
function: jsonl_to_csv("{{Output}}/in.jsonl", "{{Output}}/out.csv")
|
||||
|
||||
- name: csv-to-jsonl
|
||||
type: function
|
||||
function: csv_to_jsonl("{{Output}}/out.csv", "{{Output}}/back.jsonl")
|
||||
|
||||
- name: jsonl-unique
|
||||
type: function
|
||||
function: jsonl_unique("{{Output}}/in.jsonl", "{{Output}}/unique.jsonl", "name,hash.body_sha256")
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
kind: module
|
||||
name: test-llm
|
||||
description: Test LLM step execution
|
||||
tags: test,llm,quick
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
default: example.com
|
||||
|
||||
steps:
|
||||
# Basic chat completion
|
||||
- name: basic-chat
|
||||
type: llm
|
||||
log: "Running basic chat completion"
|
||||
messages:
|
||||
- role: user
|
||||
content: "Say hello to {{Target}} in one sentence."
|
||||
timeout: 60
|
||||
exports:
|
||||
greeting: "{{basic_chat_content}}"
|
||||
|
||||
# Chat with system prompt
|
||||
- name: with-system-prompt
|
||||
type: llm
|
||||
log: "Running chat with system prompt"
|
||||
messages:
|
||||
- role: system
|
||||
content: "You are a security analyst. Be concise."
|
||||
- role: user
|
||||
content: "What is {{Target}}?"
|
||||
timeout: 60
|
||||
|
||||
# Step-level config override
|
||||
- name: with-config-override
|
||||
type: llm
|
||||
log: "Running with config override"
|
||||
messages:
|
||||
- role: user
|
||||
content: "Describe {{Target}} briefly."
|
||||
llm_config:
|
||||
max_tokens: 100
|
||||
temperature: 0.3
|
||||
timeout: 60
|
||||
|
||||
# Structured JSON output
|
||||
- name: structured-output
|
||||
type: llm
|
||||
log: "Running with structured JSON output"
|
||||
messages:
|
||||
- role: user
|
||||
content: "Return a JSON object with 'target' and 'type' fields for: {{Target}}"
|
||||
llm_config:
|
||||
response_format:
|
||||
type: json_object
|
||||
timeout: 60
|
||||
exports:
|
||||
json_result: "{{structured_output_content}}"
|
||||
|
||||
# Extra LLM parameters
|
||||
- name: extra-params
|
||||
type: llm
|
||||
log: "Running with extra parameters"
|
||||
messages:
|
||||
- role: user
|
||||
content: "What is {{Target}}?"
|
||||
extra_llm_parameters:
|
||||
top_k: 40
|
||||
repeat_penalty: 1.1
|
||||
timeout: 60
|
||||
|
||||
# Multimodal content (example structure - would need actual image)
|
||||
# - name: multimodal
|
||||
# type: llm
|
||||
# log: "Running multimodal analysis"
|
||||
# messages:
|
||||
# - role: user
|
||||
# content:
|
||||
# - type: text
|
||||
# text: "What do you see in this image?"
|
||||
# - type: image_url
|
||||
# image_url:
|
||||
# url: "data:image/png;base64,{{screenshot_base64}}"
|
||||
# timeout: 60
|
||||
|
||||
# Tool call example
|
||||
- name: with-tools
|
||||
type: llm
|
||||
log: "Running with tools"
|
||||
messages:
|
||||
- role: user
|
||||
content: "What DNS records exist for {{Target}}?"
|
||||
tools:
|
||||
- type: function
|
||||
function:
|
||||
name: dns_lookup
|
||||
description: "Look up DNS records for a domain"
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
domain:
|
||||
type: string
|
||||
description: "The domain to look up"
|
||||
record_type:
|
||||
type: string
|
||||
enum: ["A", "AAAA", "MX", "TXT", "NS", "CNAME"]
|
||||
required:
|
||||
- domain
|
||||
tool_choice: auto
|
||||
timeout: 60
|
||||
|
||||
# Embedding example
|
||||
- name: generate-embedding
|
||||
type: llm
|
||||
log: "Generating embeddings"
|
||||
is_embedding: true
|
||||
embedding_input:
|
||||
- "{{Target}} security analysis"
|
||||
- "vulnerability assessment for {{Target}}"
|
||||
timeout: 60
|
||||
exports:
|
||||
embeddings: "{{generate_embedding_llm_resp}}"
|
||||
|
||||
# Use previous export in next step
|
||||
- name: use-greeting
|
||||
type: bash
|
||||
log: "Using greeting from LLM"
|
||||
command: echo "LLM said - {{greeting}}"
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
name: test-loop
|
||||
kind: module
|
||||
description: Test foreach loop with threading
|
||||
tags: test,foreach,loop
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: create-input
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}
|
||||
- printf 'one\ntwo\nthree\nfour\nfive\n' > {{Output}}/items.txt
|
||||
|
||||
- name: process-items
|
||||
type: foreach
|
||||
input: "{{Output}}/items.txt"
|
||||
variable: item
|
||||
threads: 2
|
||||
step:
|
||||
name: process-item
|
||||
type: bash
|
||||
command: echo "Processing [[item]] for {{target}}"
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
name: test-multiline
|
||||
kind: module
|
||||
description: Test multi-line functions and commands
|
||||
tags: test,multiline,functions
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: sleep_time
|
||||
default: "2"
|
||||
|
||||
steps:
|
||||
# Multi-line bash command
|
||||
- name: multiline-bash
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Phase 1: Starting for {{target}}..."
|
||||
sleep {{sleep_time}}
|
||||
echo "[$(date +%H:%M:%S)] Phase 1 complete"
|
||||
|
||||
# Setup test markdown file
|
||||
- name: setup-markdown
|
||||
type: bash
|
||||
command: |
|
||||
echo "# Test Markdown" > /tmp/test-{{target}}.md
|
||||
echo "This is a test file for **{{target}}**" >> /tmp/test-{{target}}.md
|
||||
|
||||
# Multi-line function with variables and render_markdown_from_file
|
||||
- name: multiline-function
|
||||
type: function
|
||||
function: |
|
||||
var content = render_markdown_from_file("/tmp/test-{{target}}.md");
|
||||
log_info("Rendered: " + content);
|
||||
content
|
||||
|
||||
# Cleanup
|
||||
- name: cleanup
|
||||
type: bash
|
||||
command: rm -f /tmp/test-{{target}}.md
|
||||
@@ -0,0 +1,20 @@
|
||||
name: test-parallel-commands
|
||||
kind: module
|
||||
description: Test parallel bash command execution
|
||||
tags: test,parallel,bash
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: parallel-echo
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- 'echo "command 1: {{target}}"'
|
||||
- 'echo "command 2: {{target}}"'
|
||||
- 'echo "command 3: {{target}}"'
|
||||
|
||||
- name: verify-output
|
||||
type: bash
|
||||
command: echo "All parallel commands completed for {{target}}"
|
||||
@@ -0,0 +1,26 @@
|
||||
name: test-parallel-functions
|
||||
kind: module
|
||||
description: Test parallel function execution
|
||||
tags: test,parallel,functions
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: setup-test-file
|
||||
type: bash
|
||||
command: echo "test content" > /tmp/parallel-func-test.txt
|
||||
|
||||
- name: parallel-funcs
|
||||
type: function
|
||||
parallel_functions:
|
||||
- trim(" hello ")
|
||||
- contains("hello world", "world")
|
||||
- fileExists("/tmp/parallel-func-test.txt")
|
||||
exports:
|
||||
func_results: "output"
|
||||
|
||||
- name: cleanup
|
||||
type: bash
|
||||
command: rm -f /tmp/parallel-func-test.txt
|
||||
@@ -0,0 +1,36 @@
|
||||
name: test-parallel-steps
|
||||
kind: module
|
||||
description: Test nested parallel steps with mixed types
|
||||
tags: test,parallel,nested
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: setup
|
||||
type: bash
|
||||
command: mkdir -p /tmp/parallel-steps-test
|
||||
|
||||
- name: nested-parallel
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: sub-step-1
|
||||
type: bash
|
||||
command: 'echo "sub 1: {{target}}" > /tmp/parallel-steps-test/sub1.txt'
|
||||
- name: sub-step-2
|
||||
type: bash
|
||||
command: 'echo "sub 2: {{target}}" > /tmp/parallel-steps-test/sub2.txt'
|
||||
- name: sub-step-3
|
||||
type: function
|
||||
function: 'trim(" nested ")'
|
||||
|
||||
- name: verify-files
|
||||
type: function
|
||||
function: fileExists("/tmp/parallel-steps-test/sub1.txt")
|
||||
exports:
|
||||
file_exists: "output"
|
||||
|
||||
- name: cleanup
|
||||
type: bash
|
||||
command: rm -rf /tmp/parallel-steps-test
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
name: test-params-exports
|
||||
kind: module
|
||||
description: Test workflow for validating params and step exports
|
||||
tags: test,params,exports,validation
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: enable_feature
|
||||
type: string
|
||||
default: "true"
|
||||
- name: skip_validation
|
||||
type: string
|
||||
default: "false"
|
||||
- name: custom_value
|
||||
type: string
|
||||
default: "default_value"
|
||||
|
||||
steps:
|
||||
# Step 1: Check if enable_feature param is true
|
||||
- name: check-enable-feature
|
||||
type: bash
|
||||
command: echo "Checking enable_feature={{enable_feature}}"
|
||||
exports:
|
||||
feature_enabled: "{{enable_feature}}"
|
||||
decision:
|
||||
switch: "{{feature_enabled}}"
|
||||
cases:
|
||||
"true":
|
||||
goto: feature-enabled-step
|
||||
"false":
|
||||
goto: feature-disabled-step
|
||||
|
||||
# Step 2a: Executed when feature is enabled
|
||||
- name: feature-enabled-step
|
||||
type: bash
|
||||
command: echo "Feature is ENABLED"
|
||||
exports:
|
||||
feature_status: "ENABLED"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: check-skip-validation
|
||||
|
||||
# Step 2b: Executed when feature is disabled
|
||||
- name: feature-disabled-step
|
||||
type: bash
|
||||
command: echo "Feature is DISABLED"
|
||||
exports:
|
||||
feature_status: "DISABLED"
|
||||
|
||||
# Step 3: Check skip_validation param
|
||||
- name: check-skip-validation
|
||||
type: bash
|
||||
command: echo "Checking skip_validation={{skip_validation}}"
|
||||
exports:
|
||||
should_skip: "{{skip_validation}}"
|
||||
decision:
|
||||
switch: "{{should_skip}}"
|
||||
cases:
|
||||
"true":
|
||||
goto: validation-skipped
|
||||
"false":
|
||||
goto: run-validation
|
||||
|
||||
# Step 4a: Validation skipped
|
||||
- name: validation-skipped
|
||||
type: bash
|
||||
command: echo "Validation SKIPPED"
|
||||
exports:
|
||||
validation_result: "SKIPPED"
|
||||
decision:
|
||||
switch: "always"
|
||||
cases:
|
||||
"always":
|
||||
goto: export-custom-value
|
||||
|
||||
# Step 4b: Run validation
|
||||
- name: run-validation
|
||||
type: bash
|
||||
command: echo "Validation PASSED"
|
||||
exports:
|
||||
validation_result: "PASSED"
|
||||
|
||||
# Step 5: Export custom_value param and test variable propagation
|
||||
- name: export-custom-value
|
||||
type: bash
|
||||
command: echo "Custom value={{custom_value}}"
|
||||
exports:
|
||||
exported_custom: "{{custom_value}}"
|
||||
|
||||
# Step 6: Verify all exports are accessible from previous steps
|
||||
- name: verify-exports
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Export Verification ==="
|
||||
echo "feature_enabled: {{feature_enabled}}"
|
||||
echo "feature_status: {{feature_status}}"
|
||||
echo "validation_result: {{validation_result}}"
|
||||
echo "exported_custom: {{exported_custom}}"
|
||||
echo "target: {{target}}"
|
||||
echo "=== All exports verified ==="
|
||||
exports:
|
||||
verification_complete: "true"
|
||||
|
||||
# Step 7: Final summary with all variables
|
||||
- name: final-summary
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Workflow Summary ==="
|
||||
echo "Target: {{target}}"
|
||||
echo "Enable Feature Param: {{enable_feature}}"
|
||||
echo "Feature Enabled Export: {{feature_enabled}}"
|
||||
echo "Feature Status: {{feature_status}}"
|
||||
echo "Skip Validation Param: {{skip_validation}}"
|
||||
echo "Should Skip Export: {{should_skip}}"
|
||||
echo "Validation Result: {{validation_result}}"
|
||||
echo "Custom Value Param: {{custom_value}}"
|
||||
echo "Exported Custom: {{exported_custom}}"
|
||||
echo "Verification Complete: {{verification_complete}}"
|
||||
echo "=== End Summary ==="
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
name: test-preferences
|
||||
kind: module
|
||||
description: Test workflow preferences feature - demonstrates setting CLI flags in YAML
|
||||
|
||||
# Preferences allow setting CLI-like flags directly in the workflow
|
||||
# CLI flags always take precedence over these preferences
|
||||
preferences:
|
||||
disable_notifications: true # Equivalent to --disable-notification
|
||||
disable_logging: false # Equivalent to --disable-logging
|
||||
heuristics_check: 'none' # Equivalent to --heuristics-check none
|
||||
ci_output_format: false # Equivalent to --ci-output-format
|
||||
silent: false # Equivalent to --silent
|
||||
repeat: false # Equivalent to --repeat
|
||||
repeat_wait_time: '30s' # Equivalent to --repeat-wait-time 30s
|
||||
|
||||
params:
|
||||
- name: message
|
||||
default: "Hello from preferences test"
|
||||
|
||||
steps:
|
||||
- name: echo-message
|
||||
type: bash
|
||||
command: 'echo "{{message}}"'
|
||||
|
||||
- name: show-target
|
||||
type: bash
|
||||
command: 'echo "Target is {{Target}}"'
|
||||
|
||||
- name: test-function
|
||||
type: function
|
||||
function: 'log_info("Preferences test completed for {{Target}}")'
|
||||
@@ -0,0 +1,40 @@
|
||||
name: test-remote-bash-docker
|
||||
kind: module
|
||||
description: Test remote-bash step type with Docker runner
|
||||
tags: test,remote-bash,docker
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: check-alpine-docker
|
||||
type: remote-bash
|
||||
log: "Running command in Docker container"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
command: cat /etc/os-release | grep -i alpine
|
||||
|
||||
- name: run-multiple-docker
|
||||
type: remote-bash
|
||||
log: "Running multiple commands sequentially"
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
commands:
|
||||
- 'echo "First command: {{target}}"'
|
||||
- echo "Second command"
|
||||
- hostname
|
||||
|
||||
- name: parallel-docker
|
||||
type: remote-bash
|
||||
log: "Running parallel commands"
|
||||
timeout: 30
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: alpine:latest
|
||||
parallel_commands:
|
||||
- 'echo "Parallel 1: {{target}}"'
|
||||
- 'echo "Parallel 2: {{target}}"'
|
||||
- 'echo "Parallel 3: {{target}}"'
|
||||
@@ -0,0 +1,57 @@
|
||||
name: test-remote-bash-ssh
|
||||
kind: module
|
||||
description: Test remote-bash step type with SSH runner
|
||||
tags: test,remote-bash,ssh
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: ssh_host
|
||||
default: localhost
|
||||
- name: ssh_port
|
||||
default: "2222"
|
||||
- name: ssh_user
|
||||
default: testuser
|
||||
- name: ssh_password
|
||||
default: testpass
|
||||
|
||||
steps:
|
||||
- name: check-ssh-connection
|
||||
type: remote-bash
|
||||
log: "Testing SSH connection"
|
||||
step_runner: ssh
|
||||
step_runner_config:
|
||||
host: "{{ssh_host}}"
|
||||
port: 2222
|
||||
user: "{{ssh_user}}"
|
||||
password: "{{ssh_password}}"
|
||||
command: echo "Hello from SSH" && hostname
|
||||
|
||||
- name: run-multiple-ssh
|
||||
type: remote-bash
|
||||
log: "Running multiple commands via SSH"
|
||||
step_runner: ssh
|
||||
step_runner_config:
|
||||
host: "{{ssh_host}}"
|
||||
port: 2222
|
||||
user: "{{ssh_user}}"
|
||||
password: "{{ssh_password}}"
|
||||
commands:
|
||||
- 'echo "Target: {{target}}"'
|
||||
- whoami
|
||||
- pwd
|
||||
|
||||
- name: parallel-ssh
|
||||
type: remote-bash
|
||||
log: "Running parallel commands via SSH"
|
||||
timeout: 30
|
||||
step_runner: ssh
|
||||
step_runner_config:
|
||||
host: "{{ssh_host}}"
|
||||
port: 2222
|
||||
user: "{{ssh_user}}"
|
||||
password: "{{ssh_password}}"
|
||||
parallel_commands:
|
||||
- echo "Parallel 1"
|
||||
- echo "Parallel 2"
|
||||
- echo "Parallel 3"
|
||||
@@ -0,0 +1,70 @@
|
||||
name: test-reports-params
|
||||
kind: module
|
||||
description: Test that report paths can reference params with nested template variables
|
||||
tags: test,reports,params
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: dnsFile
|
||||
type: string
|
||||
default: "{{Output}}/probing/dns-{{TargetSpace}}.txt"
|
||||
- name: httpFile
|
||||
type: string
|
||||
default: "{{Output}}/probing/http-results.txt"
|
||||
|
||||
reports:
|
||||
- name: dns-results
|
||||
path: "{{dnsFile}}"
|
||||
type: text
|
||||
- name: http-results
|
||||
path: "{{httpFile}}"
|
||||
type: text
|
||||
|
||||
steps:
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/probing
|
||||
|
||||
- name: create-dns-file
|
||||
type: bash
|
||||
command: |
|
||||
echo "ns1.{{Target}}" > {{dnsFile}}
|
||||
echo "DNS file created at: {{dnsFile}}"
|
||||
|
||||
- name: create-http-file
|
||||
type: bash
|
||||
command: |
|
||||
echo "http://{{Target}}:80" > {{httpFile}}
|
||||
echo "HTTP file created at: {{httpFile}}"
|
||||
|
||||
- name: verify-files
|
||||
type: bash
|
||||
command: |
|
||||
echo "DNS file exists: $(test -f {{dnsFile}} && echo 'yes' || echo 'no')"
|
||||
echo "HTTP file exists: $(test -f {{httpFile}} && echo 'yes' || echo 'no')"
|
||||
|
||||
- name: read-dns-content
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== DNS File Content ==="
|
||||
cat {{dnsFile}}
|
||||
echo "=== End DNS Content ==="
|
||||
|
||||
- name: read-http-content
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== HTTP File Content ==="
|
||||
cat {{httpFile}}
|
||||
echo "=== End HTTP Content ==="
|
||||
|
||||
- name: final-summary
|
||||
type: bash
|
||||
command: |
|
||||
echo "=== Reports Params Summary ==="
|
||||
echo "Target: {{Target}}"
|
||||
echo "TargetSpace: {{TargetSpace}}"
|
||||
echo "DNS File Path: {{dnsFile}}"
|
||||
echo "HTTP File Path: {{httpFile}}"
|
||||
echo "=== All Reports Verified ==="
|
||||
@@ -0,0 +1,21 @@
|
||||
name: test-requirements-fail
|
||||
kind: module
|
||||
description: Test workflow dependency validation (failure case)
|
||||
tags: test,requirements,validation
|
||||
|
||||
dependencies:
|
||||
commands:
|
||||
- echo
|
||||
- nonexistent-tool-xyz-12345
|
||||
files:
|
||||
- /tmp
|
||||
- /nonexistent/path/xyz
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: should-not-reach
|
||||
type: bash
|
||||
command: echo "Should not reach here"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
name: test-requirements
|
||||
kind: module
|
||||
description: Test workflow dependency validation (success case)
|
||||
tags: test,requirements,validation
|
||||
|
||||
dependencies:
|
||||
commands:
|
||||
- echo
|
||||
- cat
|
||||
files:
|
||||
- /tmp
|
||||
variables:
|
||||
- name: target
|
||||
type: string
|
||||
required: true
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: requirements-passed
|
||||
type: bash
|
||||
command: echo "All requirements satisfied for {{target}}"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
name: test-runner
|
||||
kind: module
|
||||
description: Test runner configuration
|
||||
tags: test,runner,host
|
||||
|
||||
# This module runs on the local host (default)
|
||||
runner: host
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: echo-runner-type
|
||||
type: bash
|
||||
command: echo "Running on host with target={{target}}"
|
||||
|
||||
- name: show-hostname
|
||||
type: bash
|
||||
command: hostname
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
name: test-sleep-flow
|
||||
kind: flow
|
||||
description: Flow that orchestrates multiple sleep modules (~60s total)
|
||||
tags: test,flow,sleep
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
modules:
|
||||
- name: recon-phase
|
||||
path: modules/test-sleep-module
|
||||
params:
|
||||
sleep_time: "10"
|
||||
|
||||
- name: parallel-phase
|
||||
path: modules/test-sleep-parallel
|
||||
depends_on:
|
||||
- recon-phase
|
||||
|
||||
- name: final-phase
|
||||
path: modules/test-sleep-module
|
||||
params:
|
||||
sleep_time: "5"
|
||||
depends_on:
|
||||
- parallel-phase
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
name: test-sleep-module
|
||||
kind: module
|
||||
description: Module that simulates long-running tasks with sleep
|
||||
tags: test,sleep,simulation
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: sleep_time
|
||||
default: "5"
|
||||
|
||||
steps:
|
||||
- name: start-task
|
||||
type: function
|
||||
function: sleep(2)
|
||||
|
||||
- name: phase-1-sleep
|
||||
type: bash
|
||||
command: echo "[$(date +%H:%M:%S)] Starting long task for {{target}}"
|
||||
|
||||
- name: phase-2-sleep
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Phase 2: Simulating scanning..."
|
||||
sleep {{sleep_time}}
|
||||
echo "[$(date +%H:%M:%S)] Phase 2 complete"
|
||||
|
||||
- name: phase-3-sleep
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Phase 3: Simulating analysis..."
|
||||
sleep {{sleep_time}}
|
||||
echo "[$(date +%H:%M:%S)] Phase 3 complete"
|
||||
|
||||
- name: finish-task
|
||||
type: bash
|
||||
command: echo "[$(date +%H:%M:%S)] All phases complete for {{target}}"
|
||||
@@ -0,0 +1,41 @@
|
||||
name: test-sleep-parallel
|
||||
kind: module
|
||||
description: Module that runs parallel sleep tasks
|
||||
tags: test,parallel,sleep
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: setup
|
||||
type: bash
|
||||
command: echo "[$(date +%H:%M:%S)] Starting parallel sleep test for {{target}}"
|
||||
|
||||
- name: parallel-sleeps
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: sleep-task-a
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Task A starting (15s)..."
|
||||
sleep 15
|
||||
echo "[$(date +%H:%M:%S)] Task A complete"
|
||||
|
||||
- name: sleep-task-b
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Task B starting (10s)..."
|
||||
sleep 10
|
||||
echo "[$(date +%H:%M:%S)] Task B complete"
|
||||
|
||||
- name: sleep-task-c
|
||||
type: bash
|
||||
command: |
|
||||
echo "[$(date +%H:%M:%S)] Task C starting (5s)..."
|
||||
sleep 5
|
||||
echo "[$(date +%H:%M:%S)] Task C complete"
|
||||
|
||||
- name: finish
|
||||
type: bash
|
||||
command: echo "[$(date +%H:%M:%S)] All parallel tasks complete (took ~15s total)"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
name: test-ssh-runner
|
||||
kind: module
|
||||
description: Test SSH runner execution
|
||||
tags: test,runner,ssh
|
||||
|
||||
runner: ssh
|
||||
runner_config:
|
||||
host: localhost
|
||||
port: 2222
|
||||
user: testuser
|
||||
password: testpass
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: check-remote
|
||||
type: bash
|
||||
command: echo "Hello from SSH" && hostname
|
||||
@@ -0,0 +1,36 @@
|
||||
kind: module
|
||||
name: test-structured-args
|
||||
description: Test workflow for structured argument fields
|
||||
tags: test,bash,args
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: rate
|
||||
default: "100"
|
||||
- name: config_file
|
||||
default: "config.yaml"
|
||||
|
||||
steps:
|
||||
- name: test-with-all-args
|
||||
type: bash
|
||||
command: "echo 'Running tool'"
|
||||
speed_args: "-t {{threads}} --rate {{rate}}"
|
||||
config_args: "-c {{config_file}}"
|
||||
input_args: "-i {{target}}"
|
||||
output_args: "-o output.txt"
|
||||
log: "Testing structured args with all fields"
|
||||
|
||||
- name: test-with-some-args
|
||||
type: bash
|
||||
command: "echo 'Running second tool'"
|
||||
speed_args: "-t {{threads}}"
|
||||
input_args: "-target {{target}}"
|
||||
log: "Testing structured args with some fields"
|
||||
|
||||
- name: test-without-args
|
||||
type: bash
|
||||
command: "echo 'Hello {{target}}'"
|
||||
log: "Testing without structured args"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
name: test-target-types
|
||||
kind: module
|
||||
description: Test dependencies target_types with domain and url
|
||||
tags: test,dependencies,target-types
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
dependencies:
|
||||
target_types:
|
||||
- domain
|
||||
- url
|
||||
|
||||
steps:
|
||||
- name: echo-ok
|
||||
type: bash
|
||||
command: echo "OK for {{target}}"
|
||||
@@ -0,0 +1,14 @@
|
||||
name: test-timeout-exceed
|
||||
kind: module
|
||||
description: Test step timeout exceeded (1s timeout, 10s sleep)
|
||||
tags: test,timeout,error
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: slow-command
|
||||
type: bash
|
||||
command: sleep 10
|
||||
timeout: 1
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
name: test-timeout
|
||||
kind: module
|
||||
description: Test step timeout handling (success case)
|
||||
tags: test,timeout,quick
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: quick-command
|
||||
type: bash
|
||||
command: echo "fast command for {{target}}"
|
||||
timeout: 5
|
||||
|
||||
- name: another-quick-command
|
||||
type: bash
|
||||
command: echo "another fast command"
|
||||
timeout: 10
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
name: test-trigger-cron
|
||||
kind: module
|
||||
description: Test cron trigger
|
||||
tags: test,trigger,cron
|
||||
|
||||
trigger:
|
||||
- name: every-minute
|
||||
on: cron
|
||||
schedule: "* * * * *"
|
||||
enabled: true
|
||||
- name: manual
|
||||
on: manual
|
||||
enabled: true
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: log-execution
|
||||
type: bash
|
||||
command: echo "Cron triggered at $(date)" >> /tmp/cron-test.log
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
name: test-trigger-event
|
||||
kind: module
|
||||
description: Test event-based trigger
|
||||
tags: test,trigger,event
|
||||
|
||||
trigger:
|
||||
- name: on-new-asset
|
||||
on: event
|
||||
event:
|
||||
topic: "assets.new"
|
||||
filters:
|
||||
- "event.source == 'test'"
|
||||
input:
|
||||
type: event_data
|
||||
field: "url"
|
||||
name: target
|
||||
enabled: true
|
||||
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: process-asset
|
||||
type: bash
|
||||
command: 'echo "New asset discovered: {{target}}"'
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
name: test-trigger-watch
|
||||
kind: module
|
||||
description: Test file watch trigger
|
||||
tags: test,trigger,watch
|
||||
|
||||
trigger:
|
||||
- name: watch-files
|
||||
on: watch
|
||||
path: "/tmp/watch-test"
|
||||
enabled: true
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
|
||||
steps:
|
||||
- name: process-change
|
||||
type: bash
|
||||
command: echo "File changed in watch path"
|
||||
Reference in New Issue
Block a user