From 2f0fc8a2a00c996cfd2b6747d8f8421b4814aeeb Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Mon, 16 Mar 2026 15:10:14 +0100 Subject: [PATCH 1/7] feat: add qb-security workflow, remove mobsfscan and nuclei Adds reusable QB Security workflow that scans for invisible Unicode characters (GlassWorm / Trojan Source supply chain attacks) using the new QuickBirdEng/actions/detect-invisible-unicode action. Removes the unused mobsfscan-json and nuclei-scan workflow definitions. --- .github/actions/mobsfscan-json.yml | 14 ----------- .github/actions/nuclei-scan.yml | 26 ------------------- .github/workflows/qb-security.yml | 40 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 .github/actions/mobsfscan-json.yml delete mode 100644 .github/actions/nuclei-scan.yml create mode 100644 .github/workflows/qb-security.yml diff --git a/.github/actions/mobsfscan-json.yml b/.github/actions/mobsfscan-json.yml deleted file mode 100644 index fccd9a7..0000000 --- a/.github/actions/mobsfscan-json.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Mobsfscan -description: A scanner for mobile applications -#outputs: -# random-number: -# description: "Random number" -# value: ${{ steps.random-number-generator.outputs.random-number }} -runs: - using: "composite" - steps: - - name: mobsfscan - uses: MobSF/mobsfscan@main - with: - args: '. --json' -# manual evaluation of the result is required here (check if there exists a solution already for it) diff --git a/.github/actions/nuclei-scan.yml b/.github/actions/nuclei-scan.yml deleted file mode 100644 index d9f96cc..0000000 --- a/.github/actions/nuclei-scan.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Nuclei Scan -description: A website scanner -input: - target: - required: true - type: string - -runs: - using: "composite" - steps: - - uses: actions/checkout@v2 - - - name: Nuclei - Vulnerability Scan - uses: projectdiscovery/nuclei-action@main - with: - target: ${{ inputs.target }} - - name: GitHub Workflow artifacts - uses: actions/upload-artifact@v2 - with: - name: nuclei.log - path: nuclei.log - - - name: GitHub Security Dashboard Alerts update - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: nuclei.sarif diff --git a/.github/workflows/qb-security.yml b/.github/workflows/qb-security.yml new file mode 100644 index 0000000..e9cce69 --- /dev/null +++ b/.github/workflows/qb-security.yml @@ -0,0 +1,40 @@ +name: QB Security + +on: + workflow_call: + inputs: + runs-on: + description: 'Runner label for the scan job.' + type: string + default: 'ubuntu-latest' + search-directory: + description: 'Directory to scan recursively for invisible Unicode characters.' + type: string + default: '.' + exclude-dirs: + description: 'Comma-separated directory names to exclude from the scan.' + type: string + default: '.git,node_modules,.idea,build,dist' + exclude-patterns: + description: 'Comma-separated file glob patterns to exclude from the scan.' + type: string + default: '*.png,*.jpg,*.jpeg,*.gif,*.ico,*.pdf,*.zip,*.tar,*.gz,*.bin,*.dill' + fail-on-found: + description: 'Fail the workflow when invisible Unicode characters are found.' + type: boolean + default: true + +jobs: + unicode-security-scan: + runs-on: ${{ inputs.runs-on }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Detect Invisible Unicode + uses: QuickBirdEng/actions/detect-invisible-unicode@main + with: + search-directory: ${{ inputs.search-directory }} + exclude-dirs: ${{ inputs.exclude-dirs }} + exclude-patterns: ${{ inputs.exclude-patterns }} + fail-on-found: ${{ inputs.fail-on-found }} From f388332ce2d80d11fde05fc44c4b0f5d024263c5 Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Mon, 16 Mar 2026 15:12:16 +0100 Subject: [PATCH 2/7] feat: remove commented-out MobSF scan jobs from flutter-security-checks --- .github/workflows/flutter-security-checks.yml | 86 ------------------- 1 file changed, 86 deletions(-) diff --git a/.github/workflows/flutter-security-checks.yml b/.github/workflows/flutter-security-checks.yml index 88ef070..1befb92 100644 --- a/.github/workflows/flutter-security-checks.yml +++ b/.github/workflows/flutter-security-checks.yml @@ -61,89 +61,3 @@ jobs: head: HEAD extra_args: --debug --only-verified ${{ env.TH_EXCLUDE_PATHS }} ${{ env.TH_INCLUDE_PATHS }} - # https://github.com/fundacaocerti/mobsf-action - # https://github.com/fundacaocerti/mobsf-action/pull/16 - # https://github.com/fundacaocerti/mobsf-action/pull/17 -# mobsf_scan_only: -# runs-on: [ self-hosted ] -# steps: -# - name: Checkout Repo -# uses: actions/checkout@master -# - name: Set-up Java -# uses: actions/setup-java@v1 -# with: -# java-version: '12.x' -# - name: Set-up Flutter -# uses: subosito/flutter-action@v1 -# with: -# flutter-version: ${{ inputs.flutterVersion }} -# - name: Flutter Install Dependencies -# run: flutter pub get -# - name: Flutter Build -# run: flutter build apk -## - name: Fix Permissions for MobSF Docker -## run: | -## set -e -## sudo mkdir -p /home/runner/work/_temp/_github_home -## sudo chown -R 9901:9901 /home/runner/work/_temp/_github_home -## sudo mkdir -p /home/runner/work/$REPO_NAME/$REPO_NAME -## sudo chown -R 9901:9901 /home/runner/work/$REPO_NAME/$REPO_NAME -## env: -## REPO_NAME: ${{ github.event.repository.name }} -# - name: Run MobSF Analysis -# uses: fundacaocerti/mobsf-action@v1.7.2 -# env: -# INPUT_FILE_NAME: build/app/outputs/apk/app.apk -# SCAN_TYPE: apk -# OUTPUT_FILE_NAME: mobsf-report -## - name: Cleanup MobSF Permissions -## run: | -## set -e -## sudo chown -R runner:docker /home/runner/work/_temp/_github_home -## sudo chown -R runner:docker /home/runner/work/$REPO_NAME/$REPO_NAME -## env: -## REPO_NAME: ${{ github.event.repository.name }} -# - name: Upload MobSF Analysis PDF Result -# uses: actions/upload-artifact@v2 -# with: -# name: mobsf-report-apk.pdf -# path: mobsf-report-apk.pdf -# - name: Upload MobSF Analysis JSON Result -# uses: actions/upload-artifact@v2 -# with: -# name: mobsf-report-apk.json -# path: mobsf-report-apk.json - -# mobsf_scan_ios: -# runs-on: [ self-hosted, ios ] -# steps: -# - name: Checkout Repo -# uses: actions/checkout@master -# - name: Set-up Java -# uses: actions/setup-java@v1 -# with: -# java-version: '12.x' -# - name: Set-up Flutter -# uses: subosito/flutter-action@v1 -# with: -# flutter-version: ${{ inputs.flutterVersion }} -# - name: Flutter Install Dependencies -# run: flutter pub get -# - name: Flutter Build -# run: flutter build ipa -# - name: Run MobSF Analysis -# uses: fundacaocerti/mobsf-action@v1.7.2 -# env: -# INPUT_FILE_NAME: build/ios/archive/Runner.xcarchive -# SCAN_TYPE: ipa -# OUTPUT_FILE_NAME: mobsf-report -# - name: Upload MobSF Analysis PDF Result -# uses: actions/upload-artifact@v2 -# with: -# name: mobsf-report-ipa.pdf -# path: mobsf-report-ipa.pdf -# - name: Upload MobSF Analysis JSON Result -# uses: actions/upload-artifact@v2 -# with: -# name: mobsf-report-ipa.json -# path: mobsf-report-ipa.json From 7225eb7c88f528193bf6e6e716810e784c08cb74 Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Mon, 16 Mar 2026 15:40:22 +0100 Subject: [PATCH 3/7] feat: remove flutter-security-checks workflow Jobs moved into standalone actions in the actions repo: - secret-scan (QuickBirdEng/actions/secret-scan) - trufflehog-scan (QuickBirdEng/actions/trufflehog-scan) --- .github/workflows/flutter-security-checks.yml | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 .github/workflows/flutter-security-checks.yml diff --git a/.github/workflows/flutter-security-checks.yml b/.github/workflows/flutter-security-checks.yml deleted file mode 100644 index 1befb92..0000000 --- a/.github/workflows/flutter-security-checks.yml +++ /dev/null @@ -1,63 +0,0 @@ -on: - workflow_call: - inputs: - flutter-version: - required: true - type: string - relative-include-path: - required: false - type: string - relative-exclude-path: - required: false - type: string - git-lfs: - description: "Checkout the project with git lfs" - type: boolean - default: false - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - secret_scanner: - runs-on: [ self-hosted ] - steps: - - uses: actions/checkout@v2 - with: - lfs: ${{ inputs.git-lfs }} - - run: echo ${{inputs.relative-include-path}} ${{inputs.relative-exclude-path}} - - uses: max/secret-scan@master - with: - include_path: '${{ inputs.relative-include-path }}' - exclude_path: '${{ inputs.relative-exclude-path }}' -# - run: echo >\n -# !!!!!!!!!!!!!! Message from the security officer !!!!!!!!!!!!!!\n -# Should there be findings, think about if that upload-key should be put into source code?\n -# \n -# If the upload-key can be in the source code, put the path of that file into a text file and provide that path as an input parameter for the security checks job \"relative-exclude-path":" .pathtofile/secret_check_ignore_paths.txt\"\n -# If the upload-key should not be in the source code, consider putting it into the GitHub secrets\n - - trufflehog-package-analysis: - runs-on: [ self-hosted ] - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: set env variables for check - if: inputs.relative-exclude-path != '' - run: | - echo 'TH_EXCLUDE_PATHS=--exclude-paths=${{ inputs.relative-exclude-path }}' >> $GITHUB_ENV - - name: set env variables for check - if: inputs.relative-include-path != '' - run: | - echo 'TH_INCLUDE_PATHS=--include-paths=${{ inputs.relative-include-path }}' >> $GITHUB_ENV - - name: TruffleHog OSS - uses: trufflesecurity/trufflehog@main - with: - path: ./ - base: ${{ github.event.repository.default_branch }} - head: HEAD - extra_args: --debug --only-verified ${{ env.TH_EXCLUDE_PATHS }} ${{ env.TH_INCLUDE_PATHS }} - From caf24fccaf1b4ba665633ae4e664068659c0d231 Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Mon, 16 Mar 2026 15:42:50 +0100 Subject: [PATCH 4/7] fix: use default-k8s-runner as default runner --- .github/workflows/qb-security.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/qb-security.yml b/.github/workflows/qb-security.yml index e9cce69..63e5de7 100644 --- a/.github/workflows/qb-security.yml +++ b/.github/workflows/qb-security.yml @@ -6,7 +6,7 @@ on: runs-on: description: 'Runner label for the scan job.' type: string - default: 'ubuntu-latest' + default: 'default-k8s-runner' search-directory: description: 'Directory to scan recursively for invisible Unicode characters.' type: string From 4aab554fe37b25a320f5d8b870c77a3a7559d8bd Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Tue, 17 Mar 2026 07:08:37 +0100 Subject: [PATCH 5/7] Add qb-security docs and workflow table to README --- README.md | 7 ++ docs/qb-security/explanation.md | 78 +++++++++++++++++++ .../qb-security-calling-example.yml | 9 +++ 3 files changed, 94 insertions(+) create mode 100644 docs/qb-security/explanation.md create mode 100644 docs/qb-security/qb-security-calling-example.yml diff --git a/README.md b/README.md index 8d1a48a..264060c 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,10 @@ The examples in [The docs folder](docs/) are provided as examples of calling wor From the root of your project repo, place the calling workflow in `.github/workflows`. +## Workflows + +| Workflow | Description | Docs | +|---|---|---| +| `qb-security` | Invisible Unicode detection + verified secret scanning on pull requests | [docs/qb-security](docs/qb-security/explanation.md) | +| `sanity-requirements` | LoC delta check and branch-ticket check for feature branches | [docs/sanity-requirements](docs/sanity-requirements/explanation.md) | + diff --git a/docs/qb-security/explanation.md b/docs/qb-security/explanation.md new file mode 100644 index 0000000..c302516 --- /dev/null +++ b/docs/qb-security/explanation.md @@ -0,0 +1,78 @@ +# QB Security + +A reusable workflow that runs supply-chain security checks on every pull request. + +## What it checks + +### Invisible Unicode detection + +Scans every source file in the PR for invisible Unicode characters used in two known supply-chain attacks: + +- **GlassWorm** — embeds Unicode Variation Selectors (U+FE00–U+FE0F, U+E0100–U+E01EF) inside commits. The characters are invisible in code editors, terminals, and GitHub's diff view, allowing payloads to hide inside what appear to be legitimate changes. +- **Trojan Source** — uses bidirectional control characters (U+202A–U+202E, U+2066–U+2069) to visually reorder code during review so that what a reviewer sees differs from what the compiler executes. + +Binary files are skipped automatically. Findings are emitted as inline PR annotations pointing to the exact file and line. + +### Secret scanning (TruffleHog) + +Scans the commits introduced by the PR for verified secrets using [TruffleHog OSS](https://github.com/trufflesecurity/trufflehog). Only **verified** secrets (credentials that TruffleHog can confirm are active against the real service) are reported, which eliminates false positives from example keys or already-rotated credentials. Findings are emitted as inline PR annotations. + +## Usage + +```yaml +name: Security + +on: + pull_request: + +jobs: + security: + uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main + secrets: inherit +``` + +That is the minimal setup. `secrets: inherit` is required for the TruffleHog job to access `GITHUB_TOKEN`. + +## Inputs + +All inputs are optional. Defaults are intentionally broad so most repos need no configuration. + +| Input | Type | Default | Description | +|---|---|---|---| +| `runs-on` | string | `default-k8s-runner` | Runner label | +| `search-directory` | string | `.` | Root directory for the invisible Unicode scan | +| `exclude-dirs` | string | `.git,node_modules,.idea,build,dist` | Directory names to skip | +| `exclude-patterns` | string | `*.png,*.jpg,*.jpeg,*.gif,*.ico,*.pdf,*.zip,*.tar,*.gz,*.bin,*.dill` | File glob patterns to skip | +| `fail-on-found` | boolean | `true` | Fail the check when invisible Unicode is detected | + +## Typical overrides + +**Exclude an additional generated directory:** +```yaml +jobs: + security: + uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main + with: + exclude-dirs: '.git,node_modules,.idea,build,dist,generated' + secrets: inherit +``` + +**Run on a self-hosted runner:** +```yaml +jobs: + security: + uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main + with: + runs-on: 'self-hosted' + secrets: inherit +``` + +**Audit mode (report but do not fail):** +```yaml +jobs: + security: + uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main + with: + fail-on-found: false + secrets: inherit +``` diff --git a/docs/qb-security/qb-security-calling-example.yml b/docs/qb-security/qb-security-calling-example.yml new file mode 100644 index 0000000..f3be3cb --- /dev/null +++ b/docs/qb-security/qb-security-calling-example.yml @@ -0,0 +1,9 @@ +name: Security + +on: + pull_request: + +jobs: + security: + uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main + secrets: inherit From a6e3e7f445aecf3edbac5e48d3acf1a794c6058c Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Tue, 17 Mar 2026 07:16:33 +0100 Subject: [PATCH 6/7] Add flutter-packages to workflow table in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 264060c..c8ed979 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,5 @@ From the root of your project repo, place the calling workflow in `.github/workf |---|---|---| | `qb-security` | Invisible Unicode detection + verified secret scanning on pull requests | [docs/qb-security](docs/qb-security/explanation.md) | | `sanity-requirements` | LoC delta check and branch-ticket check for feature branches | [docs/sanity-requirements](docs/sanity-requirements/explanation.md) | +| `flutter-package-branch` / `flutter-package-release` | Flutter lint, test, and Slack notification for package repos | [docs/flutter-packages](docs/flutter-packages/explanation.md) | From f7a07ffba1196c65ce44c024f7c5d7557643bd24 Mon Sep 17 00:00:00 2001 From: Klaus Niedermair Date: Tue, 17 Mar 2026 16:12:22 +0100 Subject: [PATCH 7/7] Update qb-security docs: no secrets needed for TruffleHog TruffleHog action now downloads its own binary and scans the local checkout, so neither secrets: inherit nor GITHUB_TOKEN is required in the calling workflow. --- docs/qb-security/explanation.md | 15 +++++++++++++-- docs/qb-security/qb-security-calling-example.yml | 11 ++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/qb-security/explanation.md b/docs/qb-security/explanation.md index c302516..8fe1917 100644 --- a/docs/qb-security/explanation.md +++ b/docs/qb-security/explanation.md @@ -26,12 +26,23 @@ on: pull_request: jobs: + # Invisible Unicode detection — no secrets needed security: uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main - secrets: inherit + + # Secret scanning — separate job, passes only the token it needs + trufflehog-scan: + runs-on: default-k8s-runner + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: QuickBirdEng/actions/trufflehog-scan@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -That is the minimal setup. `secrets: inherit` is required for the TruffleHog job to access `GITHUB_TOKEN`. +The unicode scan requires no secrets. TruffleHog runs as a separate job and receives only `GITHUB_TOKEN` — no `secrets: inherit` needed. ## Inputs diff --git a/docs/qb-security/qb-security-calling-example.yml b/docs/qb-security/qb-security-calling-example.yml index f3be3cb..38f7494 100644 --- a/docs/qb-security/qb-security-calling-example.yml +++ b/docs/qb-security/qb-security-calling-example.yml @@ -6,4 +6,13 @@ on: jobs: security: uses: QuickBirdEng/workflows/.github/workflows/qb-security.yml@main - secrets: inherit + + trufflehog-scan: + runs-on: default-k8s-runner + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: QuickBirdEng/actions/trufflehog-scan@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}