feat: add LLM step executor with vision and tool support, event workflow system, and inheritance

- Add LLM executor supporting OpenAI vision, tool calling, embeddings, and structured outputs
- Introduce event emitter/receiver workflows with deduplication and filtering (generate_event functions)
- Add workflow extends/override system enabling inheritance chains and step merge modes
- Update function naming to snake_case across all testdata (fileExists→file_exists, etc.)
- Add comprehensive test fixtures for linter, events, CDN, step dependencies, and extends workflows
This commit is contained in:
j3ssie
2026-01-20 18:23:57 +08:00
parent 9ed02e7eee
commit 1403d20a4d
219 changed files with 25734 additions and 1696 deletions
+99
View File
@@ -0,0 +1,99 @@
# Event Workflow Examples
This folder contains sample workflows demonstrating the event trigger and generation system.
## Workflows
### Emitters (Event Generators)
| File | Description |
|------|-------------|
| `simple-emitter.yaml` | Basic event emission with `generate_event` and `generate_event_from_file` |
| `vuln-emitter.yaml` | Simulates a vulnerability scanner emitting structured finding events |
### Receivers (Event Triggers)
| File | Description |
|------|-------------|
| `simple-receiver.yaml` | Basic event trigger that listens for discovery events |
| `filtered-receiver.yaml` | Advanced filtering with severity checks and jq extraction |
| `dedupe-receiver.yaml` | Event deduplication to prevent duplicate processing |
## Event Functions
### generate_event(workspace, topic, source, data_type, data)
Emit a single event with optional structured data. The workspace parameter identifies the target space for the event.
```yaml
- type: function
function: |
generate_event("{{Workspace}}", "discovery.asset", "my-scanner", "subdomain", "api.example.com")
```
### generate_event_from_file(workspace, topic, source, data_type, file_path)
Emit one event per line from a file. Returns the count of events emitted.
```yaml
- type: function
functions:
- 'generate_event_from_file("{{Workspace}}", "discovery.asset", "my-scanner", "subdomain", "{{Output}}/subdomains.txt")'
```
## Trigger Configuration
### Basic Event Trigger
```yaml
trigger:
- name: on-new-asset
on: event
event:
topic: "discovery.asset"
input:
type: event_data
field: "value"
name: target
enabled: true
```
### Filtered Event Trigger
```yaml
trigger:
- name: on-high-severity
on: event
event:
topic: "scan.finding"
filters:
- "event.data.severity == 'high'"
- "event.data.confirmed == true"
input:
type: function
function: 'jq("{{event.data}}", ".url")'
name: target
enabled: true
```
### Deduplicated Event Trigger
```yaml
trigger:
- name: on-url-dedupe
on: event
event:
topic: "crawler.url"
dedupe_key: "{{event.data.url}}"
dedupe_window: "5m"
input:
type: event_data
field: "value"
name: target
enabled: true
```
## Running Examples
```bash
# Run the emitter to generate events
osmedeus run -m simple-emitter -t example.com
# The receiver will automatically process events if registered with the scheduler
osmedeus run -m simple-receiver -t example.com
```
+56
View File
@@ -0,0 +1,56 @@
# Deduplicated Event Receiver Workflow
# This workflow demonstrates event deduplication to prevent processing duplicates
name: dedupe-receiver
kind: module
description: Workflow with event deduplication to avoid processing duplicates
tags: event,receiver,dedupe,example
trigger:
# Deduplicate events by URL within a 5 minute window
- name: on-new-url-dedupe
on: event
event:
topic: "crawler.url"
filters:
- "event.data_type == 'url'"
dedupe_key: "{{event.data.url}}"
dedupe_window: "5m"
input:
type: event_data
field: "value"
name: target
enabled: true
# Deduplicate by composite key (source + target)
- name: on-asset-composite-dedupe
on: event
event:
topic: "discovery.asset"
dedupe_key: "{{event.source}}-{{event.data.value}}"
dedupe_window: "1h"
input:
type: event_data
field: "value"
name: target
enabled: true
params:
- name: target
required: true
steps:
- name: log-unique
type: function
functions:
- 'print_blue("Processing unique event for: {{target}}")'
- name: process-unique
type: bash
command: |
echo "Unique target: {{target}}"
echo "{{target}}" >> {{Output}}/unique-targets.txt
- name: log-done
type: function
functions:
- 'print_green("Processed unique: {{target}}")'
+55
View File
@@ -0,0 +1,55 @@
# Filtered Event Receiver Workflow
# This workflow demonstrates advanced event filtering and input extraction
name: filtered-receiver
kind: module
description: Workflow with filtered event triggers and complex input extraction
tags: event,receiver,filter,example
trigger:
# Only trigger on high/critical severity findings
- name: on-high-severity-finding
on: event
event:
topic: "scan.finding"
filters:
- "event.data.severity == 'high' || event.data.severity == 'critical'"
- "event.data.confirmed == true"
input:
type: function
function: 'jq("{{event.data}}", ".url")'
name: target
enabled: true
# Trigger on completed scans with results
- name: on-scan-complete
on: event
event:
topic: "scan.complete"
filters:
- "event.data.finding_count > 0"
input:
type: event_data
field: "data.target"
name: target
enabled: true
params:
- name: target
required: true
steps:
- name: log-alert
type: function
functions:
- 'print_red("ALERT: High severity finding for {{target}}")'
- name: notify
type: function
functions:
- 'print_yellow("Sending notification for {{target}}")'
- 'notify_webhook("High severity finding detected: {{target}}")'
- name: log-processed
type: function
functions:
- 'print_green("Alert processed for {{target}}")'
+52
View File
@@ -0,0 +1,52 @@
# Simple Event Emitter Workflow
# This workflow demonstrates how to emit events that can trigger other workflows
name: simple-emitter
kind: module
description: Simple workflow that emits events for discovered assets
tags: event,emitter,example
params:
- name: target
required: true
steps:
- name: log-start
type: function
functions:
- 'print_green("Starting discovery for {{target}}")'
- name: discover-assets
type: bash
command: |
echo "api.{{target}}" > {{Output}}/assets.txt
echo "www.{{target}}" >> {{Output}}/assets.txt
echo "mail.{{target}}" >> {{Output}}/assets.txt
- name: emit-single-event
type: function
description: Emit a single event for a specific asset
functions:
- 'generate_event("{{Workspace}}", "discovery.asset", "simple-emitter", "subdomain", "new.{{target}}")'
- 'print_blue("Emitted single event for new.{{target}}")'
- name: emit-events-from-file
type: function
description: Emit events for each line in a file
functions:
- 'set_var("count", generate_event_from_file("{{Workspace}}", "discovery.asset", "simple-emitter", "subdomain", "{{Output}}/assets.txt"))'
- 'print_green("Emitted " + get_var("count") + " events from file")'
- name: emit-structured-event
type: function
description: Emit an event with structured JSON data
function: |
generate_event("{{Workspace}}", "discovery.complete", "simple-emitter", "summary", {
target: "{{target}}",
asset_count: 3,
status: "completed"
})
- name: log-complete
type: function
functions:
- 'print_green("Discovery complete for {{target}}")'
+45
View File
@@ -0,0 +1,45 @@
# Simple Event Receiver Workflow
# This workflow demonstrates how to receive events via triggers
name: simple-receiver
kind: module
description: Simple workflow that receives and processes events
tags: event,receiver,trigger,example
trigger:
# Trigger on new asset discovery events
- name: on-new-asset
on: event
event:
topic: "discovery.asset"
filters:
- "event.source == 'simple-emitter'"
- "event.data_type == 'subdomain'"
input:
type: event_data
field: "value"
name: target
enabled: true
- name: manual-trigger
on: manual
enabled: false
params:
- name: target
required: true
steps:
- name: log-received
type: function
functions:
- 'print_blue("Received event for target: {{target}}")'
- name: process-asset
type: bash
command: |
echo "Processing asset: {{target}}"
echo "{{target}}" >> {{Output}}/processed-assets.txt
- name: log-complete
type: function
functions:
- 'print_green("Processed: {{target}}")'
+83
View File
@@ -0,0 +1,83 @@
# Vulnerability Event Emitter Workflow
# This workflow simulates a scanner that emits vulnerability findings as events
name: vuln-emitter
kind: module
description: Simulates a vulnerability scanner emitting finding events
tags: event,emitter,vulnerability,example
params:
- name: target
required: true
steps:
- name: log-start
type: function
functions:
- 'print_blue("Starting vulnerability scan for {{target}}")'
- name: simulate-scan
type: bash
command: |
echo "Scanning {{target}} for vulnerabilities..."
sleep 1
- name: emit-critical-finding
type: function
description: Emit a critical severity finding
function: |
generate_event("{{Workspace}}", "scan.finding", "vuln-scanner", "vulnerability", {
url: "https://{{target}}/admin",
severity: "critical",
template_id: "exposed-admin-panel",
confirmed: true,
matched_at: "/admin",
description: "Admin panel exposed without authentication"
})
post_run:
- 'print_red("CRITICAL: Exposed admin panel found")'
- name: emit-high-finding
type: function
description: Emit a high severity finding
function: |
generate_event("{{Workspace}}", "scan.finding", "vuln-scanner", "vulnerability", {
url: "https://{{target}}/api/v1/users",
severity: "high",
template_id: "api-info-disclosure",
confirmed: true,
matched_at: "/api/v1/users",
description: "API endpoint leaking user information"
})
post_run:
- 'print_yellow("HIGH: API info disclosure found")'
- name: emit-low-finding
type: function
description: Emit a low severity finding (should not trigger filtered-receiver)
function: |
generate_event("{{Workspace}}", "scan.finding", "vuln-scanner", "vulnerability", {
url: "https://{{target}}/robots.txt",
severity: "low",
template_id: "robots-txt-exposed",
confirmed: true,
matched_at: "/robots.txt",
description: "Robots.txt file accessible"
})
- name: emit-scan-complete
type: function
description: Emit scan completion event with summary
function: |
generate_event("{{Workspace}}", "scan.complete", "vuln-scanner", "summary", {
target: "{{target}}",
finding_count: 3,
critical_count: 1,
high_count: 1,
low_count: 1,
duration_seconds: 5
})
- name: log-complete
type: function
functions:
- 'print_green("Vulnerability scan complete for {{target}}")'
+17
View File
@@ -0,0 +1,17 @@
kind: flow
name: base-flow
description: Base flow for testing inheritance
params:
- name: target
type: string
required: true
modules:
- name: module-a
path: base-module
- name: module-b
path: base-module
depends_on:
- module-a
+33
View File
@@ -0,0 +1,33 @@
kind: module
name: base-module
description: Base module for testing inheritance
params:
- name: target
type: string
required: true
- name: threads
type: string
default: "10"
- name: timeout
type: string
default: "3600"
dependencies:
commands:
- echo
files:
- /bin/bash
steps:
- name: step-one
type: bash
command: echo "Step one for {{target}}"
- name: step-two
type: bash
command: echo "Step two with threads={{threads}}"
- name: step-three
type: bash
command: echo "Step three"
+12
View File
@@ -0,0 +1,12 @@
kind: module
name: chain-a
description: Chain test - root
params:
- name: param-a
default: "from-a"
steps:
- name: step-from-a
type: bash
command: echo "from chain-a"
+15
View File
@@ -0,0 +1,15 @@
kind: module
name: chain-b
description: Chain test - middle
extends: chain-a
override:
params:
param-a:
default: "overridden-by-b"
steps:
mode: append
steps:
- name: step-from-b
type: bash
command: echo "from chain-b"
+15
View File
@@ -0,0 +1,15 @@
kind: module
name: chain-c
description: Chain test - leaf
extends: chain-b
override:
params:
param-a:
default: "final-from-c"
steps:
mode: append
steps:
- name: step-from-c
type: bash
command: echo "from chain-c"
+13
View File
@@ -0,0 +1,13 @@
kind: flow
name: child-flow
description: Child flow that extends base flow
extends: base-flow
override:
modules:
mode: append
modules:
- name: module-c
path: base-module
depends_on:
- module-b
+18
View File
@@ -0,0 +1,18 @@
kind: module
name: child-merge-steps
description: Child that merges steps (replace specific, remove some, add new)
extends: base-module
override:
steps:
mode: merge
steps:
- name: step-new
type: bash
command: echo "New step added"
remove:
- step-three
replace:
- name: step-two
type: bash
command: echo "Step two replaced with new command"
@@ -0,0 +1,15 @@
kind: module
name: child-override-steps
description: Child that appends steps
extends: base-module
override:
params:
threads:
default: "20"
steps:
mode: append
steps:
- name: step-four
type: bash
command: echo "Step four added by child"
@@ -0,0 +1,12 @@
kind: module
name: child-prepend-steps
description: Child that prepends steps
extends: base-module
override:
steps:
mode: prepend
steps:
- name: step-zero
type: bash
command: echo "Step zero prepended by child"
@@ -0,0 +1,16 @@
kind: module
name: child-replace-steps
description: Child that replaces all steps
extends: base-module
override:
steps:
mode: replace
steps:
- name: new-step-one
type: bash
command: echo "Completely new step one"
- name: new-step-two
type: bash
command: echo "Completely new step two"
+11
View File
@@ -0,0 +1,11 @@
kind: module
name: child-simple
description: Simple child that only overrides params
extends: base-module
override:
params:
threads:
default: "5"
timeout:
default: "1800"
+9
View File
@@ -0,0 +1,9 @@
kind: module
name: circular-a
description: Circular dependency test A
extends: circular-b
steps:
- name: step-a
type: bash
command: echo "A"
+9
View File
@@ -0,0 +1,9 @@
kind: module
name: circular-b
description: Circular dependency test B
extends: circular-a
steps:
- name: step-b
type: bash
command: echo "B"
+8
View File
@@ -0,0 +1,8 @@
kind: flow
name: kind-mismatch
description: This flow tries to extend a module (should fail)
extends: base-module
modules:
- name: module-x
path: base-module
+59
View File
@@ -0,0 +1,59 @@
name: all-errors
kind: module
description: A workflow with all types of lint errors for testing
tags: test,linter,errors
params:
- name: target
required: true
steps:
# Error: undefined-variable - Taget is misspelled
- name: undefined-var-step
type: bash
command: echo "{{Taget}}"
# Error: duplicate-step-name
- name: duplicate-step
type: bash
command: echo "first"
- name: duplicate-step
type: bash
command: echo "second duplicate"
# Error: empty-step - bash step with no command
- name: empty-bash-step
type: bash
# Error: invalid-goto - references non-existent step
- name: decision-step
type: bash
command: echo "decision"
decision:
switch: "{{target}}"
cases:
"skip":
goto: nonexistent-step
default:
goto: _end
# Error: invalid-depends-on - references non-existent step
- name: invalid-dep-step
type: bash
command: echo "depends"
depends_on:
- step-that-does-not-exist
# Error: circular-dependency - creates a cycle
- name: cycle-step-a
type: bash
command: echo "a"
depends_on:
- cycle-step-b
- name: cycle-step-b
type: bash
command: echo "b"
depends_on:
- cycle-step-a
+33
View File
@@ -0,0 +1,33 @@
name: valid-workflow
kind: module
description: A valid workflow with no lint issues
tags: test,linter
params:
- name: target
required: true
- name: threads
default: 10
steps:
- name: step-one
type: bash
command: echo "Target is {{target}}"
timeout: 5m
exports:
output_one: "output"
- name: step-two
type: bash
command: echo "Output is {{output_one}}"
depends_on:
- step-one
timeout: 5m
exports:
final_output: "output"
- name: step-three
type: function
function: 'log_info("Final: {{final_output}}")'
depends_on:
- step-two
+22
View File
@@ -0,0 +1,22 @@
name: warnings-only
kind: module
description: A workflow with only warning-level issues
tags: test,linter,warnings
params:
- name: target
required: true
steps:
# Warning: unused-variable - exported but never used
- name: step-with-unused-export
type: bash
command: echo "Hello {{target}}"
exports:
unused_output: "output"
another_unused: "also_unused"
- name: final-step
type: bash
command: echo "done"
timeout: 5m
+1 -1
View File
@@ -24,7 +24,7 @@ steps:
- name: verify-exports-and-params
type: bash
pre_condition: 'fileExists("{{anotherParam3as1}}")'
pre_condition: 'file_exists("{{anotherParam3as1}}")'
command: |
echo "Module 2: paramFromFlowFile={{paramFromFlowFile}}"
echo "Module 2: module1_completed={{module1_completed}}"
@@ -21,7 +21,7 @@ steps:
# Foreach with templated input path
- name: foreach-with-templates
type: foreach
pre_condition: 'fileExists("{{inputFile}}")'
pre_condition: 'file_exists("{{inputFile}}")'
input: "{{inputFile}}"
variable: item
threads: 2
@@ -34,7 +34,7 @@ steps:
# Verify results
- name: verify-foreach
type: bash
pre_condition: 'fileExists("{{outputDir}}/item1.txt")'
pre_condition: 'file_exists("{{outputDir}}/item1.txt")'
command: |
echo "=== Foreach Verification ==="
echo "Foreach completed for {{Target}}"
@@ -36,7 +36,7 @@ steps:
- name: verify-parallel
type: bash
pre_condition: 'fileExists("{{parallelOutput}}/bash1.txt")'
pre_condition: 'file_exists("{{parallelOutput}}/bash1.txt")'
command: |
echo "=== Parallel Steps Verification ==="
echo "parallel_dir={{parallel_dir}}"
@@ -26,7 +26,7 @@ steps:
# 2. Function step with templated function calls
- name: function-with-templates
type: function
pre_condition: 'fileExists("{{bash_output_path}}")'
pre_condition: 'file_exists("{{bash_output_path}}")'
function: 'log_info("Processing {{Target}} with path {{customPath}}")'
exports:
function_result: "processed-{{Target}}"
@@ -50,7 +50,7 @@ steps:
# 5. Final verification step
- name: verify-all-templates
type: bash
pre_condition: 'fileExists("{{customPath}}")'
pre_condition: 'file_exists("{{customPath}}")'
command: |
echo "=== Template Rendering Verification ==="
echo "bash_output_path={{bash_output_path}}"
+3 -3
View File
@@ -52,7 +52,7 @@ steps:
## String Functions Demo
```osm-func
"Uppercase target: " + toUpperCase("{{Target}}")
"Uppercase target: " + to_upper_case("{{Target}}")
```
```osm-func
@@ -73,7 +73,7 @@ steps:
uuid()
``` |
| Random String | ```osm-func
randomString(8)
random_string(8)
``` |
| Contains 'example' | ```osm-func
contains("{{Target}}", "example")
@@ -100,7 +100,7 @@ steps:
# Step 4: Verify the report was created
- name: verify-report
type: function
function: 'fileExists("{{Output}}/security-report.md")'
function: 'file_exists("{{Output}}/security-report.md")'
# Step 5: Display report path
- name: show-report-path
+129
View File
@@ -0,0 +1,129 @@
name: test-cdn-functions
kind: module
description: Test CDN/Storage utility functions
tags: test,cdn,storage,functions
params:
- name: target
required: true
steps:
# Test cdn_list with empty prefix (should return empty array when no storage configured)
- name: test-cdn-list-empty
type: function
function: cdn_list("")
exports:
list_result: "output"
# Test cdn_list with prefix
- name: test-cdn-list-prefix
type: function
function: cdn_list("scans/")
exports:
list_prefix_result: "output"
# Test cdn_stat with non-existent file (should return null)
- name: test-cdn-stat-missing
type: function
function: cdn_stat("nonexistent/file.txt")
exports:
stat_result: "output"
# Test cdn_exists with non-existent file
- name: test-cdn-exists-missing
type: function
function: cdn_exists("nonexistent/file.txt")
exports:
exists_result: "output"
# Test cdn_get_presigned_url with empty path (should return empty string)
- name: test-cdn-presigned-empty
type: function
function: cdn_get_presigned_url("")
exports:
presigned_empty: "output"
# Test cdn_get_presigned_url with path and expiry
- name: test-cdn-presigned-with-expiry
type: function
function: cdn_get_presigned_url("test/file.txt", 60)
exports:
presigned_result: "output"
# Test cdn_sync_upload with empty directory (should handle gracefully)
- name: test-cdn-sync-upload-empty
type: function
function: cdn_sync_upload("", "remote/prefix/")
exports:
sync_upload_empty: "output"
# Test cdn_sync_download with empty local directory (should handle gracefully)
- name: test-cdn-sync-download-empty
type: function
function: cdn_sync_download("remote/prefix/", "")
exports:
sync_download_empty: "output"
# Test cdn_upload with empty paths
- name: test-cdn-upload-empty
type: function
function: cdn_upload("", "")
exports:
upload_empty: "output"
# Test cdn_download with empty paths
- name: test-cdn-download-empty
type: function
function: cdn_download("", "")
exports:
download_empty: "output"
# Test cdn_delete with empty path
- name: test-cdn-delete-empty
type: function
function: cdn_delete("")
exports:
delete_empty: "output"
# Create local test directory for sync testing
- name: create-test-dir
type: bash
command: |
mkdir -p /tmp/cdn-test-{{target}}
echo "file1 content" > /tmp/cdn-test-{{target}}/file1.txt
echo "file2 content" > /tmp/cdn-test-{{target}}/file2.txt
mkdir -p /tmp/cdn-test-{{target}}/subdir
echo "subfile content" > /tmp/cdn-test-{{target}}/subdir/subfile.txt
# Test cdn_sync_upload with actual directory (will fail if no storage, but should not crash)
- name: test-cdn-sync-upload-dir
type: function
function: cdn_sync_upload("/tmp/cdn-test-{{target}}", "test-sync/{{target}}/")
exports:
sync_upload_result: "output"
# Test cdn_sync_download to actual directory
- name: test-cdn-sync-download-dir
type: function
function: cdn_sync_download("test-sync/{{target}}/", "/tmp/cdn-download-{{target}}")
exports:
sync_download_result: "output"
# Cleanup
- name: cleanup
type: bash
command: |
rm -rf /tmp/cdn-test-{{target}}
rm -rf /tmp/cdn-download-{{target}}
# Log results summary
- name: log-results
type: function
function: |
log_info("CDN Function Test Results:");
log_info(" cdn_list empty: " + JSON.stringify({{list_result}}));
log_info(" cdn_stat missing: " + ({{stat_result}} === null ? "null" : JSON.stringify({{stat_result}})));
log_info(" cdn_exists missing: " + {{exists_result}});
log_info(" cdn_sync_upload result: " + JSON.stringify({{sync_upload_result}}));
log_info(" cdn_sync_download result: " + JSON.stringify({{sync_download_result}}));
true
+4 -4
View File
@@ -87,7 +87,7 @@ steps:
- name: verify-ports-file
type: function
log: "Verifying ports file exists"
function: fileExists("{{ports_file}}")
function: file_exists("{{ports_file}}")
exports:
ports_verified: "output"
@@ -150,7 +150,7 @@ steps:
- name: read-subdomain-results
type: function
log: "Reading subdomain scan results"
function: readFile("{{output_dir}}/subdomain-results.txt")
function: read_file("{{output_dir}}/subdomain-results.txt")
exports:
scan_results: "output"
@@ -200,8 +200,8 @@ steps:
type: function
log: "Getting file statistics"
parallel_functions:
- fileLength("{{output_dir}}/report.txt")
- fileExists("{{output_dir}}/all-ports.txt")
- file_length("{{output_dir}}/report.txt")
- file_exists("{{output_dir}}/all-ports.txt")
- trim(" {{target}} ")
exports:
file_stats: "output"
+55
View File
@@ -0,0 +1,55 @@
name: test-debounce-dedupe
kind: module
desc: Test workflow for debounce and deduplication features
params:
- name: output
default: "/tmp/debounce-dedupe-test"
triggers:
# Watch trigger with debounce - waits 500ms after last change before triggering
- name: on-asset-change
on: watch
path: "{{output}}/assets.txt"
debounce: 500ms
enabled: true
input:
type: file
path: "{{output}}/assets.txt"
# Event trigger with deduplication - ignores duplicate events within 10s window
- name: on-new-asset
on: event
event:
topic: "assets.new"
dedupe_key: "{{event.source}}-{{event.data.url}}"
dedupe_window: 10s
enabled: true
input:
type: event_data
field: url
name: target
# Event trigger with filters and deduplication
- name: on-critical-vuln
on: event
event:
topic: "vulnerabilities.new"
filters:
- "event.data.severity == 'critical'"
dedupe_key: "{{event.data.template}}-{{event.data.host}}"
dedupe_window: 5m
enabled: true
input:
type: event_data
field: host
name: target
steps:
- name: log-event
type: function
call: log_info("Trigger fired: {{_step_name}}")
- name: process-target
type: bash
run: echo "Processing target: {{target}}"
+1 -1
View File
@@ -169,7 +169,7 @@ steps:
- name: check-alive-hosts
type: function
log: "Checking alive hosts count"
function: fileLength("{{Output}}/enumeration/httpx.json")
function: file_length("{{Output}}/enumeration/httpx.json")
exports:
alive_count: "output"
decision:
+22
View File
@@ -0,0 +1,22 @@
name: test-event-chain-stage1
kind: module
description: Stage 1 of event chain - discovery
tags: test,event,chain
params:
- name: target
required: true
steps:
- name: discover
type: bash
command: |
echo "Discovering subdomains for {{target}}..."
echo "api.{{target}}" > {{Output}}/discovered.txt
echo "www.{{target}}" >> {{Output}}/discovered.txt
- name: emit-discoveries
type: function
function: |
generate_event_from_file("stage1.complete", "chain-stage1", "subdomain", "{{Output}}/discovered.txt")
log_info("Stage 1 complete - emitted discovery events")
+34
View File
@@ -0,0 +1,34 @@
name: test-event-chain-stage2
kind: module
description: Stage 2 of event chain - probing (triggered by stage1)
tags: test,event,chain
trigger:
- name: on-stage1-complete
on: event
event:
topic: "stage1.complete"
filters:
- "event.source == 'chain-stage1'"
input:
type: event_data
field: "value"
name: target
enabled: true
params:
- name: target
required: true
steps:
- name: probe
type: bash
command: |
echo "Probing {{target}}..."
echo "{{target}} is live" > {{Output}}/probed.txt
- name: emit-probe-complete
type: function
function: |
generate_event("{{Workspace}}", "stage2.complete", "chain-stage2", "probed-host", "{{target}}")
log_info("Stage 2 complete - probed {{target}}")
+43
View File
@@ -0,0 +1,43 @@
name: test-event-emitter
kind: module
description: Test workflow that emits events
tags: test,event,emitter
params:
- name: target
required: true
steps:
- name: generate-subdomains
type: bash
command: |
echo "api.{{target}}" > {{Output}}/subdomains.txt
echo "www.{{target}}" >> {{Output}}/subdomains.txt
echo "admin.{{target}}" >> {{Output}}/subdomains.txt
- name: emit-single-event
type: function
function: |
generate_event("{{Workspace}}", "assets.new", "test-emitter", "subdomain", "single.{{target}}")
- name: emit-events-from-file
type: function
function: |
generate_event_from_file("{{Workspace}}", "assets.new", "test-emitter", "subdomain", "{{Output}}/subdomains.txt")
exports:
emitted_count: "{{result}}"
- name: emit-complex-event
type: function
function: |
generate_event("{{Workspace}}", "vulnerabilities.new", "test-scanner", "finding", {
url: "https://{{target}}/admin",
severity: "high",
template: "exposed-admin-panel",
matched: "/admin"
})
- name: log-result
type: function
function: |
log_info("Emitted events for {{target}}, file event count: " + "{{emitted_count}}")
+50
View File
@@ -0,0 +1,50 @@
name: test-event-receiver
kind: module
description: Test workflow that receives events via trigger
tags: test,event,receiver,trigger
trigger:
- name: on-new-subdomain
on: event
event:
topic: "assets.new"
filters:
- "event.source == 'test-emitter'"
- "event.data_type == 'subdomain'"
input:
type: event_data
field: "value"
name: target
enabled: true
- name: on-vulnerability
on: event
event:
topic: "vulnerabilities.new"
filters:
- "event.source == 'test-scanner'"
- "event.data.severity == 'high' || event.data.severity == 'critical'"
input:
type: function
function: 'jq("{{event.data}}", ".url")'
name: target
enabled: true
params:
- name: target
required: true
steps:
- name: log-received
type: function
function: |
log_info("Event received for target: {{target}}")
- name: process-target
type: bash
command: echo "Processing event target: {{target}}" > {{Output}}/processed.txt
- name: mark-complete
type: function
function: |
appendFile("{{Output}}/events.log", "Processed: {{target}}\n")
+6 -6
View File
@@ -1,6 +1,6 @@
name: test-exports-functions
kind: module
description: Test exports with utility functions like fileLength, contains, fileExists, replace
description: Test exports with utility functions like file_length, contains, file_exists, replace
tags: test,exports,functions,utility
params:
@@ -29,13 +29,13 @@ steps:
type: bash
command: echo "Checking file length"
exports:
line_count: "fileLength('{{outputFile}}')"
line_count: "file_length('{{outputFile}}')"
- name: test-trim
type: bash
command: echo "Checking trim"
exports:
trimmed_output: "trim(readFile('{{trimTestFile}}'))"
trimmed_output: "trim(read_file('{{trimTestFile}}'))"
- name: test-contains-success
type: bash
@@ -53,19 +53,19 @@ steps:
type: bash
command: echo "Checking existence"
exports:
file_exists: "fileExists('{{outputFile}}')"
file_exists: "file_exists('{{outputFile}}')"
- name: test-fileexists-false
type: bash
command: echo "Checking nonexistent"
exports:
missing_file: "fileExists('{{Output}}/nonexistent.txt')"
missing_file: "file_exists('{{Output}}/nonexistent.txt')"
- name: test-replace
type: bash
command: echo "Checking replace"
exports:
replaced_output: "replace(readFile('{{replaceTestFile}}'), ',', '-')"
replaced_output: "replace(read_file('{{replaceTestFile}}'), ',', '-')"
- name: final-summary
type: bash
+14
View File
@@ -0,0 +1,14 @@
name: test-extends-aggressive
kind: module
description: Aggressive variant - extends base with higher resource usage
tags: test,extends,aggressive
extends: test-extends-base
override:
params:
threads:
default: "50"
rate_limit:
default: "500"
verbose:
default: "true"
+26
View File
@@ -0,0 +1,26 @@
name: test-extends-base
kind: module
description: Base workflow for testing extends functionality
tags: test,extends,base
params:
- name: target
required: true
- name: threads
type: string
default: "10"
- name: rate_limit
type: string
default: "100"
- name: verbose
type: string
default: "false"
steps:
- name: show-config
type: bash
command: echo "Target={{target}} Threads={{threads}} RateLimit={{rate_limit}} Verbose={{verbose}}"
- name: scan-step
type: bash
command: echo "Running scan with {{threads}} threads at rate {{rate_limit}}"
+12
View File
@@ -0,0 +1,12 @@
name: test-extends-fast
kind: module
description: Fast variant - extends base with lower resource usage
tags: test,extends,fast
extends: test-extends-base
override:
params:
threads:
default: "5"
rate_limit:
default: "50"
+2 -2
View File
@@ -17,13 +17,13 @@ steps:
- name: check-file
type: function
function: fileExists("/tmp/test-{{target}}.txt")
function: file_exists("/tmp/test-{{target}}.txt")
exports:
exists: "output"
- name: read-file
type: function
function: readFile("/tmp/test-{{target}}.txt")
function: read_file("/tmp/test-{{target}}.txt")
exports:
content: "output"
+19
View File
@@ -0,0 +1,19 @@
name: test-multi-type-variable
kind: module
description: Test comma-separated types in dependencies.variables
tags: test,dependencies,multi-type
params:
- name: target
required: true
dependencies:
variables:
- name: Target
type: domain,url
required: true
steps:
- name: echo-ok
type: bash
command: echo "OK for {{target}}"
+24
View File
@@ -0,0 +1,24 @@
name: test-notification
kind: module
description: Test notification triggers
tags: test,notification
params:
- name: target
required: true
steps:
- name: trigger-step-started
type: function
function: |
send_webhook_event("step_started", {step: "trigger-step-started", target: "{{target}}"})
- name: echo-test
type: bash
command: echo "Testing notifications for {{target}}"
- name: trigger-step-complete
type: function
function: |
send_webhook_event("step_complete", {step: "echo-test", target: "{{target}}"})
notify_webhook("Workflow completed for {{target}}")
+1 -1
View File
@@ -17,7 +17,7 @@ steps:
parallel_functions:
- trim(" hello ")
- contains("hello world", "world")
- fileExists("/tmp/parallel-func-test.txt")
- file_exists("/tmp/parallel-func-test.txt")
exports:
func_results: "output"
+1 -1
View File
@@ -27,7 +27,7 @@ steps:
- name: verify-files
type: function
function: fileExists("/tmp/parallel-steps-test/sub1.txt")
function: file_exists("/tmp/parallel-steps-test/sub1.txt")
exports:
file_exists: "output"
+27
View File
@@ -0,0 +1,27 @@
name: test-step-dependencies
kind: module
description: Test DAG-style step dependencies
params:
- name: target
required: true
steps:
- name: step-a
type: bash
command: echo "A completed"
- name: step-b
type: bash
command: echo "B completed"
depends_on: [step-a]
- name: step-c
type: bash
command: echo "C completed"
depends_on: [step-a]
- name: step-d
type: bash
command: echo "D completed"
depends_on: [step-b, step-c]