name: "CertiSigma Census"
description: "Cryptographic file inventory and exfiltration detection — scan, verify integrity, or detect breaches in your CI/CD pipeline."
author: "Ten Sigma Sagl"

branding:
  icon: shield
  color: blue

inputs:
  command:
    description: "Census command to run: scan, integrity, compare, bulk-scan"
    required: true
  target:
    description: "Directory to scan or check"
    required: false
    default: "."
  manifest:
    description: "Path to manifest file"
    required: false
    default: ".census-manifest.db"
  api-key:
    description: "CertiSigma API key (or set CERTISIGMA_API_KEY env var)"
    required: false
    default: ""
  source:
    description: "Source label for attestation"
    required: false
    default: "github-ci"
  format:
    description: "Output format: text, json, jsonl, sarif (default: sarif for compare, json for others)"
    required: false
    default: ""
  upload-sarif:
    description: "Auto-upload SARIF to GitHub Security tab (true/false)"
    required: false
    default: "true"
  exit-zero:
    description: "Report-only mode — always exit 0 (true/false)"
    required: false
    default: "false"
  version:
    description: "Census version to install (e.g. 1.6.1). Empty = latest."
    required: false
    default: ""
  extra-args:
    description: "Additional CLI arguments passed through verbatim"
    required: false
    default: ""
  python-version:
    description: "Python version for the runner"
    required: false
    default: "3.12"

outputs:
  exit-code:
    description: "Census command exit code"
    value: ${{ steps.run-census.outputs.exit-code }}
  output-file:
    description: "Path to generated report file (if any)"
    value: ${{ steps.run-census.outputs.output-file }}
  sarif-file:
    description: "Path to SARIF file (if generated)"
    value: ${{ steps.run-census.outputs.sarif-file }}

runs:
  using: "composite"
  steps:
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ inputs.python-version }}

    - name: Install Census
      shell: bash
      env:
        CENSUS_VERSION: ${{ inputs.version }}
      run: |
        if [ -n "$CENSUS_VERSION" ]; then
          pip install --quiet "certisigma-census==${CENSUS_VERSION}"
        else
          pip install --quiet certisigma-census
        fi

    - name: Mask API key
      shell: bash
      env:
        INPUT_API_KEY: ${{ inputs.api-key }}
      run: |
        EFFECTIVE_KEY="${INPUT_API_KEY:-${CERTISIGMA_API_KEY:-}}"
        if [ -n "$EFFECTIVE_KEY" ]; then
          echo "::add-mask::${EFFECTIVE_KEY}"
        fi

    - name: Run Census
      id: run-census
      shell: bash
      env:
        INPUT_COMMAND: ${{ inputs.command }}
        INPUT_TARGET: ${{ inputs.target }}
        INPUT_MANIFEST: ${{ inputs.manifest }}
        INPUT_API_KEY: ${{ inputs.api-key }}
        INPUT_SOURCE: ${{ inputs.source }}
        INPUT_FORMAT: ${{ inputs.format }}
        INPUT_EXIT_ZERO: ${{ inputs.exit-zero }}
        INPUT_EXTRA_ARGS: ${{ inputs.extra-args }}
        INPUT_UPLOAD_SARIF: ${{ inputs.upload-sarif }}
      run: |
        set +e

        SARIF_PATH="${RUNNER_TEMP}/census-results.sarif"
        OUTPUT_PATH="${RUNNER_TEMP}/census-results.json"

        # Resolve effective format: sarif for compare by default, json otherwise
        if [ -n "$INPUT_FORMAT" ]; then
          EFF_FORMAT="$INPUT_FORMAT"
        elif [ "$INPUT_COMMAND" = "compare" ]; then
          EFF_FORMAT="sarif"
        else
          EFF_FORMAT="json"
        fi

        if [ "$EFF_FORMAT" = "sarif" ] && [ "$INPUT_COMMAND" != "compare" ]; then
          echo "::error::format=sarif is only valid with command=compare"
          exit 1
        fi

        # Set API key if provided as input (takes precedence over env var)
        if [ -n "$INPUT_API_KEY" ]; then
          export CERTISIGMA_API_KEY="$INPUT_API_KEY"
        fi

        # Build command array
        CMD=(census --quiet)

        case "$INPUT_COMMAND" in
          scan)
            CMD+=("scan" "$INPUT_TARGET" "--manifest" "$INPUT_MANIFEST" "--source" "$INPUT_SOURCE")
            if [ "$EFF_FORMAT" = "json" ] || [ "$EFF_FORMAT" = "jsonl" ]; then
              CMD+=("--json")
            fi
            ;;
          integrity)
            CMD+=("integrity" "$INPUT_MANIFEST")
            if [ "$EFF_FORMAT" = "json" ]; then
              CMD+=("--json")
            elif [ "$EFF_FORMAT" = "jsonl" ]; then
              CMD+=("--format" "jsonl")
            fi
            ;;
          compare)
            CMD+=("compare" "$INPUT_TARGET" "--manifest" "$INPUT_MANIFEST")
            if [ "$EFF_FORMAT" = "sarif" ]; then
              CMD+=("--format" "sarif" "--output" "$SARIF_PATH")
            elif [ "$EFF_FORMAT" = "json" ]; then
              CMD+=("--json")
            elif [ "$EFF_FORMAT" = "jsonl" ]; then
              CMD+=("--format" "jsonl")
            fi
            ;;
          bulk-scan)
            CMD+=("bulk-scan" "$INPUT_TARGET" "--manifest" "$INPUT_MANIFEST" "--source" "$INPUT_SOURCE")
            if [ "$EFF_FORMAT" = "json" ]; then
              CMD+=("--json")
            elif [ "$EFF_FORMAT" = "jsonl" ]; then
              CMD+=("--format" "jsonl")
            fi
            ;;
          *)
            echo "::error::Unknown command: use scan, integrity, compare, or bulk-scan"
            exit 1
            ;;
        esac

        if [ "$INPUT_EXIT_ZERO" = "true" ]; then
          if [ "$INPUT_COMMAND" = "compare" ] || [ "$INPUT_COMMAND" = "bulk-scan" ]; then
            CMD+=("--exit-zero")
          fi
        fi

        # Append extra args (word-split intentionally)
        if [ -n "$INPUT_EXTRA_ARGS" ]; then
          # shellcheck disable=SC2086
          CMD+=($INPUT_EXTRA_ARGS)
        fi

        echo "::group::Census command"
        echo "Running: ${CMD[*]}"
        echo "::endgroup::"

        "${CMD[@]}" > "$OUTPUT_PATH" 2>&1
        CENSUS_EXIT=$?

        cat "$OUTPUT_PATH"

        # Set outputs
        echo "exit-code=${CENSUS_EXIT}" >> "$GITHUB_OUTPUT"
        echo "output-file=${OUTPUT_PATH}" >> "$GITHUB_OUTPUT"

        if [ -f "$SARIF_PATH" ]; then
          echo "sarif-file=${SARIF_PATH}" >> "$GITHUB_OUTPUT"
        else
          echo "sarif-file=" >> "$GITHUB_OUTPUT"
        fi

        # Write step summary
        {
          echo "### CertiSigma Census — \`${INPUT_COMMAND}\`"
          echo ""
          echo "| Field | Value |"
          echo "|-------|-------|"
          echo "| Command | \`${INPUT_COMMAND}\` |"
          echo "| Target | \`${INPUT_TARGET}\` |"
          echo "| Format | \`${EFF_FORMAT}\` |"
          echo "| Exit code | \`${CENSUS_EXIT}\` |"
          if [ -f "$SARIF_PATH" ]; then
            echo "| SARIF | Uploaded to Security tab |"
          fi
          echo ""
          if [ "$CENSUS_EXIT" -eq 0 ]; then
            echo "> **Result:** No issues found."
          elif [ "$INPUT_COMMAND" = "compare" ] || [ "$INPUT_COMMAND" = "bulk-scan" ]; then
            echo "> **Result:** Matches found — potential data exfiltration detected."
          elif [ "$INPUT_COMMAND" = "integrity" ]; then
            echo "> **Result:** Integrity violations detected."
          else
            echo "> **Result:** Census exited with code ${CENSUS_EXIT}."
          fi
        } >> "$GITHUB_STEP_SUMMARY"

        # Exit with Census exit code (or 0 if exit-zero is set)
        if [ "$INPUT_EXIT_ZERO" = "true" ]; then
          exit 0
        fi
        exit "$CENSUS_EXIT"

    - name: Upload SARIF
      if: >-
        always()
        && steps.run-census.outputs.sarif-file != ''
        && inputs.upload-sarif == 'true'
      uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: ${{ steps.run-census.outputs.sarif-file }}
        category: certisigma-census
