Skip to content

Commit 5eafddb

Browse files
authored
Merge pull request xencon#1732 from sbadakhc/issue-1730/release-v1-1-50
Release v1.1.50 (xencon#1730)
2 parents 934788f + 0cd5767 commit 5eafddb

13 files changed

Lines changed: 119 additions & 20 deletions

File tree

.claude/rules/ci-checks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@ gh pr create \
6969
shellcheck --severity=warning --exclude=SC1091 $(find . -name "*.sh" -not -path "./.git/*")
7070

7171
# Individual checks
72-
./aixcl checks paths # or: agents, elisions, generated, ascii, yaml, compose, env
72+
./aixcl checks paths # or: agents, elisions, generated, ascii, pins, yaml, compose, env
7373
./aixcl checks pr-refs <body-file>
7474
```

.claude/skills/housekeeping/SKILL.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,19 @@ fi
165165
- [ ] If gitleaks not installed, note it as a tooling gap
166166
- [ ] Note: `--no-git` scans ALL files on disk, including gitignored runtime files (e.g. `pgadmin-servers.json`). Findings in gitignored runtime files belong in the `.gitleaks.toml` `paths` allowlist, not the `commits` allowlist.
167167

168-
### Step 8 -- Docker Image Pin Hygiene
168+
### Step 8 -- Container Image Pin Hygiene
169+
170+
Covers compose files AND shell code under `lib/` and `scripts/` -- four
171+
unpinned alpine references hid in shell code because the old sweep only
172+
scanned compose (issue #1726). Legitimate `:latest` uses (locally built
173+
`localhost/` images, ollama model tags) are exempted via `localhost/`
174+
detection or an inline `pin-waiver:` comment.
169175

170176
```bash
171-
grep -hn "image:" services/docker-compose*.yml | grep -v "#" | \
172-
grep -E "image:\s+(\S+:latest\s*$|\S*/[^:]+\s*$|[^/:]+\s*$)" \
173-
&& echo "FAIL: unpinned image tags found above" || echo "ok: all images pinned"
177+
./aixcl checks pins
174178
```
175179

176-
- [ ] All images in all compose files use pinned version tags (no `latest`, no bare image names)
180+
- [ ] All container image references are pinned (no `latest`, no bare image names), or carry an explicit `pin-waiver:` comment with a reason
177181

178182
### Step 9 -- Shellcheck Sweep (All Scripts)
179183

.opencode/rules/ci-checks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@ gh pr create \
6969
shellcheck --severity=warning --exclude=SC1091 $(find . -name "*.sh" -not -path "./.git/*")
7070

7171
# Individual checks
72-
./aixcl checks paths # or: agents, elisions, generated, ascii, yaml, compose, env
72+
./aixcl checks paths # or: agents, elisions, generated, ascii, pins, yaml, compose, env
7373
./aixcl checks pr-refs <body-file>
7474
```

.opencode/skills/housekeeping/SKILL.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,19 @@ fi
165165
- [ ] If gitleaks not installed, note it as a tooling gap
166166
- [ ] Note: `--no-git` scans ALL files on disk, including gitignored runtime files (e.g. `pgadmin-servers.json`). Findings in gitignored runtime files belong in the `.gitleaks.toml` `paths` allowlist, not the `commits` allowlist.
167167

168-
### Step 8 -- Docker Image Pin Hygiene
168+
### Step 8 -- Container Image Pin Hygiene
169+
170+
Covers compose files AND shell code under `lib/` and `scripts/` -- four
171+
unpinned alpine references hid in shell code because the old sweep only
172+
scanned compose (issue #1726). Legitimate `:latest` uses (locally built
173+
`localhost/` images, ollama model tags) are exempted via `localhost/`
174+
detection or an inline `pin-waiver:` comment.
169175

170176
```bash
171-
grep -hn "image:" services/docker-compose*.yml | grep -v "#" | \
172-
grep -E "image:\s+(\S+:latest\s*$|\S*/[^:]+\s*$|[^/:]+\s*$)" \
173-
&& echo "FAIL: unpinned image tags found above" || echo "ok: all images pinned"
177+
./aixcl checks pins
174178
```
175179

176-
- [ ] All images in all compose files use pinned version tags (no `latest`, no bare image names)
180+
- [ ] All container image references are pinned (no `latest`, no bare image names), or carry an explicit `pin-waiver:` comment with a reason
177181

178182
### Step 9 -- Shellcheck Sweep (All Scripts)
179183

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to the AIXCL project will be documented in this file.
55
## [Unreleased]
66

77

8+
## [v1.1.50] - 2026-07-03
9+
10+
### Summary
11+
12+
Release v1.1.50 -- Supply-chain hardening: every container image reference in the repository is now pinned, and a new check makes unpinned references anywhere in compose or shell code a CI failure.
13+
14+
### Added
15+
16+
- [x] **Image Pin Check**: new `./aixcl checks pins` (included in `checks all` and the housekeeping skill) sweeps compose files plus shell code under lib/ and scripts/ for `:latest`, untagged `FROM` lines in generated Dockerfile templates, and untagged registry references. Locally built `localhost/` images and explicitly annotated `pin-waiver:` cases are exempt. Closes #1728.
17+
18+
### Fixed
19+
20+
- [x] **Unpinned Alpine References**: four shell code paths (app provisioning, the generated app Dockerfile template, the env-restore helper, and the rootless setup smoke test) pulled `alpine:latest` or bare `alpine`; all are pinned to `docker.io/library/alpine:3.24.1`. These were invisible to the previous compose-only pin sweep. Closes #1726.
21+
22+
823
## [v1.1.49] - 2026-07-03
924

1025
### Summary

completion/aixcl.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ _aixcl_complete() {
198198
return 0
199199
;;
200200
'checks')
201-
local checks_actions="all paths agents elisions generated ascii yaml compose env pr-refs"
201+
local checks_actions="all paths agents elisions generated ascii pins yaml compose env pr-refs"
202202
mapfile -t COMPREPLY < <(compgen -W "$checks_actions" -- "$cur")
203203
return 0
204204
;;

lib/aixcl/commands/app.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ EOF
10271027

10281028
# Write Dockerfile stub
10291029
cat > "${app_dir}/docker/Dockerfile" << 'EOF'
1030-
FROM alpine:latest
1030+
FROM docker.io/library/alpine:3.24.1
10311031
10321032
# TODO: Add your application here
10331033

lib/aixcl/commands/checks.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ CHECKS_FAILED=()
99
CHECKS_SKIPPED=()
1010

1111
_checks_usage() {
12-
echo "Usage: $0 checks {all|paths|agents|elisions|generated|ascii|yaml|compose|env|pr-refs <file>}"
12+
echo "Usage: $0 checks {all|paths|agents|elisions|generated|ascii|pins|yaml|compose|env|pr-refs <file>}"
1313
echo " all Run every check below (continues on failure, prints summary)"
1414
echo " paths Documentation relative links and stale path patterns"
1515
echo " agents .claude/ vs .opencode/ rules and skills mirror parity"
1616
echo " elisions AI-elision placeholders and suspicious mass deletions"
1717
echo " generated Tracked generated files and stale artifacts"
18+
echo " pins Container image references pinned (compose + shell code)"
1819
echo " ascii Non-ASCII punctuation in markdown files"
1920
echo " yaml yamllint over the repository"
2021
echo " compose docker compose config validation (main + overrides)"
@@ -140,6 +141,9 @@ function checks_cmd() {
140141
elisions)
141142
bash "${checks_dir}/check-ai-elisions.sh" "$@"
142143
;;
144+
pins)
145+
bash "${checks_dir}/check-image-pins.sh"
146+
;;
143147
generated)
144148
bash "${checks_dir}/check-generated-files.sh"
145149
;;
@@ -177,6 +181,7 @@ function checks_cmd() {
177181
_check_run "elisions" bash "${checks_dir}/check-ai-elisions.sh" || true
178182
_check_run "generated" bash "${checks_dir}/check-generated-files.sh" || true
179183
_check_run "ascii" _check_ascii || true
184+
_check_run "pins" bash "${checks_dir}/check-image-pins.sh" || true
180185

181186
if command -v yamllint > /dev/null 2>&1; then
182187
_check_run "yaml" yamllint -c "${SCRIPT_DIR}/.yamllint.yml" "${SCRIPT_DIR}" || true

lib/aixcl/commands/models.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ is_model_installed() {
99

1010
case "$engine" in
1111
ollama)
12-
# Ollama list shows models. We need to match the name exactly, or with :latest if missing tag
12+
# Ollama list shows models. Match the name exactly, or with the
13+
# default tag appended when the name carries none.
14+
# (pin-waiver: ollama MODEL tags are not container images)
1315
local model_with_tag="$model"
14-
[[ ! "$model" =~ : ]] && model_with_tag="${model}:latest"
16+
[[ ! "$model" =~ : ]] && model_with_tag="${model}:latest" # pin-waiver: ollama model tag
1517
"${DOCKER_BIN:-docker}" exec "$container" ollama list 2>/dev/null | awk '{print $1}' | grep -qE "^${model_with_tag}$"
1618
return $?
1719
;;

lib/aixcl/commands/stack.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ function start() {
475475
--user "${current_uid}:${current_gid}" \
476476
-v "${env_backup_volume}:/backup:ro" \
477477
-v "${SCRIPT_DIR}:/target" \
478-
alpine sh -c "test -f /backup/.env && cp /backup/.env /target/.env && chmod 600 /target/.env" >/dev/null 2>&1; then
478+
docker.io/library/alpine:3.24.1 sh -c "test -f /backup/.env && cp /backup/.env /target/.env && chmod 600 /target/.env" >/dev/null 2>&1; then
479479
if [ -f "$env_file" ]; then
480480
# Fix ownership and permissions if file was created by root
481481
if [ -O "$env_file" ] || [ -w "$env_file" ]; then

0 commit comments

Comments
 (0)