Skip to main content
Back to Home

Zelsis for open-source projects

Maintainers review pull requests from people they have never met, often in their spare time. Zelsis checks each one for leaked keys, injection, missing auth checks and the logic bugs that slip through review, and it is free for public repositories.

  • Public repositories scan free, with no limit

    The Free plan caps private projects, not public ones. Scan the default branch whenever you like and keep the history.

  • A check on every pull request

    The workflow below sends the base and head commits, so a contributor’s pull request is only judged on what it changes. Findings appear on their lines in “Files changed”, critical and high as errors, medium as warnings.

  • Your own key, your own limits

    The gate runs for public repositories without a key, but then it shares GitHub’s limit for unauthenticated requests with everyone else. A free account and an API key give your project its own.

  • You decide what blocks a merge

    Only findings from proven rules can fail the gate, and failStrategy in .zelsisrc.json sets which severities block. Dismiss a finding once and it stops counting.

Set it up in two steps

1. Create a free account, open Settings → API keys and add the key to your repository as the Actions secret ZELSIS_API_KEY. 2. Save this as .github/workflows/zelsis.yml.

name: Zelsis Pre-Flight Release Gate

on:
  pull_request:
    branches: [main, master, develop]
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  zelsis-gate:
    name: Zelsis Release Gate (your app)
    runs-on: ubuntu-latest
    steps:
      - name: Run Zelsis gate check
        env:
          # Create a key in Zelsis: Settings -> API keys. Required for private repositories.
          ZELSIS_API_KEY: ${{ secrets.ZELSIS_API_KEY }}
          # Lets Zelsis read a private repository for this run only.
          GH_READ_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          REF=""
          BASE=""
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            REF="${{ github.event.pull_request.head.sha }}"
            BASE="${{ github.event.pull_request.base.sha }}"
          fi

          FAIL_OPEN="${{ vars.ZELSIS_FAIL_OPEN || 'false' }}"

          RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "https://zelsis.com/api/v1/gate-check" \
            -H "Content-Type: application/json" \
            -H "x-api-key: $ZELSIS_API_KEY" \
            -d '{"repoUrl": "${{ github.server_url }}/${{ github.repository }}", "githubToken": "'$GH_READ_TOKEN'", "ref": "'$REF'", "base": "'$BASE'"}')

          HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
          BODY=$(echo "$RESPONSE" | sed '$d')

          if [ "$HTTP_CODE" -ge 400 ] && [ "$HTTP_CODE" -lt 500 ]; then
            echo "::error::Zelsis rejected the request (HTTP $HTTP_CODE): $(echo "$BODY" | jq -r '[.error // "see log"] + [.details[]? | .field + ": " + .message] | join("; ")'). Check the ZELSIS_API_KEY secret."
            exit 1
          fi

          if [ "$HTTP_CODE" -ge 500 ] || [ -z "$BODY" ] || ! echo "$BODY" | jq -e . >/dev/null 2>&1; then
            echo "Zelsis API unreachable or returned an invalid response."
            if [ "$FAIL_OPEN" = "true" ]; then
              echo "Fail-open is enabled: passing the gate."
              echo '{"gateStatus": "PASSED", "readinessScore": 100}' > zelsis-report.json
              exit 0
            fi
            exit 1
          fi

          echo "$BODY" > zelsis-report.json
          STATUS=$(jq -r '.gateStatus // "ERROR"' zelsis-report.json)
          SCORE=$(jq -r '.readinessScore // 0' zelsis-report.json)
          echo "Gate: $STATUS, score: $SCORE/100" >> $GITHUB_STEP_SUMMARY
          # Shows each finding on its line in the pull request diff
          jq -r '.annotations[]? // empty' zelsis-report.json
          if [ "$STATUS" = "FAILED" ]; then echo "::error::Zelsis gate FAILED (score $SCORE/100)"; exit 1; fi

      - name: Upload gate report
        if: always()
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: zelsis-release-report
          path: zelsis-report.json

Fork pull requests cannot read repository secrets on GitHub, so for them the gate runs without the key. More options are in the CI gate docs.

Try it on your repository first

Paste the GitHub URL on the dashboard and see what a scan finds before you add the workflow. If something looks wrong, tell us: false alarms on well-kept projects are how the rules get better.