mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-17 21:25:49 +02:00
- Add is_file, is_dir, is_git functions for type validation - Add is_compress function to detect compressed file extensions - Implement pure Go wget with parallel segmented downloads (>1MB files) - Add git_clone_subfolder with ZIP fallback for GitHub repos - Add helper utilities: ZIP extraction, directory/file copying - Add SkipWorkspace option to skip output dirs in empty-target mode - Fix workflow preference loading order for --module-url flag - Add 20+ comprehensive tests for all new functions
Osmedeus Library SDK
Use Osmedeus as a Go library to programmatically execute workflows and evaluate utility functions.
Installation
go get github.com/j3ssie/osmedeus/v5/lib
Quick Start
package main
import (
"fmt"
"log"
"github.com/j3ssie/osmedeus/v5/lib"
)
func main() {
// Define a simple workflow
workflowYAML := `
name: simple-scan
kind: module
steps:
- name: echo-target
type: bash
command: echo "Scanning {{target}}"
`
// Run the workflow
result, err := lib.Run("example.com", workflowYAML, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s\n", result.Status)
fmt.Printf("Duration: %v\n", result.Duration)
}
API Reference
Workflow Execution
Run(target, workflowYAML, opts) (*RunResult, error)
Execute a module workflow against a target.
result, err := lib.Run("example.com", workflowYAML, nil)
RunWithContext(ctx, target, workflowYAML, opts) (*RunResult, error)
Execute with context support for cancellation and timeout.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
result, err := lib.RunWithContext(ctx, "example.com", workflowYAML, nil)
RunModule(target, workflowYAML) (*RunResult, error)
Convenience wrapper with default options.
result, err := lib.RunModule("example.com", workflowYAML)
RunModuleWithParams(target, workflowYAML, params) (*RunResult, error)
Convenience wrapper with custom parameters.
result, err := lib.RunModuleWithParams("example.com", workflowYAML, map[string]string{
"threads": "20",
"timeout": "30",
})
Function Evaluation
Eval(expression, opts) (interface{}, error)
Evaluate a JavaScript expression with optional context.
// Simple expression
result, err := lib.Eval(`1 + 1`, nil)
// With context variables
result, err := lib.Eval(`trim(input)`, &lib.EvalOptions{
Context: map[string]interface{}{"input": " hello "},
})
EvalCondition(condition, opts) (bool, error)
Evaluate a boolean condition.
ok, err := lib.EvalCondition(`len(items) > 0`, &lib.EvalOptions{
Context: map[string]interface{}{"items": []string{"a", "b"}},
})
EvalFunction(expression) (interface{}, error)
Convenience wrapper for Eval without options.
result, err := lib.EvalFunction(`uuid()`)
EvalFunctionWithContext(expression, ctx) (interface{}, error)
Convenience wrapper for Eval with context.
result, err := lib.EvalFunctionWithContext(`split(text, ",")`, map[string]interface{}{
"text": "a,b,c",
})
Workflow Validation
ParseWorkflow(workflowYAML) (*core.Workflow, error)
Parse a workflow YAML string without executing.
workflow, err := lib.ParseWorkflow(workflowYAML)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Workflow: %s (%s)\n", workflow.Name, workflow.Kind)
ValidateWorkflow(workflowYAML) error
Parse and validate a workflow. Returns nil if valid.
if err := lib.ValidateWorkflow(workflowYAML); err != nil {
fmt.Printf("Invalid workflow: %v\n", err)
}
Configuration Options
RunOptions
opts := &lib.RunOptions{
// Parameters passed to the workflow
Params: map[string]string{
"threads": "20",
"custom": "value",
},
// Scan aggressiveness: "aggressive", "default", or "gently"
Tactic: "default",
// Show commands without executing
DryRun: false,
// Enable detailed output (shows step stdout)
Verbose: false,
// Suppress step output (default: true for library mode)
Silent: true,
// Custom configuration (nil uses DefaultConfig)
Config: nil,
// Override base folder path
BaseFolder: "",
// Override workspaces output directory
WorkspacesPath: "",
// Disable writing workflow state files
DisableWorkflowState: false,
// Skip database operations (default: true for library mode)
DisableDatabase: true,
}
EvalOptions
opts := &lib.EvalOptions{
// Variables accessible in the expression
Context: map[string]interface{}{
"input": "value",
"count": 42,
},
// Convenience: sets ctx["target"]
Target: "example.com",
}
Result Types
RunResult
type RunResult struct {
WorkflowName string
RunID string
Target string
Status string // "completed", "failed", "cancelled", "skipped"
StartTime time.Time
EndTime time.Time
Duration time.Duration
Steps []*StepResult
Exports map[string]interface{}
Artifacts []string
Error error
OutputPath string
Message string
}
// Helper methods
result.IsSuccess() // true if completed
result.IsFailed() // true if failed
result.IsCancelled() // true if cancelled
result.IsSkipped() // true if skipped
result.GetExport("name") // get exported variable
result.GetExportString("x") // get as string
result.GetExportBool("x") // get as bool
result.SuccessfulSteps() // count of successful steps
result.FailedSteps() // count of failed steps
result.SkippedSteps() // count of skipped steps
StepResult
type StepResult struct {
Name string
Type string
Status string // "success", "failed", "skipped"
Output string
Duration time.Duration
Error error
Exports map[string]interface{}
}
// Helper methods
step.IsSuccess()
step.IsFailed()
step.IsSkipped()
step.GetExport("name")
Error Handling
The library provides typed errors for different failure scenarios:
result, err := lib.Run(target, yaml, nil)
if err != nil {
switch {
case errors.Is(err, lib.ErrEmptyTarget):
fmt.Println("Target cannot be empty")
case errors.Is(err, lib.ErrEmptyWorkflow):
fmt.Println("Workflow YAML cannot be empty")
case errors.Is(err, lib.ErrNotModule):
fmt.Println("Only module workflows are supported")
case lib.IsParseError(err):
fmt.Println("YAML parsing failed:", err)
case lib.IsValidationError(err):
fmt.Println("Workflow validation failed:", err)
case lib.IsExecutionError(err):
fmt.Println("Execution failed:", err)
default:
fmt.Println("Error:", err)
}
}
Error Types
| Error | Description |
|---|---|
ErrEmptyTarget |
Target cannot be empty |
ErrEmptyWorkflow |
Workflow content cannot be empty |
ErrEmptyExpression |
Expression cannot be empty |
ErrNotModule |
Workflow must be kind 'module' |
ParseError |
YAML parsing failed |
ValidationError |
Workflow validation failed |
ExecutionError |
Step execution failed |
Available Functions
The Eval functions have access to all Osmedeus utility functions:
File Operations
| Function | Description |
|---|---|
fileExists(path) |
Check if file exists |
fileLength(path) |
Get file line count |
dirLength(path) |
Get directory entry count |
readFile(path) |
Read file contents |
readLines(path, n) |
Read first n lines |
removeFile(path) |
Delete a file |
createFolder(path) |
Create directory |
appendFile(path, data) |
Append to file |
glob(pattern) |
Find files matching pattern |
String Operations
| Function | Description |
|---|---|
trim(s) |
Remove whitespace |
split(s, sep) |
Split string |
join(arr, sep) |
Join array |
replace(s, old, new) |
Replace substring |
contains(s, substr) |
Check substring |
startsWith(s, prefix) |
Check prefix |
endsWith(s, suffix) |
Check suffix |
toLowerCase(s) |
Convert to lowercase |
toUpperCase(s) |
Convert to uppercase |
match(s, pattern) |
Regex match |
regexExtract(s, pattern) |
Extract regex groups |
Type Conversion
| Function | Description |
|---|---|
parseInt(s) |
Parse integer |
parseFloat(s) |
Parse float |
toString(v) |
Convert to string |
toBoolean(v) |
Convert to boolean |
Utilities
| Function | Description |
|---|---|
len(v) |
Get length |
isEmpty(v) |
Check if empty |
isNotEmpty(v) |
Check if not empty |
uuid() |
Generate UUID |
randomString(n) |
Generate random string |
base64Encode(s) |
Base64 encode |
base64Decode(s) |
Base64 decode |
Logging
| Function | Description |
|---|---|
log_info(msg) |
Log info message |
log_warn(msg) |
Log warning |
log_error(msg) |
Log error |
log_debug(msg) |
Log debug |
Examples
Run a Reconnaissance Workflow
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/j3ssie/osmedeus/v5/lib"
)
func main() {
workflow := `
name: recon
kind: module
params:
- name: threads
default: "10"
steps:
- name: subdomain-enum
type: bash
command: subfinder -d {{target}} -t {{threads}} -o {{Output}}/subdomains.txt
exports:
subdomains_file: "{{Output}}/subdomains.txt"
- name: check-results
type: function
function: |
log_info("Found " + fileLength("{{subdomains_file}}") + " subdomains")
`
// Run with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
result, err := lib.RunWithContext(ctx, "example.com", workflow, &lib.RunOptions{
Params: map[string]string{"threads": "20"},
Tactic: "aggressive",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s\n", result.Status)
fmt.Printf("Duration: %v\n", result.Duration)
fmt.Printf("Output: %s\n", result.OutputPath)
// Check exports
if file, ok := result.GetExportString("subdomains_file"); ok {
fmt.Printf("Subdomains file: %s\n", file)
}
}
Validate Workflow Before Execution
package main
import (
"fmt"
"log"
"github.com/j3ssie/osmedeus/v5/lib"
)
func main() {
workflow := `
name: my-workflow
kind: module
steps:
- name: step1
type: bash
command: echo "hello"
`
// Validate first
if err := lib.ValidateWorkflow(workflow); err != nil {
log.Fatalf("Invalid workflow: %v", err)
}
// Parse to inspect
w, _ := lib.ParseWorkflow(workflow)
fmt.Printf("Workflow: %s\n", w.Name)
fmt.Printf("Steps: %d\n", len(w.Steps))
// Then execute
result, err := lib.Run("target.com", workflow, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Result: %s\n", result.Status)
}
Use Functions for File Processing
package main
import (
"fmt"
"log"
"github.com/j3ssie/osmedeus/v5/lib"
)
func main() {
// Check if file exists
exists, _ := lib.Eval(`fileExists("/tmp/results.txt")`, nil)
fmt.Printf("File exists: %v\n", exists)
// Read and process file
if exists.(bool) {
lineCount, _ := lib.Eval(`fileLength("/tmp/results.txt")`, nil)
fmt.Printf("Line count: %v\n", lineCount)
}
// String processing
result, _ := lib.Eval(`split(trim(input), ",")`, &lib.EvalOptions{
Context: map[string]interface{}{
"input": " a, b, c ",
},
})
fmt.Printf("Split result: %v\n", result)
// Conditional logic
hasResults, err := lib.EvalCondition(`fileExists(path) && fileLength(path) > 0`, &lib.EvalOptions{
Context: map[string]interface{}{
"path": "/tmp/results.txt",
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Has results: %v\n", hasResults)
}
Custom Configuration
package main
import (
"log"
"github.com/j3ssie/osmedeus/v5/internal/config"
"github.com/j3ssie/osmedeus/v5/lib"
)
func main() {
// Load custom config
cfg, err := config.Load("/path/to/osmedeus-base")
if err != nil {
// Fall back to default
cfg = config.DefaultConfig()
}
workflow := `
name: custom-scan
kind: module
steps:
- name: scan
type: bash
command: echo "Using custom config"
`
result, err := lib.Run("target.com", workflow, &lib.RunOptions{
Config: cfg,
WorkspacesPath: "/custom/output/path",
Verbose: true,
Silent: false,
})
if err != nil {
log.Fatal(err)
}
log.Printf("Completed: %s", result.Status)
}