From ac5d3b08ca8c2966c199067f271ae6c1eee39b67 Mon Sep 17 00:00:00 2001 From: Timo Behrendt Date: Wed, 11 Feb 2026 19:54:00 +0100 Subject: [PATCH] feat: mvp (#1) Reviewed-on: https://gitea.t000-n.de/t.behrendt/tas-actions/pulls/1 Co-authored-by: Timo Behrendt Co-committed-by: Timo Behrendt --- .gitea/workflows/cd.yaml | 25 ++++++++++++++ README.md | 28 ++++++++++++++++ renovate.json | 4 +++ tas-upload-sarif/README.md | 48 +++++++++++++++++++++++++++ tas-upload-sarif/action.yml | 66 +++++++++++++++++++++++++++++++++++++ 5 files changed, 171 insertions(+) create mode 100644 .gitea/workflows/cd.yaml create mode 100644 renovate.json create mode 100644 tas-upload-sarif/README.md create mode 100644 tas-upload-sarif/action.yml diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml new file mode 100644 index 0000000..2c8d5fd --- /dev/null +++ b/.gitea/workflows/cd.yaml @@ -0,0 +1,25 @@ +name: CD + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.2 + with: + fetch-depth: 0 + - name: Increment tag + id: tag + uses: https://gitea.t000-n.de/t.behrendt/conventional-semantic-git-tag-increment@af46017d0af5fd6af4425f8e6961f14280a1acd1 # 0.1.26 + with: + token: ${{ secrets.GITEA_TOKEN }} + - name: Push tag + uses: ./release-git-tag + with: + tag: ${{ steps.tag.outputs.new-tag }} diff --git a/README.md b/README.md index 754d807..b482ac2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,30 @@ # tas-actions +Reusable GitHub Actions for [TAS (Tea Advanced Security)](https://github.com/go-gitea/gitea): upload SARIF reports and gate CI on the API response. + +## Actions + +### [tas-upload-sarif](tas-upload-sarif/) + +Uploads a SARIF report from a file to TAS and **fails the job** if the API returns `allowed: false`. + +**Example workflow** (e.g. after a security scan that produces SARIF): + +```yaml +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # Run your scanner and produce SARIF (e.g. to results.sarif) + # - run: ./run-scanner --output results.sarif + + - name: Upload SARIF to TAS and gate + uses: your-org/tas-actions/tas-upload-sarif@v1 + with: + tas-base-url: 'https://tas.example.com' + sarif-file: 'results.sarif' +``` + +See [tas-upload-sarif/README.md](tas-upload-sarif/README.md) for all inputs and options. \ No newline at end of file diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..f275018 --- /dev/null +++ b/renovate.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["local>t.behrendt/renovate-configs:action"] +} diff --git a/tas-upload-sarif/README.md b/tas-upload-sarif/README.md new file mode 100644 index 0000000..b64e2bd --- /dev/null +++ b/tas-upload-sarif/README.md @@ -0,0 +1,48 @@ +# TAS Upload SARIF + +Reusable GitHub Action that uploads a SARIF report to [TAS (Tea Advanced Security)](https://github.com/go-gitea/gitea) and **fails the job** if the API responds with `allowed: false` (e.g. new findings above your policy). + +## Inputs + +| Input | Required | Description | +| -------------- | -------- | --------------------------------------------------------- | +| `tas-base-url` | Yes | Base URL of the TAS API (e.g. `https://tas.example.com`) | +| `sarif-file` | Yes | Path to the SARIF report file (JSON) | +| `owner` | No | Repository owner (default: `github.repository_owner`) | +| `repo` | No | Repository name (default: `github.event.repository.name`) | +| `branch` | No | Branch name (default: `github.ref_name`) | + +## Usage + +```yaml +- name: Upload SARIF to TAS and gate + uses: https://gitea.t000-n.de/t.behrendt/tas-actions/tas-upload-sarif@v1 + with: + tas-base-url: "https://tas.example.com" + sarif-file: "results.sarif" +``` + +With explicit owner/repo/branch (e.g. for monorepos or custom refs): + +```yaml +- uses: [your-org/tas-actions/tas-upload-sarif@v1](https://gitea.t000-n.de/t.behrendt/tas-actions/tas-upload-sarif@v1) + with: + tas-base-url: ${{ vars.TAS_BASE_URL }} + sarif-file: 'scan-output.sarif' + owner: ${{ github.repository_owner}} + repo: ${{ github.event.repository.name }} + branch: ${{ github.head_ref }} +``` + +## Behavior + +1. **POST**s the SARIF file to `{tas-base-url}/repos/{owner}/{repo}/branches/{branch}/reports`. +2. On HTTP non-200, the step fails and prints the response body. +3. On success, reads the JSON response: + - If `allowed` is `true`, the step succeeds. + - If `allowed` is `false`, the step **fails** and prints `reason` and the full response (e.g. `new_critical`, `new_high`, `new_findings`). + +## Requirements + +- **GitHub-hosted runners**: `curl` and `jq` are available by default. +- **Self-hosted runners**: ensure `curl` and `jq` are installed. diff --git a/tas-upload-sarif/action.yml b/tas-upload-sarif/action.yml new file mode 100644 index 0000000..53e7362 --- /dev/null +++ b/tas-upload-sarif/action.yml @@ -0,0 +1,66 @@ +name: 'TAS Upload SARIF' +description: 'Upload a SARIF report to TAS (Tea Advanced Security) and fail the job if gating returns allowed: false' +inputs: + tas-base-url: + description: 'Base URL of the TAS API (e.g. https://tas.example.com)' + required: true + sarif-file: + description: 'Path to the SARIF report file (JSON)' + required: true + owner: + description: 'Repository owner (default: GitHub repository owner)' + required: false + repo: + description: 'Repository name (default: GitHub repository name)' + required: false + branch: + description: 'Branch name (default: current ref name, e.g. main)' + required: false +runs: + using: 'composite' + steps: + - name: Upload SARIF to TAS and gate + shell: bash + env: + OWNER: ${{ inputs.owner || github.repository_owner }} + REPO: ${{ inputs.repo || github.event.repository.name }} + BRANCH: ${{ inputs.branch || github.ref_name }} + BASE_URL: ${{ inputs.tas-base-url }} + SARIF_FILE: ${{ inputs.sarif-file }} + run: | + BASE_URL="${BASE_URL%/}" + URL="${BASE_URL}/repos/${OWNER}/${REPO}/branches/${BRANCH}/reports" + echo "Uploading SARIF to TAS: $URL" + + if [[ ! -f "$SARIF_FILE" ]]; then + echo "::error::SARIF file not found: $SARIF_FILE" + exit 1 + fi + + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$URL" \ + -H "Content-Type: application/json" \ + -d @"$SARIF_FILE") + + HTTP_BODY=$(echo "$RESPONSE" | head -n -1) + HTTP_CODE=$(echo "$RESPONSE" | tail -n 1) + + if [[ "$HTTP_CODE" != "200" ]]; then + echo "::error::TAS API returned HTTP $HTTP_CODE" + echo "$HTTP_BODY" | head -20 + exit 1 + fi + + ALLOWED=$(echo "$HTTP_BODY" | jq -r '.allowed') + REASON=$(echo "$HTTP_BODY" | jq -r '.reason // empty') + + if [[ "$ALLOWED" != "true" ]]; then + echo "::error::TAS gating failed (allowed: false). $REASON" + echo "::error::new_critical/new_high/new_medium/new_low are in the API response." + echo "$HTTP_BODY" | jq '.' + exit 1 + fi + + echo "TAS gating passed (allowed: true)." + if [[ -n "$REASON" ]]; then + echo "$REASON" + fi