Skip to content

About

Railo — auto-fix security vulnerabilities in your PRs. Deterministic patches, no AI.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Railo

Closes the security findings in your pull requests with a small, reviewed fix PR.

Railo scans each pull request, rewrites the vulnerable code with a deterministic, known-safe pattern, and opens a separate fix PR against the PR's branch for you to review. No LLM writes the code, and everything runs in your own GitHub Actions runner.

Quick start

Create .github/workflows/railo.yml:

name: Railo

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: read          # enforce mode pushes the fix PR's branch: use write
  pull-requests: write    # suggestions on the PR, and the fix PR
  checks: write           # the Railo check run on your PR
  security-events: write  # optional: upload findings to GitHub code scanning

jobs:
  railo:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09  # v5.1.0
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0
          persist-credentials: false  # enforce mode pushes with this token: remove this line
      - uses: IWEBai/railo-action@v2
        with:
          mode: warn

actions/checkout is pinned by commit to v5.1.0, which runs on Node 24.

Open a pull request and Railo runs. The Action is free: no account, no license key and no usage limit to set up.

Modes

Mode What happens
warn Railo posts each proposed fix as a one-click suggestion on the lines it changes (Commit suggestion applies it). Nothing is pushed.
enforce Railo opens a separate fix PR against your PR's branch. Your branch is never changed until you merge it.

Start with warn to see what Railo would change. Warn mode's check is neutral, so findings never block a merge; set fail_on_findings: true if you want them to fail the job.

A fix PR opened with the workflow's own token does not start your other workflows (a GitHub rule). Re-run your checks on the fix PR, or push to it, before merging.

What Railo fixes

Fix quality is measured on every release against a benchmark of reviewed vulnerable-to-fixed pairs; a fix class ships only at 90% or better.

Python fix class Example rewrite
SQL injection (CWE-89) cur.execute(f"... {x}") → cur.execute("... %s", (x,))
Command injection (CWE-78) os.system("ping -c 1 " + host) → subprocess.run(["ping", "-c", "1", host])
Hardcoded secrets (CWE-798) password="..." → password=os.environ.get("PASSWORD")
Cross-site scripting (CWE-79) mark_safe("<b>" + name + "</b>") → format_html("<b>{}</b>", name)
Path traversal (CWE-22) adds a check that the resolved path stays under the base directory, raising PermissionError otherwise
XXE (CWE-611) xml.etree.ElementTree → defusedxml.ElementTree (the fix PR says when your project must add defusedxml)
Missing timeout (CWE-400) httpx.post(url, json=data) → httpx.post(url, json=data, timeout=10.0)
Insecure randomness (CWE-330) random.randint(a, b) → secrets.SystemRandom().randint(a, b)
Cookie flags (CWE-614/1004) set_cookie("session", v) → set_cookie("session", v, httponly=True, secure=True, samesite="Lax")

Python also has fixes for SQL injection in Django and SQLAlchemy raw queries, server-side template injection, open redirects, disabled CSRF protection, permissive CORS with credentials, disabled TLS certificate checks, unsafe YAML loading, eval() on data, XML entity expansion, stack traces sent to users, and debug mode left on.

Other languages, each fix class on the same benchmark:

Language Fix classes
JavaScript and TypeScript SQL injection, command injection, cross-site scripting, path traversal, open redirects, hardcoded secrets, missing timeouts, insecure randomness, insecure cookies
Java SQL injection, cross-site scripting, path traversal, SSRF, hardcoded secrets
Go SQL injection, command injection, hardcoded secrets
PHP SQL injection, command injection, cross-site scripting, path traversal, unsafe unserialize, hardcoded secrets, insecure randomness, insecure cookies

Findings in other languages (Ruby and C#, for example) are still detected and reported, but not rewritten.

When Railo cannot fix something safely (for example a command whose program comes from user input), it says so in the PR rather than guessing.

How a fix is checked

Each fix PR states, per finding, what was checked:

  • Rescan. The rules run again on the patched file: the finding must be gone and no new finding may appear.
  • Safe pattern present. The rewritten code must contain the known-safe construct (a bound query parameter, an argument list, an escaping call).
  • Your tests. Your own CI runs on every fix PR, like on any other change. Railo also tries the project's test command before and after each fix and drops a fix that adds a failure, but it runs inside the Action's container, which does not have your project's dependencies installed, so for most projects the command cannot start there. A test command that cannot start is not counted against a fix.

Every fix PR needs your review before merging.

Inputs

Input Default Description
license_key none Not needed. Kept so workflows that already pass it keep working.
mode warn warn or enforce
base_branch the PR's base branch Branch to diff against
max_diff_lines 500 Largest total diff Railo will deliver in one run
test_before_commit false Only for direct pushes (RAILO_DELIVERY=push): also run test_command first, inside the Action's container.
test_command pytest Command used by test_before_commit
fail_on_findings false Warn mode only: fail the job when Railo finds something
sarif none SARIF files from scanners you already run, as globs (e.g. results/*.sarif)

Fixing findings from your own scanners

If a step earlier in the job writes SARIF (CodeQL, Semgrep, Bandit, Snyk and most other scanners can), pass it in and Railo fixes those findings too:

      - run: semgrep scan --sarif --output semgrep.sarif
      - uses: IWEBai/railo-action@v2
        with:
          mode: enforce
          sarif: semgrep.sarif

Railo checks these fixes by rerunning its own rules, not your scanner's, so the fix PR says the original rule was not rerun. Rescan with your scanner before merging.

Configuration

Add a .railo.yml to your repository root for more control:

max_diff_lines: 300
test_timeout: 300

To skip paths, list them in a .railoignore file (same syntax as .gitignore).

Privacy

Scanning, fixing and testing run in your GitHub Actions runner; your source code is not sent to Railo. The Action sends one usage record per run to railo.dev: organization and repository name, plan, mode, number of findings and fixes, scan duration and the Action version. Set the environment variable RAILO_TELEMETRY=0 on the step and nothing is sent to Railo. Nothing is enforced from railo.dev; the Action has no usage limit.

Links

License

The files in this repository are MIT licensed (see LICENSE). The engine runs from the ghcr.io/iwebai/railo image, which is Railo's and free to use through this Action under the terms of service.

About

Railo — auto-fix security vulnerabilities in your PRs. Deterministic patches, no AI.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors