Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/self-pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,47 @@ jobs:
with:
files: ${{ needs.changed-files.outputs.composite_files }}

# ----------------- CodeQL Analysis -----------------
codeql:
name: CodeQL Analysis
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: changed-files
if: needs.changed-files.outputs.action_files != ''
permissions:
contents: read
security-events: write
pull-requests: write
actions: read
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Generate CodeQL config for changed files
id: codeql-config
uses: ./src/security/codeql-config
with:
changed-paths: ${{ needs.changed-files.outputs.action_files }}

- name: Initialize CodeQL
if: steps.codeql-config.outputs.skip != 'true'
uses: ./src/security/codeql-init
with:
languages: actions
config-file: ${{ steps.codeql-config.outputs.config-file }}

- name: Perform CodeQL Analysis
if: steps.codeql-config.outputs.skip != 'true'
uses: ./src/security/codeql-analyze
with:
category: '/language:actions'

- name: Post CodeQL Results to PR
if: always() && github.event_name == 'pull_request' && steps.codeql-config.outputs.skip != 'true'
uses: ./src/security/codeql-reporter
with:
github-token: ${{ secrets.MANAGE_TOKEN || github.token }}
languages: actions

# ----------------- Lint Report -----------------
lint-report:
name: Lint Report
Expand Down
43 changes: 43 additions & 0 deletions src/security/codeql-analyze/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><img src="https://github.com/LerianStudio.png" width="72" alt="Lerian" /></td>
<td><h1>codeql-analyze</h1></td>
</tr>
</table>

Composite action that wraps [`github/codeql-action/analyze`](https://github.com/github/codeql-action) to perform CodeQL analysis and upload SARIF results to the GitHub Security tab. Designed to be paired with [`codeql-init`](../codeql-init/).

## Inputs

| Input | Description | Required | Default |
|---|---|:---:|---|
| `category` | Category for SARIF results used for deduplication (e.g., `/language:actions`) | Yes | — |
| `output` | Output directory for SARIF files | No | `../results` |

## Usage

### Basic (paired with codeql-init)

```yaml
steps:
- uses: actions/checkout@v6

- name: Initialize CodeQL
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-init@v1.x.x
with:
languages: actions

- name: Perform CodeQL Analysis
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-analyze@v1.x.x
with:
category: '/language:actions'
```

## Permissions required

```yaml
permissions:
contents: read
security-events: write # required for SARIF upload to GitHub Security tab
actions: read # required for workflows using private repositories
```
Comment thread
bedatty marked this conversation as resolved.
20 changes: 20 additions & 0 deletions src/security/codeql-analyze/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CodeQL Analyze
description: Perform CodeQL analysis and upload SARIF results to GitHub Security tab

inputs:
category:
description: 'Category for SARIF results used for deduplication (e.g., /language:actions)'
required: true
output:
description: 'Output directory for SARIF files'
required: false
default: '../results'

runs:
using: composite
steps:
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: ${{ inputs.category }}
output: ${{ inputs.output }}
45 changes: 45 additions & 0 deletions src/security/codeql-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><img src="https://github.com/LerianStudio.png" width="72" alt="Lerian" /></td>
<td><h1>codeql-config</h1></td>
</tr>
</table>

Composite action that generates a dynamic CodeQL configuration file scoped to changed paths. Ensures CodeQL analyzes only modified files in PRs instead of the entire repository. Designed to run before [`codeql-init`](../codeql-init/).

## Inputs

| Input | Description | Required | Default |
|---|---|:---:|---|
| `changed-paths` | Comma or newline-separated list of changed file paths | Yes | — |
| `output-file` | Path where the generated config file will be written | No | `.github/codeql-config-pr.yml` |

## Outputs

| Output | Description |
|---|---|
| `config-file` | Path to the generated CodeQL config file |
| `skip` | `true` if no paths were resolved and CodeQL should be skipped |

## Usage

### With changed-workflows (this repo)

```yaml
- name: Generate CodeQL config
id: codeql-config
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-config@v1.x.x
Comment thread
bedatty marked this conversation as resolved.
with:
changed-paths: ${{ needs.changed-files.outputs.action_files }}

- name: Initialize CodeQL
if: steps.codeql-config.outputs.skip != 'true'
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-init@v1.x.x
with:
languages: actions
config-file: ${{ steps.codeql-config.outputs.config-file }}
```

## Permissions required

No special permissions needed — this composite only generates a file.
62 changes: 62 additions & 0 deletions src/security/codeql-config/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CodeQL Config Generator
description: Generates a dynamic CodeQL configuration file scoped to changed paths for PR-only analysis.

inputs:
changed-paths:
description: 'Comma or newline-separated list of changed file paths or directories to scope the analysis'
required: true
output-file:
description: 'Path where the generated CodeQL config file will be written'
required: false
default: '.github/codeql-config-pr.yml'
outputs:
config-file:
description: 'Path to the generated CodeQL config file'
value: ${{ steps.generate.outputs.config-file }}
skip:
description: 'true if no paths were resolved and CodeQL should be skipped'
value: ${{ steps.generate.outputs.skip }}
Comment thread
bedatty marked this conversation as resolved.

runs:
using: composite
steps:
- name: Generate CodeQL config
id: generate
shell: bash
env:
CONFIG_FILE: ${{ inputs.output-file }}
RAW_PATHS: ${{ inputs.changed-paths }}
run: |
# Normalize separators (comma or newline) to newline
PATHS=$(printf '%s' "$RAW_PATHS" | tr ',' '\n' | sed '/^[[:space:]]*$/d' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')

if [ -z "$PATHS" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "No paths provided — skipping CodeQL config generation"
exit 0
fi
Comment thread
bedatty marked this conversation as resolved.

# Remove duplicates and empty lines
PATHS=$(echo "$PATHS" | sort -u | sed '/^[[:space:]]*$/d')

if [ -z "$PATHS" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "No paths resolved — skipping CodeQL config generation"
exit 0
fi

# Generate config
{
echo "paths:"
echo "$PATHS" | while IFS= read -r p; do
echo " - '$p'"
done
} > "$CONFIG_FILE"
Comment thread
bedatty marked this conversation as resolved.

echo "skip=false" >> "$GITHUB_OUTPUT"
echo "config-file=$CONFIG_FILE" >> "$GITHUB_OUTPUT"

count=$(echo "$PATHS" | wc -l | tr -d ' ')
echo "::group::CodeQL config — $count path(s)"
cat "$CONFIG_FILE"
echo "::endgroup::"
61 changes: 61 additions & 0 deletions src/security/codeql-init/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><img src="https://github.com/LerianStudio.png" width="72" alt="Lerian" /></td>
<td><h1>codeql-init</h1></td>
</tr>
</table>

Composite action that wraps [`github/codeql-action/init`](https://github.com/github/codeql-action) to initialize CodeQL analysis for specified languages. Uses the `security-extended` query suite by default for broader coverage. Designed to be paired with [`codeql-analyze`](../codeql-analyze/).

## Inputs

| Input | Description | Required | Default |
|---|---|:---:|---|
| `languages` | Languages to analyze (comma-separated, e.g., `actions`, `javascript-typescript`, `go`) | Yes | — |
| `queries` | Query suite to use (`security-standard`, `security-extended`, `security-and-quality`) | No | `security-extended` |
| `config-file` | Path to CodeQL configuration file | No | — |

## Usage

### Basic (GitHub Actions YAML analysis)

```yaml
jobs:
codeql:
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: read
security-events: write
actions: read
steps:
- uses: actions/checkout@v6

- name: Initialize CodeQL
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-init@v1.x.x
with:
languages: actions

- name: Analyze
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-analyze@v1.x.x
with:
category: '/language:actions'
```

### Multiple languages

```yaml
- name: Initialize CodeQL
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-init@v1.x.x
with:
languages: javascript-typescript
queries: security-and-quality
```

## Permissions required

```yaml
permissions:
contents: read
security-events: write
actions: read # required for workflows using private repositories
```
25 changes: 25 additions & 0 deletions src/security/codeql-init/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CodeQL Init
description: Initialize CodeQL analysis for specified languages with configurable query suite

inputs:
languages:
description: 'Languages to analyze (comma-separated, e.g., actions, javascript-typescript, go)'
required: true
queries:
description: 'Query suite to use (security-standard, security-extended, security-and-quality)'
required: false
default: 'security-extended'
config-file:
description: 'Path to CodeQL configuration file'
required: false
default: ''

runs:
using: composite
steps:
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ inputs.languages }}
queries: ${{ inputs.queries }}
config-file: ${{ inputs.config-file || '' }}
60 changes: 60 additions & 0 deletions src/security/codeql-reporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><img src="https://github.com/LerianStudio.png" width="72" alt="Lerian" /></td>
<td><h1>codeql-reporter</h1></td>
</tr>
</table>

Composite action that reads CodeQL SARIF output and posts a formatted security report as a PR comment. Uses an idempotent comment strategy (updates existing comment on re-runs). Designed to run after [`codeql-analyze`](../codeql-analyze/).

## Inputs

| Input | Description | Required | Default |
|---|---|:---:|---|
| `github-token` | GitHub token with `pull-requests:write` and `issues:write` permissions | Yes | — |
| `sarif-path` | Path to the CodeQL SARIF output directory | No | `../results` |
| `languages` | Comma-separated list of languages analyzed (for display) | Yes | — |
| `fail-on-findings` | Fail the step when security findings are detected (`true`/`false`) | No | `false` |

## Outputs

| Output | Description |
|---|---|
| `has-findings` | `true` if any CodeQL findings were detected |
| `findings-count` | Total number of CodeQL findings |

## Usage

### After CodeQL analysis (paired with codeql-init and codeql-analyze)

```yaml
steps:
- uses: actions/checkout@v6

- name: Initialize CodeQL
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-init@v1.x.x
with:
languages: go

- name: Perform CodeQL Analysis
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-analyze@v1.x.x
with:
category: '/language:go'

- name: Post CodeQL Results to PR
if: always() && github.event_name == 'pull_request'
uses: LerianStudio/github-actions-shared-workflows/src/security/codeql-reporter@v1.x.x
with:
github-token: ${{ github.token }}
languages: go
```

## Permissions required

```yaml
permissions:
contents: read
security-events: write
pull-requests: write
actions: read
```
Loading