Skip to content

Add a lint workflow covering all five languages - #10

Merged
MarkusPaulsen merged 1 commit into
chore/lint-cleanupfrom
ci/lint-workflow
Sep 8, 2026
Merged

MarkusPaulsen merged 1 commit into
chore/lint-cleanupfrom
ci/lint-workflow

Conversation

@MarkusPaulsen

@MarkusPaulsen MarkusPaulsen commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Second of two, on top of #9 which clears the findings so this starts green. Six parallel jobs cover the five languages in the repository plus the workflows themselves: shellcheck, gcc with cppcheck, ruff with bandit, yamllint, hadolint and actionlint. All of it finishes in well under a minute.

Linked issues

No linked issues.

1. Problem

The repository has no CI at all. 2900 lines of shell, C and Python that define the sandbox boundary are checked by nobody before merge, and the two suites under tests/ only run when somebody remembers to run them.

For a sandbox this matters more than usual: the defects that count are unquoted expansions on attacker-influenced paths, a shell invocation assembled from data, and a workflow that hands secrets to untrusted pull request code. All three are exactly what these linters find.

2. Improvement from the user's perspective

No Improvement from the user's perspective. This changes no behaviour a run can observe.

3. Improvement from the maintainer's perspective

A reviewer stops spending attention on quoting and formatting, and a failure names the language it came from, so it points straight at the area that broke. Thresholds and exceptions live in .hadolint.yaml, .yamllint and .bandit, so a local run behaves exactly like CI.

4. Testing manual

Prerequisites

  1. Docker, to reproduce the jobs in images matching the runner. No local installs.

Steps

  1. Run the shell job: find . -name '*.sh' -print0 | xargs -0 docker run --rm -v "$PWD:/mnt" -w /mnt koalaman/shellcheck:v0.11.0 -x -S warning.
  2. Run the C job: in ubuntu:24.04, gcc -fsyntax-only -Wall -Wextra -Werror -fanalyzer over every .c file, then cppcheck --enable=warning --error-exitcode=1.
  3. Run the Python job: ruff check --no-cache ., then bandit --recursive --ini .bandit --severity-level medium docker/prune_phase/orchestrate var/tmp/helpers.
  4. Run the remaining three: yamllint --strict ., hadolint --config .hadolint.yaml per Dockerfile, and actionlint.

Expected result

Every command exits zero and prints nothing beyond informational hadolint output, which sits below the configured threshold. After merge, the same six jobs appear on a pull request and the workflow itself is checked by actionlint.

Negative case (what must still be rejected)

Each gate must fail on a real defect rather than merely pass on a clean tree. Verified by construction while writing it: a hard-coded C file list failed as soon as a file existed only on another branch, --enable=style failed depending on the cppcheck version rather than on the code, and --configfile aborted bandit outright because .bandit is INI while that flag expects YAML. All three are fixed in this branch.

Layers exercised

No layer-specific behaviour changed.

5. Test case coverage regarding this PR

No behaviour covered by the suites changed. The three suites that #8 adds under tests/landlock-acceptance/ were checked against the shell job and are clean, so this stays green when #8 merges.

Breaking changes and migration

No breaking changes or migration.

Checklist

  • The title of this pull request describes the change, not the implementation.
  • I have self-reviewed the diff of this pull request.
  • Tests were added or updated for the behaviour changed here, in both directions: the forbidden case stays denied and the permitted case still works.
  • Any weakening of the sandbox boundary is stated explicitly above, including what it now permits that it did not permit before.
  • The change was exercised in a container started without --privileged, --cap-add or --security-opt, or the manual says why that was not possible.
  • Documentation (README.md, the comments in core/) was updated where the change is user-facing.
  • CI is green, or every remaining failure is explained above.
  • No secrets, tokens or absolute local paths are contained in the diff.

Review progress

  • Code review
  • Manual test

Six parallel jobs, one per language plus the workflows themselves, so a
failure points straight at the area that broke. Together they run in well
under a minute.

  shell       shellcheck -x -S warning
  c           gcc -Wall -Wextra -Werror -fanalyzer, then cppcheck
  python      ruff, then bandit
  yaml        yamllint --strict
  docker      hadolint
  workflows   actionlint

Conventions follow the Ares2 workflows: empty top-level permissions with a
read-only grant per job, checkout without persisted credentials, actions
pinned to a commit SHA, external binaries verified against a checksum, a
timeout on every job, and concurrency cancellation for pull requests only.

Some choices worth stating:

- shellcheck runs with -x so it follows the "source" directives. Without it
  the shared library file is analysed in isolation and every caller reports
  findings that do not exist.
- The C job discovers sources with find rather than listing them. A hard-coded
  list breaks as soon as a file is added or removed on another branch.
- cppcheck runs with --enable=warning only. The "style" category is largely
  taste, and its findings differ between cppcheck versions, which would make
  the gate fail depending on the runner image rather than on the code.
- bandit is configured through --ini: its --configfile expects YAML, while
  .bandit is an INI file, and passing the wrong one fails the run outright.
- The thresholds and exceptions live in .hadolint.yaml, .yamllint and .bandit
  rather than in this workflow, so a local run behaves like CI.

Every job was simulated locally against the actual commands before this was
committed, and all six pass on this branch.
@MarkusPaulsen MarkusPaulsen mentioned this pull request Sep 8, 2026
8 tasks
MarkusPaulsen pushed a commit that referenced this pull request Sep 8, 2026
Two corrections to the previous commit, both found by comparing against the
Ares2 originals rather than by reading my own version again.

Section 4 had lost its fill-in scaffold. Ares2 ends the section with
Prerequisites, Steps, Expected result, Negative case and the modes exercised,
so an author is asked for the negative case in the body of the pull request,
where a reviewer looks for it. My version dropped all of it and then
reintroduced the same idea as a checklist tick, which is weaker: a tick claims
the case was covered, the scaffold makes the author write down what it was.
The scaffold is back, with the four Ares enforcement combinations replaced by
the three layers this project composes, each of which can be disabled on its
own.

Section 4 had also lost both recurring meta-rules, its character limit and the
"simple words" paragraph, even though the template states in its own header
that they are repeated in every section where they apply. The counts now match
Ares2 exactly: seven limits, six "simple words" paragraphs, eleven "always
required" statements.

The template check itself is adopted, which is what makes the template binding
rather than advisory:

- .github/scripts/CheckPullRequestTemplate.java differs from the Ares2 version
  in a single line, the escape phrase for section 5, because this repository
  has no production Java code to report coverage for and answers that section
  with "No behaviour covered by the suites changed".
- .github/workflows/pullrequest-template.yml is taken over unchanged. It runs
  the checker in single-file source mode, so there is no build step, exempts
  Renovate and Dependabot at step level so a required check never hangs
  pending, and passes the pull request body through the environment rather
  than interpolating it into the shell, since a fork author controls it
  verbatim.

Running the checker locally against the open pull requests: #11 and #12 pass,
#8, #9 and #10 do not yet, and their descriptions are being brought into shape
separately.
@MarkusPaulsen
MarkusPaulsen merged commit 7483c6f into chore/lint-cleanup Sep 8, 2026
@MarkusPaulsen
MarkusPaulsen deleted the ci/lint-workflow branch September 8, 2026 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant