Skip to content

Commit f03ffb7

Browse files
GottZclaude
andcommitted
security: SHA-pin all Actions, permissions deny-all, Guard tx wrapping
CI/CD hardening from 10-agent security armada: - All 11 GitHub Actions pinned to immutable SHA hashes - Top-level permissions: {} (deny-all), per-job minimal grants - Guard batch wrapped in explicit transaction (FOR UPDATE SKIP LOCKED locks now persist through block processing) - Deploy stage removed (local builds, no external SSH) - License label corrected to MPL-2.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7b8ffc2 commit f03ffb7

File tree

3 files changed

+75
-103
lines changed

3 files changed

+75
-103
lines changed

.github/workflows/ci.yml

Lines changed: 37 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -14,62 +14,58 @@ on:
1414
branches: [root]
1515
workflow_dispatch:
1616

17-
# Ein concurrent Run pro Branch/PR — neuerer Run cancelt älteren.
18-
# Deploy-Jobs sind davon ausgenommen (concurrency-group ohne cancel).
17+
# Deny-all default — jeder Job deklariert eigene Permissions.
18+
permissions: {}
19+
1920
concurrency:
2021
group: ${{ github.workflow }}-${{ github.ref }}
2122
cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }}
2223

2324
env:
2425
GO_VERSION: "1.25"
25-
# Registry: GitHub Container Registry (ghcr.io)
2626
REGISTRY: ghcr.io
2727
IMAGE_NAME: ghcr.io/gottz/ctx
2828

2929
jobs:
30-
# ─────────────────────────────────────────────────────────────────────────────
31-
# Stage 1: Lint
32-
# golangci-lint mit der .golangci.yml aus dem Repo (Fallback: Default-Config).
33-
# Läuft auf: alle Trigger. Kein Netzwerk, kein Docker.
34-
# ─────────────────────────────────────────────────────────────────────────────
30+
# ─── Stage 1: Lint ────────────────────────────────────────────────────────────
3531
lint:
3632
name: Lint
3733
runs-on: ubuntu-latest
3834
timeout-minutes: 10
35+
permissions:
36+
contents: read
3937
steps:
4038
- name: Checkout
41-
uses: actions/checkout@v6
39+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4240

4341
- name: Setup Go ${{ env.GO_VERSION }}
44-
uses: actions/setup-go@v6
42+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
4543
with:
4644
go-version: ${{ env.GO_VERSION }}
4745
cache: true
4846
cache-dependency-path: go/go.sum
4947

5048
- name: golangci-lint
51-
uses: golangci/golangci-lint-action@v9
49+
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
5250
with:
5351
version: latest
5452
working-directory: go
5553
args: --timeout=5m
5654
only-new-issues: ${{ github.event_name == 'pull_request' }}
5755

58-
# ─────────────────────────────────────────────────────────────────────────────
59-
# Stage 2: Unit Tests
60-
# go test ./... -short — kein Docker, kein Netzwerk, rein deterministisch.
61-
# Läuft auf: alle Trigger. Race-Detector aktiviert.
62-
# ─────────────────────────────────────────────────────────────────────────────
56+
# ─── Stage 2: Unit Tests ──────────────────────────────────────────────────────
6357
unit-tests:
6458
name: Unit Tests
6559
runs-on: ubuntu-latest
6660
timeout-minutes: 10
61+
permissions:
62+
contents: read
6763
steps:
6864
- name: Checkout
69-
uses: actions/checkout@v6
65+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
7066

7167
- name: Setup Go ${{ env.GO_VERSION }}
72-
uses: actions/setup-go@v6
68+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
7369
with:
7470
go-version: ${{ env.GO_VERSION }}
7571
cache: true
@@ -89,48 +85,41 @@ jobs:
8985
./...
9086
9187
- name: Upload coverage
92-
uses: actions/upload-artifact@v7
88+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
9389
with:
9490
name: coverage-unit
9591
path: go/coverage.out
9692
retention-days: 7
9793

98-
# ─────────────────────────────────────────────────────────────────────────────
99-
# Stage 3: Integration Tests
100-
# Testcontainers mit pgvector/pgvector:pg17.
101-
# Build-Tag: integration. Braucht Docker-in-Docker (ubuntu-latest hat Docker).
102-
# Image-Caching via TESTCONTAINERS_PULL_POLICY + GitHub Actions Cache.
103-
# ─────────────────────────────────────────────────────────────────────────────
94+
# ─── Stage 3: Integration Tests ───────────────────────────────────────────────
10495
integration-tests:
10596
name: Integration Tests
10697
runs-on: ubuntu-latest
10798
timeout-minutes: 20
10899
needs: [unit-tests]
100+
permissions:
101+
contents: read
109102

110103
env:
111-
# Testcontainers: Ryuk (Cleanup-Container) muss im CI-Kontext erreichbar sein.
112104
TESTCONTAINERS_RYUK_DISABLED: "false"
113-
# Nicht bei jedem Pull versuchen — nutze lokalen Cache falls vorhanden.
114105
TESTCONTAINERS_PULL_POLICY: "default"
115-
# Für init-data.sh im Testcontainer
116106
CONTEXT_DB: context_store
117107
CONTEXT_DB_USER: context_user
118108
CONTEXT_DB_PASSWORD: testpassword
119109

120110
steps:
121111
- name: Checkout
122-
uses: actions/checkout@v6
112+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
123113

124114
- name: Setup Go ${{ env.GO_VERSION }}
125-
uses: actions/setup-go@v6
115+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
126116
with:
127117
go-version: ${{ env.GO_VERSION }}
128118
cache: true
129119
cache-dependency-path: go/go.sum
130120

131-
# Docker Layer Cache für testcontainers Images.
132121
- name: Cache testcontainers Docker images
133-
uses: ScribeMD/docker-cache@0.5.0
122+
uses: ScribeMD/docker-cache@fb28c93772363301b8d0a6072ce850224b73f74e # 0.5.0
134123
with:
135124
key: docker-testcontainers-pgvector-pg17-${{ runner.os }}
136125

@@ -147,31 +136,27 @@ jobs:
147136
./...
148137
149138
- name: Upload integration coverage
150-
uses: actions/upload-artifact@v7
139+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
151140
with:
152141
name: coverage-integration
153142
path: go/coverage-integration.out
154143
retention-days: 7
155144

156-
# ─────────────────────────────────────────────────────────────────────────────
157-
# Stage 4: Build Binary
158-
# CGO_ENABLED=0, linux/amd64, statisch gelinkt.
159-
# Artifact wird an Docker-Stage übergeben — kein doppelter Build.
160-
# ─────────────────────────────────────────────────────────────────────────────
145+
# ─── Stage 4: Build Binary ────────────────────────────────────────────────────
161146
build:
162147
name: Build
163148
runs-on: ubuntu-latest
164149
timeout-minutes: 10
165150
needs: [lint, unit-tests]
166-
# Integration Tests sind optional für den Build (laufen parallel).
167-
# Docker-Push wartet auf integration-tests.
151+
permissions:
152+
contents: read
168153

169154
steps:
170155
- name: Checkout
171-
uses: actions/checkout@v6
156+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
172157

173158
- name: Setup Go ${{ env.GO_VERSION }}
174-
uses: actions/setup-go@v6
159+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
175160
with:
176161
go-version: ${{ env.GO_VERSION }}
177162
cache: true
@@ -199,65 +184,51 @@ jobs:
199184
./ctxd --version 2>/dev/null || ./ctxd version 2>/dev/null || true
200185
201186
- name: Upload binary artifact
202-
uses: actions/upload-artifact@v7
187+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
203188
with:
204189
name: ctxd-linux-amd64
205190
path: ctxd
206191
retention-days: 1
207192

208-
# ─────────────────────────────────────────────────────────────────────────────
209-
# Stage 5: Docker Build + Push
210-
# ghcr.io/gottz/ctx — GitHub Container Registry.
211-
# Multi-Layer-Cache: GitHub Actions Cache + inline Registry Cache.
212-
# Tags: root-branch → :latest + :sha-SHORT + :YYYY-MM-DD
213-
# Nur auf root-Branch und workflow_dispatch — nicht auf PRs.
214-
# ─────────────────────────────────────────────────────────────────────────────
193+
# ─── Stage 5: Docker Build + Push ─────────────────────────────────────────────
215194
docker:
216195
name: Docker Build + Push
217196
runs-on: ubuntu-latest
218197
timeout-minutes: 20
219198
needs: [build, integration-tests]
220-
# PRs builden das Image (Smoke Test), pushen aber nicht.
221-
# Push nur auf root-Branch oder manuellem Dispatch.
222199
if: >-
223200
github.ref == 'refs/heads/root' ||
224201
github.event_name == 'workflow_dispatch'
225202
226203
permissions:
227204
contents: read
228-
packages: write # Pflicht für ghcr.io Push
205+
packages: write
229206

230207
steps:
231208
- name: Checkout
232-
uses: actions/checkout@v6
209+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
233210

234211
- name: Download binary artifact
235-
uses: actions/download-artifact@v8
212+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
236213
with:
237214
name: ctxd-linux-amd64
238215

239216
- name: Make binary executable
240217
run: chmod +x ctxd
241218

242-
# Docker Buildx für Cache-Export-Support
243219
- name: Setup Docker Buildx
244-
uses: docker/setup-buildx-action@v4
220+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
245221

246222
- name: Login to GitHub Container Registry
247-
uses: docker/login-action@v4
223+
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
248224
with:
249225
registry: ${{ env.REGISTRY }}
250226
username: ${{ github.actor }}
251-
# GITHUB_TOKEN hat automatisch packages:write auf dem eigenen Repo.
252227
password: ${{ secrets.GITHUB_TOKEN }}
253228

254-
# Meta: Tags + Labels nach OCI-Standard generieren.
255-
# :latest — root-Branch
256-
# :sha-XXXX — deterministische Referenz für Rollback
257-
# :YYYY-MM-DD — human-readable Datum
258229
- name: Docker metadata
259230
id: meta
260-
uses: docker/metadata-action@v6
231+
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
261232
with:
262233
images: ${{ env.IMAGE_NAME }}
263234
tags: |
@@ -270,35 +241,20 @@ jobs:
270241
org.opencontainers.image.url=https://github.com/GottZ/ctx
271242
org.opencontainers.image.source=https://github.com/GottZ/ctx
272243
org.opencontainers.image.revision=${{ github.sha }}
273-
org.opencontainers.image.licenses=MIT
244+
org.opencontainers.image.licenses=MPL-2.0
274245
275-
# Docker Layer Cache:
276-
# - cache-from: Registry Cache (persistiert zwischen Runs)
277-
# - cache-to: Registry Cache (exportiert nach erfolgreichem Build)
278-
# Fallback: GitHub Actions Cache (type=gha) als sekundärer Cache.
279246
- name: Build and push Docker image
280-
uses: docker/build-push-action@v7
247+
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
281248
with:
282249
context: ./go
283250
file: ./go/Dockerfile
284251
platforms: linux/amd64
285252
push: true
286253
tags: ${{ steps.meta.outputs.tags }}
287254
labels: ${{ steps.meta.outputs.labels }}
288-
# Inline Registry Cache: kein separater Cache-Manifest nötig
289255
cache-from: |
290256
type=registry,ref=${{ env.IMAGE_NAME }}:buildcache
291257
type=gha
292258
cache-to: |
293259
type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max
294260
type=gha,mode=max
295-
# Binary aus Stage 4 — kein Go-Build im Dockerfile nötig
296-
build-args: |
297-
VERSION=${{ github.ref_name }}
298-
COMMIT=${{ github.sha }}
299-
300-
- name: Image digest
301-
run: echo "Pushed ${{ env.IMAGE_NAME }} — digest ${{ steps.docker_build.outputs.digest }}"
302-
303-
# Stage 6: Deploy — entfernt. Lokaler Build via docker compose.
304-
# Zukünftig: Webhook-basiertes Deploy (kein SSH nach extern).

.github/workflows/release.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ on:
88
workflow_dispatch:
99
inputs:
1010
tag:
11-
description: "Version tag (e.g. v0.1.0)"
11+
description: "Version tag (e.g. v0.14.0)"
1212
required: true
1313

14+
permissions: {}
15+
1416
concurrency:
1517
group: release-${{ github.ref }}
1618
cancel-in-progress: true
@@ -24,7 +26,7 @@ jobs:
2426
runs-on: ubuntu-latest
2527
timeout-minutes: 15
2628
permissions:
27-
contents: write # Required for creating releases
29+
contents: read
2830

2931
strategy:
3032
matrix:
@@ -43,10 +45,10 @@ jobs:
4345

4446
steps:
4547
- name: Checkout
46-
uses: actions/checkout@v6
48+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4749

4850
- name: Setup Go ${{ env.GO_VERSION }}
49-
uses: actions/setup-go@v6
51+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
5052
with:
5153
go-version: ${{ env.GO_VERSION }}
5254
cache: true
@@ -78,7 +80,7 @@ jobs:
7880
echo "Built: ${BINARY} ($(du -h "${BINARY}" | cut -f1))"
7981
8082
- name: Upload artifact
81-
uses: actions/upload-artifact@v7
83+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
8284
with:
8385
name: ctx-${{ matrix.goos }}-${{ matrix.goarch }}
8486
path: ctx-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}
@@ -93,7 +95,7 @@ jobs:
9395

9496
steps:
9597
- name: Download all artifacts
96-
uses: actions/download-artifact@v8
98+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
9799
with:
98100
path: dist/
99101
merge-multiple: true
@@ -105,7 +107,7 @@ jobs:
105107
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
106108
107109
- name: Create GitHub Release
108-
uses: softprops/action-gh-release@v2
110+
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
109111
with:
110112
tag_name: ${{ steps.version.outputs.tag }}
111113
name: ctx ${{ steps.version.outputs.tag }}

0 commit comments

Comments
 (0)