Granite: Granular Runtime Enforcement for GitHub Actions Permissions
Modern software projects use automated CI/CD pipelines to streamline their development, build, and deployment processes. GitHub Actions is a popular CI/CD platform that enables project maintainers to create custom workflows – collections of jobs composed of sequential steps – using reusable components known as actions. Wary of the security risks introduced by fully-privileged actions, GitHub provides a job-level permission model for controlling workflow access to repository resources. Unfortunately, this model is too coarse-grained to reduce the attack surface pertaining to permission misuse attacks: All actions within a job share the same permissions granted to the job. This violates the principle of least privilege and can lead to broader software supply chain attacks, whenever a compromised action exploits the granted permissions to compromise the repository resources. In this paper, we present Granite, a runtime proxy-based system that enforces fine-grained permissions for GitHub Actions at the step-level granularity within a job. Granite transparently monitors requests made by JavaScript and composite actions during workflow execution and checks them against predefined step-level policies at runtime. We evaluate Granite in terms of compatibility, security, and performance overhead using a dataset of 500 workflows comprising 12,916 jobs from the most-starred GitHub repositories that use GitHub Actions. Our analysis reveals that 52.7% of the jobs can be protected by Granite against permission misuse attacks. We evaluate Granite on 20 top-starred repositories (63 actions, 58 workflows), validate attack prevention using 10 permission misuse attacks across 42 overprivileged jobs, and measure an average overhead of 55% (3.67 seconds) per job, concluding that Granite effectively reduces CI/CD attack surfaces.
💡 Research Summary
The paper “Granite: Granular Runtime Enforcement for GitHub Actions Permissions” addresses a critical security flaw in GitHub Actions, the widely used CI/CD automation platform. The core problem lies in its permission model, which is enforced only at the granularity of entire jobs. Within a job, all steps share the same set of permissions granted to that job. This coarse-grained approach violates the principle of least privilege, creating a significant attack surface. A single compromised or malicious action inside a job can misuse the job’s high-level permissions (e.g., write access to the repository) to stage software supply chain attacks.
Existing solutions, including GitHub’s own monitoring/advisor system and static analysis tools from third parties like Step Security, are insufficient. They either provide post-facto suggestions without runtime blocking or fail to account for dynamic execution contexts, leading to inaccurate permission inference.
To overcome these limitations, the authors present Granite, the first runtime system designed to enforce fine-grained permissions at the step level within a GitHub Actions job. Granite’s architecture is built upon the self-hosted GitHub runner and operates through proxy-based monitoring:
- For JavaScript actions, it introduces a shim layer that transparently intercepts all communications between the action and the GitHub API.
- For composite actions, it constructs an isolated container environment with an HTTP proxy to monitor outgoing requests. During workflow execution, Granite checks every permission request made by an action against a predefined step-level policy in real-time, blocking any violations. It integrates seamlessly with existing workflows without requiring changes to the YAML files.
The evaluation comprehensively demonstrates Granite’s practicality and effectiveness:
- Prevalence Analysis: An analysis of 500 workflows (12,916 jobs) from top-starred GitHub repositories revealed that 46.6% of jobs are overprivileged, and Granite could protect 52.7% of all jobs.
- Compatibility & Security: Testing on 20 popular repositories (63 actions, 58 workflows) showed full compatibility. The authors then constructed 10 distinct permission misuse attacks against 42 confirmed overprivileged jobs (59.8% being High/Critical severity). Granite successfully prevented all attacks, reducing the attack surface by an average of 83.3% per vulnerable job.
- Performance: The runtime overhead introduced by Granite averages 55% (3.67 seconds) per job. A breakdown shows an average overhead of 14% (2.42 seconds) for JavaScript actions and 94% (4.92 seconds) for composite actions, the latter due to the cost of container isolation and proxy setup.
In conclusion, Granite provides a novel and practical mechanism to enforce least privilege in GitHub Actions workflows. By enabling granular, runtime permission enforcement at the step level, it significantly reduces the CI/CD attack surface with acceptable performance costs, offering a strong defense against permission misuse and supply chain attacks.
Comments & Academic Discussion
Loading comments...
Leave a Comment