Skip to main content

Route CI/CD security checks from an AI-assisted analysis of Git changes

Project description

secorch

secorch is a command-line tool that analyzes the latest Git commit and produces a validated DevSecOps routing decision. It combines four focused contexts of the commit, its message, file status, affected path, and textual patch to recommend which CI/CD security checks are relevant.

The result is printed for humans, rendered as a PDF audit report, and published as GitHub Actions outputs for downstream jobs.

Contents

What secorch Does

  • Reads the current HEAD commit from a Git repository.
  • Gives four specialized LLM agents a different context of that same commit.
  • Combines their findings using an explicit evidence-precedence policy.
  • Routes six supported security checks to either required or skip.
  • Produces a PDF containing the evidence, decision, reasons, and uncertainties.
  • Exposes values that CI workflows can use in conditions.

[!IMPORTANT] secorch does not run the security scanners itself. It decides which downstream checks should run. It also does not inspect uncommitted, staged-only, or historical changes: analysis is scoped to the latest commit only.

Architecture

The CLI is a small Typer library based Python application with two commands: init and analyze. The secorch command calls secorch.__main__:main, which invokes the Typer app. Typer parses the command and typed options, applies their defaults, and dispatches to the matching command function.

flowchart TD
    ENTRY[secorch command] --> MAIN[__main__.py: main]
    MAIN --> APP[Typer app in cli.py]
    APP --> PARSE{Parse command and options}

    INIT_OPTIONS[--output<br/>--force] --> INIT
    PARSE -->|init| INIT[commands/init.py]
    INIT --> INIT_LOGIC{Output already exists?}
    INIT_LOGIC -->|no, or --force| CONFIG_FILE[Write TOML configuration]
    INIT_LOGIC -->|yes, without --force| ERROR[User-facing error and exit 1]

    PARSE -->|analyze| ANALYZE[commands/analyze.py]
    ANALYZE_OPTIONS[--repository<br/>--config<br/>--report<br/>--github-output] --> ANALYZE
    ANALYZE --> PREFLIGHT[Validate repository and config]
    PREFLIGHT -->|invalid| ERROR
    PREFLIGHT -->|valid| ORCHESTRATE[Run analysis and validate decision]
    ORCHESTRATE --> OUTPUTS[Print result, write PDF, publish CI outputs]

cli.py registers the command functions with Typer, while each file in commands/ owns its option declarations and command-level control flow. The analyze command delegates Git access, model execution, decision validation, and report generation to focused modules; the execution-flow diagram below shows that internal process in detail.

How secorch Works

The analyze command first validates prerequisites, then invokes four logically independent analyzers (LLM agents). None of these analyzers consumes another analyzer's output; each receives and examines a different context of the same commit. The current implementation schedules them sequentially, but their evidence is combined only after all four have completed. Their labeled responses are then passed to the dependent final decision agent, whose JSON is accepted only after strict application-side validation.

flowchart TD
    CLI[secorch analyze] --> PRE{Validate inputs}
    PRE -->|repository and config valid| HEAD[Resolve repository HEAD]
    PRE -->|invalid| EXIT[Print error and exit 1]

    HEAD --> MSG[Commit message analyzer]
    HEAD --> STATUS[File status analyzer]
    HEAD --> PATHS[Affected path analyzer]
    HEAD --> DIFF[Textual patch analyzer]

    MSG --> COMBINE[Final decision agent]
    STATUS --> COMBINE
    PATHS --> COMBINE
    DIFF --> COMBINE
    COMBINE -->|invalid| FAILPDF[Write failure PDF]
    FAILPDF --> EXIT

    COMBINE -->|valid| PDF[PDF audit report]
    PDF --> GHO[GitHub Actions outputs]
    GHO --> CONSOLE[Console summary]

Analyzers and Inputs

All four contexts are derived from the same HEAD commit and used with different analyzers:

Context and Analyzer Git input What it contributes
Commit message Complete commit message Author intent and stated context
File status git diff-tree --root --name-status -r -M -C Added, modified, deleted, and renamed
Affected path Paths parsed from the same name-status data Affected files, directories, logical components, and change concentration
Textual patch (code diff) git show --patch --no-color --no-ext-diff --find-renames --find-copies Implemented behavior, important code changes, and security implications visible in the patch

The repository path may point at the repository itself or a directory inside it; GitPython searches parent directories. Empty repositories are rejected. Root commits and empty commits are handled explicitly, while binary-only changes may provide limited textual evidence.

Decision Logic

When evidence conflicts, the final agent uses this precedence order:

flowchart LR
    A["Textual patch (code diff)<br/>highest priority"] --> B[Affected path]
    B --> C[File status]
    C --> D[Commit message<br/>lowest priority]

A check is required when evidence supports running it or when the available evidence is insufficient to safely skip it. A check is skip only when there is enough evidence that it is irrelevant to the commit. The decision agent never claims that a check has already run or passed.

The supported checks are:

Check Intended routing target
secret_scanning Exposed credentials, tokens, keys, and other secrets
sca Dependency manifests, lockfiles, and dependency risk
sast Static analysis of application or library source code
iac_scanning Infrastructure-as-code and deployment configuration
container_scanning Container definitions, images, and related build inputs
dast Runtime-facing behavior suitable for dynamic application testing

Before any successful result is published, secorch requires:

  • risk_level to be exactly low, medium, or high.
  • every supported check to appear exactly once.
  • every priority to be exactly required or skip.
  • a non-empty summary and reason for every check.
  • uncertainties to be a list of strings.

Requirements and Installation

secorch requires Python 3.12+, Git, an OpenAI API key, and a Git repository with at least one commit.

Install from a checkout with uv:

git clone https://github.com/faizanmsiddiqui/secorch.git
cd secorch
uv sync

Use uv run secorch in the examples below.

Quick Start

uv run secorch init
export OPENAI_API_KEY="your-api-key"
uv run secorch analyze

This analyzes the current repository's latest commit, prints all four analyses and the final routing decision, and creates artifacts/secorch-report.pdf.

Development and Testing

uv sync --dev
uv run ruff format .
uv run ruff check .
uv run mypy
uv run pytest

Configuration

The default secorch.toml is:

[model]
provider = "openai"
name = "gpt-5.4-mini"
temperature = 0.0

Generate it with secorch init, copy secorch.example.toml, or select another file with --config. The model table and all three model values are validated.

[!IMPORTANT] OpenAI is currently the only supported provider. Keep OPENAI_API_KEY in the environment or a CI secret; never store it in the TOML file or commit it.

Commands and Inputs

secorch init

Generates the default configuration file.

secorch init [--output PATH] [--force]
Option Default Description
--output, -o secorch.toml Configuration file to create
--force, -f false Replace the file if it already exists

secorch analyze

Analyzes the repository's current HEAD commit.

secorch analyze \
  --repository . \
  --config secorch.toml \
  --report artifacts/secorch-report.pdf \
  --github-output /path/to/github-output
Input Default Description
--repository, -r . Repository or a directory within one
--config, -c secorch.toml TOML model configuration
--report secorch-report.pdf Destination for the PDF audit report
--github-output $GITHUB_OUTPUT, when set File to append CI outputs to; an explicit path takes precedence
OPENAI_API_KEY none Required environment variable used by the OpenAI client

Run secorch COMMAND --help for the generated CLI help.

Outputs

PDF Audit Report

On success, the report includes:

  • generation time in UTC and the full commit SHA.
  • completed or failed status.
  • overall risk, summary, check priorities, and reasons.
  • uncertainties reported by the decision agent.
  • every analyzer output that completed.

Pandoc converts an intermediate Markdown report into a styled A4 PDF using WeasyPrint. The destination directory is created automatically, and the completed PDF replaces the destination atomically. If analysis fails after the commit has been resolved, secorch attempts to write a failure report containing the error and any analyzer results collected before the failure.

GitHub Actions Outputs

When GITHUB_OUTPUT is present or --github-output PATH is supplied, secorch appends these values:

risk_level=medium
checks={"container_scanning":"skip","dast":"skip","iac_scanning":"skip","sast":"required","sca":"required","secret_scanning":"required"}
report=artifacts/secorch-report.pdf
check_secret_scanning=required
check_sca=required
check_sast=required
check_iac_scanning=skip
check_container_scanning=skip
check_dast=skip

checks is compact, key-sorted JSON. The individual check_<name> values are convenient for simple step conditions.

Console

stdout contains each labeled analyzer response followed by the final risk level, all six check priorities, and the PDF path. This is the detailed human view; CI integrations should consume the structured GitHub outputs instead of parsing console text.

Failure Behavior

analyze exits with status 1 for invalid paths, missing or invalid configuration, repository errors, model or provider failures (including missing credentials), or an invalid decision contract. Preflight failures occur before a commit is resolved and do not produce a report. Analysis-stage failures produce a failure PDF when report rendering itself remains available. GitHub outputs are written only after a valid decision and successful report generation.

GitHub Actions Example

name: DevSecOps Pipeline

on:
    workflow_dispatch:
    push:
        branches:
            - main
    pull_request:

permissions:
    contents: read

jobs:
    orchestrate:
        name: Analyze and Orchestrate Security Checks
        runs-on: ubuntu-latest
        outputs:
            checks: ${{ steps.secorch.outputs.checks }}
            risk_level: ${{ steps.secorch.outputs.risk_level }}
            report: ${{ steps.secorch.outputs.report }}

        steps:
            - name: Check out repository
              uses: actions/checkout@v4
              with:
                  fetch-depth: 0

            - name: Set up Python
              uses: actions/setup-python@v5
              with:
                  python-version: "3.12"
                  cache: pip

            - name: Install secorch package
              run: python -m pip install .

            - name: Generate default configuration
              run: secorch init --force

            - name: Analyze latest commit
              id: secorch
              env:
                  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
              run: secorch analyze --report artifacts/secorch-report.pdf

            - name: Upload decision report
              if: ${{ always() }}
              uses: actions/upload-artifact@v4
              with:
                  name: secorch-decision-report
                  path: artifacts/secorch-report.pdf
                  if-no-files-found: warn
                  retention-days: 14

    secret-scanning:
        name: Secret Scanning
        needs: orchestrate
        if: ${{ fromJSON(needs.orchestrate.outputs.checks).secret_scanning != 'skip' }}
        runs-on: ubuntu-latest
        steps:
            - name: Secret Scanning (dummy)
              run: echo "TODO Run secret scanning"

    sca:
        name: SCA
        needs: orchestrate
        if: ${{ fromJSON(needs.orchestrate.outputs.checks).sca != 'skip' }}
        runs-on: ubuntu-latest
        steps:
            - name: SCA (dummy)
              run: echo "TODO Run software composition analysis"

    sast:
        name: SAST
        needs: orchestrate
        if: ${{ fromJSON(needs.orchestrate.outputs.checks).sast != 'skip' }}
        runs-on: ubuntu-latest
        steps:
            - name: SAST (dummy)
              run: echo "TODO Run static application security testing"

    iac-scanning:
        name: IaC Scanning
        needs: orchestrate
        if: ${{ fromJSON(needs.orchestrate.outputs.checks).iac_scanning != 'skip' }}
        runs-on: ubuntu-latest
        steps:
            - name: IaC Scanning (dummy)
              run: echo "TODO Run infrastructure-as-code scanning"

    container-scanning:
        name: Container Scanning
        needs: orchestrate
        if: ${{ fromJSON(needs.orchestrate.outputs.checks).container_scanning != 'skip' }}
        runs-on: ubuntu-latest
        steps:
            - name: Container Scanning (dummy)
              run: echo "TODO Run container image scanning"

    dast:
        name: DAST
        needs: orchestrate
        if: ${{ fromJSON(needs.orchestrate.outputs.checks).dast != 'skip' }}
        runs-on: ubuntu-latest
        steps:
            - name: DAST (dummy)
              run: echo "TODO Run dynamic application security testing"

License

secorch is available under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

secorch-0.1.0.tar.gz (17.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

secorch-0.1.0-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file secorch-0.1.0.tar.gz.

File metadata

  • Download URL: secorch-0.1.0.tar.gz
  • Upload date:
  • Size: 17.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for secorch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6d91377f877da2e61dfb8f74aa8b9e17c74e0da8bb7517077d2f866feaa080ca
MD5 4f76e0444d78ad9e760d447c776d41c0
BLAKE2b-256 a284f882096cef47bcf5679e145fdf15bb1c4d871d2dfe26e176488d83b07b27

See more details on using hashes here.

File details

Details for the file secorch-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: secorch-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for secorch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c0af2eb9af7c14576bf613bd6001e31c56c0a0a0dd9467fab90a7d8d19cd6b12
MD5 83bd96bdff4af926c97a9cf5bf61c8e0
BLAKE2b-256 a437df5c8b2b6f8efd06695baf0cd799eb60eca6068b44d2b19e8040068dba4b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page