CI scanners should read workflows as code
GitHub Actions workflows are part of the product surface. If security scanners ignore them, they miss the place automation gets its power.
A CI workflow is code with a very large set of keys.
It can check out the repository, read secrets, publish packages, open issues, deploy the site, comment on pull requests, and run shell scripts built from pull request data. If that sounds like application code, that is because it is application code. It just lives in .github/workflows and wears a YAML costume.
GitHub's CodeQL documentation now lists GitHub Actions workflows among the languages it can analyze. That is the right direction. Workflow files are no longer plumbing around the software. They are part of the software's attack surface.
Sources: GitHub CodeQL code scanning documentation, GitHub Actions security hardening guide, GitHub GITHUB_TOKEN permissions documentation, and GitHub workflow syntax reference.
YAML is still executable design
A workflow file can look harmless because it is declarative. The dangerous bit is what the declaration enables.
The GitHub Actions hardening guide calls out script injection in inline scripts. The safer pattern is to pass untrusted values through an intermediate environment variable or, better, use an action that takes the value as an argument. That is not just a shell scripting tip. It is interface design for untrusted text.
A pull request title is user input. A branch name is user input. An issue body is user input. If a workflow drops those values directly into a shell script, the YAML file has become a tiny web app with no visible form.
That deserves the same kind of review we give application code.
Permissions are a UX surface
GitHub's token documentation says workflows can change GITHUB_TOKEN permissions for a whole workflow or for individual jobs. It also notes that an action can access the token through the github.token context even when the workflow does not explicitly pass it.
That detail matters. A reviewer scanning a workflow should not have to guess what a job can do. The permission boundary should be visible near the job that uses it.
For a content repository, most jobs should not need broad write access. A build job needs to read contents. A PR comment job may need pull-requests: write. A release job may need more, but that should be obvious and rare.
The workflow should explain itself through its permissions block:
permissions: contents: read
That tiny block is a receipt. It says the job is not carrying a deploy key just to run a linter.
Code scanning should include the automation layer
CodeQL treats code as data and reports results through GitHub code scanning alerts. Its documentation lists several setup paths: default setup, advanced setup through the CodeQL workflow, and external CI uploads.
The important bit for agent-run repositories is language coverage. CodeQL supports JavaScript and TypeScript, Python, Ruby, Rust, Swift, Go, Java/Kotlin, C and C++, C#, and GitHub Actions workflows. That last item changes the review model.
If an agent opens a PR that edits application code and workflow code, the workflow edit should not be treated as a config tweak. It should be scanned and reviewed as code that can move secrets, publish artifacts, or run deployment commands.
A green application test suite does not prove the workflow is safe. It only proves the app still builds.
Agents need workflow receipts
This matters more when agents write or modify CI. Agents are good at creating plausible YAML. They are also good at overgranting permissions because broad access makes the task pass faster.
A useful agent report should include workflow receipts when it changes automation:
Workflow changed: yes Token permissions declared: yes Untrusted input in shell: checked Secrets printed: no Code scanning coverage: checked
That is not a full security audit. It is a minimum bar. It forces the agent to look at the part of the repository that can act outside the repository.
The useful rule
Treat workflow files as product code.
They deserve code review, least-privilege permissions, scanner coverage, and boring final receipts. If the CI file can deploy the product, publish a package, or read a secret, it is not support material. It is one of the most powerful interfaces in the repo.