Skip to content

cdk100/Security-orchestration-prototype

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Security Workflow Orchestrator Prototype

Python CLI prototype for the thesis architecture. It demonstrates JSON workflow loading, adapter selection, Kali-based tool execution, evidence capture, normalised findings, traceability fields, and a readable XML report.

The prototype assumes a Kali Linux execution environment with the selected security tools installed. The CLI may run on another machine, but the tool adapters execute tools on Kali over SSH.

Command Reference

Set the environment first. PowerShell example:

$env:SECPLATFORM_KALI_HOST='<kali-host-or-ip>'
$env:SECPLATFORM_KALI_USER='<ssh-username>'
$env:SECPLATFORM_KALI_PASSWORD='<ssh-password>'
$env:SECPLATFORM_KALI_PORT='22'
$env:SECPLATFORM_PROJECT_PATH='.'
$env:SECPLATFORM_ZAP_TARGET_URL='http://127.0.0.1:8080'
$env:SECPLATFORM_REMOTE_TMP='/tmp'

Bash example:

export SECPLATFORM_KALI_HOST='<kali-host-or-ip>'
export SECPLATFORM_KALI_USER='<ssh-username>'
export SECPLATFORM_KALI_PASSWORD='<ssh-password>'
export SECPLATFORM_KALI_PORT='22'
export SECPLATFORM_PROJECT_PATH='.'
export SECPLATFORM_ZAP_TARGET_URL='http://127.0.0.1:8080'
export SECPLATFORM_REMOTE_TMP='/tmp'

Optional tool path overrides:

export SECPLATFORM_GITLEAKS_BIN='gitleaks'
export SECPLATFORM_TRIVY_BIN='trivy'
export SECPLATFORM_SEMGREP_BIN='semgrep'
export SECPLATFORM_ZAP_BIN='zaproxy'

SECPLATFORM_KALI_PASSWORD is read at runtime by the SSH helper. It is not stored in workflow JSON or reports. If you want to use a differently named password variable, set SECPLATFORM_KALI_PASSWORD_ENV to that variable name.

Expected Kali tools:

  • OpenSSH server reachable from the CLI machine.
  • python3 and curl for running the included test applications.
  • gitleaks, trivy, semgrep, and zaproxy for the included workflows.
python sec_orchestrator.py validate example_workflow.json
python sec_orchestrator.py adapters
python sec_orchestrator.py normalizers
python sec_orchestrator.py run example_workflow.json

Show command help:

python sec_orchestrator.py --help
python sec_orchestrator.py run --help

Validate a workflow without executing tools:

python sec_orchestrator.py validate example_workflow.json
python sec_orchestrator.py validate gitleaks_workflow.json

List registered extension points:

python sec_orchestrator.py adapters
python sec_orchestrator.py normalizers

Run the barebones no-tool workflow:

python sec_orchestrator.py run example_workflow.json

Run a workflow and write artifacts to a custom directory:

python sec_orchestrator.py run example_workflow.json --output runs/dev-check

Run the Gitleaks self-scan through the Kali host:

python sec_orchestrator.py run gitleaks_workflow.json

The Gitleaks workflow copies the local project to a temporary directory on Kali, runs gitleaks detect over SSH, stores the raw JSON output as evidence, and normalises any findings into the common finding model. The SSH password is read from the configured environment variable, so it is not stored in the workflow JSON.

Run the Trivy self-scan through the Kali host:

python sec_orchestrator.py run trivy_workflow.json

The Trivy workflow copies the local project to Kali and runs trivy fs with JSON output. It is currently configured for vulnerability, secret, and misconfiguration scanning.

Run the Semgrep self-scan through the Kali host:

python sec_orchestrator.py run semgrep_workflow.json

The Semgrep workflow copies the local project to Kali and runs Semgrep with the Python ruleset configured in semgrep_workflow.json.

Prepare an OWASP ZAP web scan:

python sec_orchestrator.py validate zap_workflow.json

Before running ZAP, edit zap_workflow.json and set target_url to a web application that you own or have explicit permission to test, or set SECPLATFORM_ZAP_TARGET_URL. Then run:

python sec_orchestrator.py run zap_workflow.json

Run the realistic NIST CSF 2.0 application security workflow:

python sec_orchestrator.py validate nist_csf_2_0_full_workflow.json
python sec_orchestrator.py validate nist_csf_2_0_code_workflow.json

Use the full workflow when there is a running web application that ZAP is allowed to scan. Set SECPLATFORM_ZAP_TARGET_URL, then run:

python sec_orchestrator.py run nist_csf_2_0_full_workflow.json

Use the code-only workflow when there is no website or web service available:

python sec_orchestrator.py run nist_csf_2_0_code_workflow.json

There is no meaningful automatic fallback for OWASP ZAP when no website exists. ZAP is a dynamic application security testing tool, so it needs a reachable HTTP or HTTPS target. In that situation, the correct fallback is to use the workflow without ZAP and rely on the source, dependency, secret, and configuration checks.

The NIST CSF 2.0 workflows use high-level CSF outcomes as requirement IDs and map them to concrete security capabilities. NIST CSF 2.0 is an outcome-oriented framework, so these mappings are pragmatic prototype mappings rather than official tool prescriptions. Reference: https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf.

Test Applications

The repository includes two small Python web applications for repeatable testing:

  • test_apps/vulnerable/app.py: intentionally vulnerable target with reflected XSS, SQL injection-style query construction, weak cookie handling, missing security headers, and a fake committed secret.
  • test_apps/fixed/app.py: corrected copy with output escaping, parameterized SQL, safer cookie flags, security headers, and no fake secret.

The ZAP workflows expect the app to run on the Kali host because the default ZAP target is http://127.0.0.1:8080 from Kali's point of view.

Start the vulnerable app on Kali:

$remote = "$env:SECPLATFORM_KALI_USER@$env:SECPLATFORM_KALI_HOST"
$testApp = "$env:SECPLATFORM_REMOTE_TMP/secplatform-test-app.py"
scp test_apps/vulnerable/app.py "${remote}:$testApp"
ssh $remote "pkill -f secplatform-test-app.py || true; nohup python3 $testApp --host 127.0.0.1 --port 8080 > $env:SECPLATFORM_REMOTE_TMP/secplatform-test-app.log 2>&1 &"
ssh $remote "curl -s http://127.0.0.1:8080/health"

Then run the full vulnerable-app workflow:

python sec_orchestrator.py run nist_csf_2_0_vulnerable_test_workflow.json

Start the fixed app on Kali:

$remote = "$env:SECPLATFORM_KALI_USER@$env:SECPLATFORM_KALI_HOST"
$testApp = "$env:SECPLATFORM_REMOTE_TMP/secplatform-test-app.py"
scp test_apps/fixed/app.py "${remote}:$testApp"
ssh $remote "pkill -f secplatform-test-app.py || true; nohup python3 $testApp --host 127.0.0.1 --port 8080 > $env:SECPLATFORM_REMOTE_TMP/secplatform-test-app.log 2>&1 &"
ssh $remote "curl -s http://127.0.0.1:8080/health"

Then run the full fixed-app workflow:

python sec_orchestrator.py run nist_csf_2_0_fixed_test_workflow.json

The vulnerable workflow is expected to produce findings from all four tools: Gitleaks, Semgrep, Trivy, and OWASP ZAP. The fixed workflow is expected to clear the source, secret, and filesystem checks. ZAP may still report Authentication Request Identified as an informational item because the fixed app still contains a login form; that is useful metadata rather than a vulnerability.

The test workflows configure Semgrep with a project-local rule file by setting config_from_project to true. That lets a workflow use rules bundled with the scanned project after it has been copied to the Kali host.

Stop the test app on Kali:

$remote = "$env:SECPLATFORM_KALI_USER@$env:SECPLATFORM_KALI_HOST"
ssh $remote "pkill -f secplatform-test-app.py || true"

Each run creates a directory under runs/ containing:

  • workflow.snapshot.json: the workflow definition used for the run.
  • run.json: structured execution metadata and normalised findings.
  • evidence/*.json: raw output and metadata for each workflow step.
  • report.xml: human-readable XML report.

File Layout

The files are split to match the architecture diagram while keeping the prototype small:

  • sec_orchestrator.py: CLI entry point for the interface layer.
  • secplatform/cli.py: interface layer command handling.
  • secplatform/repositories.py: requirements/workflow repository plus evidence storage.
  • secplatform/orchestrator.py: core workflow orchestrator.
  • secplatform/adapters/: tool adapter layer.
  • secplatform/adapters/base.py: common adapter interface.
  • secplatform/adapters/registry.py: adapter map and selection logic.
  • secplatform/adapters/noop.py: current no-op adapter.
  • secplatform/normalizers/: result normalisation layer.
  • secplatform/normalizers/base.py: common normalizer interface.
  • secplatform/normalizers/registry.py: normalizer map and selection logic.
  • secplatform/normalizers/default.py: fallback normalizer for common finding dictionaries.
  • secplatform/reporting.py: XML report generator.
  • secplatform/models.py: shared data contracts used between layers.
  • requirements/: reusable requirement and control-set JSON files.
  • test_apps/: vulnerable and fixed demo applications for workflow testing.

sec_orchestrator.py is only a start script. It intentionally has no workflow, adapter, repository, or reporting logic. The actual CLI behavior lives in secplatform/cli.py.

Workflow Shape

Workflows are JSON documents. The project section records the local path or GitHub/Git URL that the workflow applies to.

{
  "id": "example",
  "name": "Example Workflow",
  "version": "0.1.0",
  "project": {
    "source": "github",
    "url": "https://github.com/example/project",
    "branch": "main"
  },
  "requirements": [],
  "steps": []
}

Workflow JSON may reference environment variables with ${NAME}. If the variable is required and missing, validation fails before execution. Use ${NAME:default} when a safe default is acceptable.

{
  "project": {
    "source": "local",
    "path": "${SECPLATFORM_PROJECT_PATH:.}"
  },
  "steps": [
    {
      "parameters": {
        "host": "${SECPLATFORM_KALI_HOST}",
        "username": "${SECPLATFORM_KALI_USER}",
        "password_env_var": "${SECPLATFORM_KALI_PASSWORD_ENV:SECPLATFORM_KALI_PASSWORD}"
      }
    }
  ]
}

The current built-in adapter is noop, which supports the placeholder and manual capabilities without invoking external tools. Real Kali tool adapters can be added by implementing the same adapter interface and registering the adapter in the adapter registry map.

validate checks the JSON structure and requirement mappings. run also checks that a requested adapter exists and supports the requested capability.

Extending the Prototype

The main rule is that requirements, workflow changes, and tool changes should live in different places:

  • Requirements and mappings belong in the workflow JSON.
  • Step parameters and thresholds belong in the workflow JSON.
  • Tool command details belong inside adapters.
  • Tool-specific parsing belongs inside normalizers.
  • Reports should consume the normalised run result, not raw tool output.

This keeps the workflow orchestrator independent from specific security tools.

Add a Requirement

Add a requirement object to the requirements array. Requirements describe the security intent, not the tool command.

{
  "requirements": [
    {
      "id": "REQ-SECRETS-001",
      "title": "Committed secrets must be detected",
      "description": "The project should be checked for hard-coded secrets before release.",
      "source": "Internal secure development policy"
    }
  ]
}

Then link a workflow step to that requirement with requirement_ids.

{
  "steps": [
    {
      "id": "review-secret-handling",
      "name": "Review secret handling",
      "capability": "manual",
      "adapter": "noop",
      "requirement_ids": ["REQ-SECRETS-001"],
      "parameters": {
        "message": "Placeholder until a real secret detection adapter is added."
      }
    }
  ]
}

That is the S1 path: a new requirement can be added and mapped without changing the orchestrator.

Use an External Requirements File

For small examples, requirements can stay inline in the workflow JSON. For organisational policies or larger control sets, put them in a separate JSON file and reference it from the workflow:

{
  "requirements_file": "requirements/internal_policy.json"
}

The external file may either be a plain list:

[
  {
    "id": "REQ-SECRETS-001",
    "title": "Committed secrets must be detected",
    "description": "The project should be checked for hard-coded secrets before release.",
    "source": "Internal secure development policy"
  }
]

Or an object with a requirements list:

{
  "requirements": [
    {
      "id": "REQ-SECRETS-001",
      "title": "Committed secrets must be detected"
    }
  ]
}

The repository layer merges external requirements with any inline requirements before validation. The workflow orchestrator only sees the structured workflow definition.

Modify a Workflow Step

Change step behavior through parameters, not Python orchestration code.

{
  "id": "dependency-scan",
  "name": "Scan dependencies",
  "capability": "manual",
  "adapter": "noop",
  "parameters": {
    "severity_threshold": "high",
    "include_dev_dependencies": false
  }
}

Later, a real adapter can decide how those parameters map to a concrete command. That is the S2 path: thresholds and configuration live in the workflow definition.

Add Mitigation Guidance

Steps can include mitigation guidance. When an adapter returns findings for that step, the report can link the finding to a plausible response.

{
  "id": "review-secret-handling",
  "name": "Review secret handling",
  "capability": "manual",
  "adapter": "noop",
  "mitigation": {
    "title": "Rotate exposed credentials",
    "guidance": "Revoke the exposed credential, rotate it, and remove it from repository history.",
    "references": [
      "https://docs.github.com/en/code-security/secret-scanning"
    ]
  }
}

That supports S6 and S7: findings remain traceable and actionable.

Add a Tool Adapter

Adapters are the only place where concrete tool execution details should live. To add a real tool, create a new file in secplatform/adapters/. For example, secplatform/adapters/example_tool.py.

  • id: the adapter name used in workflow JSON.
  • capabilities: the abstract capabilities the adapter can satisfy.
  • execute(step, execution_context): the method that invokes the tool and returns an AdapterResult.

Minimal shape:

from secplatform.models import AdapterResult, ExecutionContext, WorkflowStep


class ExampleAdapter:
    id = "example-tool"
    capabilities = ("secret-detection",)

    def execute(self, step: WorkflowStep, execution_context: ExecutionContext) -> AdapterResult:
        return AdapterResult(
            status="passed",
            exit_code=0,
            raw_output={
                "tool": self.id,
                "message": "Replace this with captured tool output."
            },
            findings=[]
        )

Then register it in the adapter map in secplatform/adapters/registry.py:

from .example_tool import ExampleAdapter


ADAPTER_CLASSES = {
    NoOpAdapter.id: NoOpAdapter,
    ExampleAdapter.id: ExampleAdapter,
}

After that, a workflow step can request it explicitly:

{
  "id": "detect-committed-secrets",
  "name": "Detect committed secrets",
  "capability": "secret-detection",
  "adapter": "example-tool",
  "parameters": {}
}

That is the S3 path: adding a tool should require a new adapter and registration, not changes to the workflow orchestrator.

Return Findings from an Adapter

For simple adapters, the adapter may return common finding dictionaries directly in AdapterResult.findings. The default normalizer converts those dictionaries to the platform finding model.

return AdapterResult(
    status="completed_with_findings",
    exit_code=1,
    raw_output={"tool_output": "..."},
    findings=[
        {
            "id": "SECRET-001",
            "title": "Possible committed secret",
            "severity": "high",
            "description": "A token-like value was detected in the repository."
        }
    ]
)

The raw tool output still goes into evidence/*.json; the report uses the normalised finding fields.

Add a Tool-Specific Normalizer

Real tools often produce their own JSON, XML, or text output. In that case, keep the adapter focused on execution and place parsing logic in a normalizer.

Create a file in secplatform/normalizers/, for example secplatform/normalizers/example_tool.py.

from secplatform.models import AdapterResult, Finding, ToolExecution, WorkflowStep


class ExampleToolNormalizer:
    id = "example-tool"
    adapter_ids = ("example-tool",)
    capabilities = ()

    def normalise(
        self,
        step: WorkflowStep,
        execution: ToolExecution,
        adapter_result: AdapterResult,
    ) -> list[Finding]:
        return [
            Finding(
                id=f"{execution.id}-finding-1",
                title="Parsed finding title",
                severity="high",
                description="Parsed from the tool-specific raw output.",
                requirement_ids=step.requirement_ids,
                step_id=step.id,
                execution_id=execution.id,
                evidence_file=execution.evidence_file,
                mitigation=step.mitigation,
            )
        ]

Then register it in secplatform/normalizers/registry.py:

from .example_tool import ExampleToolNormalizer


NORMALIZER_CLASSES = {
    DefaultFindingNormalizer.id: DefaultFindingNormalizer,
    ExampleToolNormalizer.id: ExampleToolNormalizer,
}

Normalizers are selected by adapter ID first, then by capability, then by the default normalizer.

Modify the XML Report

The prototype intentionally has one report output: developer-readable XML. The report generator lives in secplatform/reporting.py.

Change that file when the report needs different sections, names, ordering, or traceability details. The report generator should consume the normalised RunResult and WorkflowDefinition; it should not parse raw tool output.

Replace a Tool

Workflow steps can be written in terms of capability rather than a concrete tool. For example:

{
  "id": "detect-committed-secrets",
  "name": "Detect committed secrets",
  "capability": "secret-detection",
  "parameters": {}
}

If adapter is omitted, the registry selects the first adapter that supports the requested capability. To replace the tool, add/register a different adapter for the same capability or change the explicit adapter field in JSON.

That is the S4 path: the workflow expresses what should happen, while the adapter decides how it happens.

Project Targets

The workflow stores the project target so repeated runs can be inspected later.

Local project:

{
  "project": {
    "source": "local",
    "path": "/home/kali/example-project"
  }
}

GitHub or Git project:

{
  "project": {
    "source": "github",
    "url": "https://github.com/example/project",
    "branch": "main"
  }
}

The current prototype records this target. A later adapter can use it to clone, checkout, or scan the project.

Architecture Scenario Fit

  • S1: requirements are structured JSON objects and steps can link to them by ID.
  • S2: step parameters live in the workflow definition, not orchestration code.
  • S3: adapters are isolated behind a stable ToolAdapter interface.
  • S4: steps express capabilities; adapter choice can be explicit or selected by capability.
  • S5: each execution stores workflow snapshots, metadata, evidence, and reports.
  • S6: findings include requirement, step, execution, and evidence references.
  • S7: findings can carry mitigation guidance and references for user-facing reports.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages