Compare commits
8 commits
main
...
feature/de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2d83c9c60 | ||
|
|
03c6aeecac | ||
|
|
bb4b9e732d | ||
|
|
b62162e11d | ||
|
|
bec46c105a | ||
|
|
24dd9a2522 | ||
|
|
b5eed58f5d | ||
|
|
ac89272860 |
16 changed files with 388 additions and 78 deletions
14
README.md
14
README.md
|
|
@ -22,6 +22,8 @@ Traditional security monitoring and EDR solutions are ineffective for CI/CD runn
|
|||
StepSecurity Harden-Runner addresses this gap by providing security monitoring tailored for CI/CD runners, with support for Linux, Windows, and macOS runners. This approach brings CI/CD runners under the same level of security scrutiny as other critical systems, addressing a significant gap in the software supply chain.
|
||||
### Harden-Runner: Security Incidents Detected
|
||||
|
||||
- [Harden-Runner Detected the Compromised axios npm Package Dropping a Remote Access Trojan](https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan) ([backstage/backstage#33693](https://github.com/backstage/backstage/issues/33693), [block/elasticgraph#1103](https://github.com/block/elasticgraph/issues/1103))
|
||||
- [Harden-Runner Detected the Trivy Compromise with Malicious v0.69.4 Release](https://www.stepsecurity.io/blog/trivy-compromised-a-second-time---malicious-v0-69-4-release) ([k8gb-io/k8gb#2294](https://github.com/k8gb-io/k8gb/issues/2294))
|
||||
- [Harden-Runner Detected the tj-actions/changed-files compromise](https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised) ([CVE-2025-30066](https://github.com/advisories/GHSA-mrrh-fwg8-r2c3))
|
||||
- [Harden Runner Detected the Sha1-Hulud Supply Chain Attack in CNCF’s Backstage Repository](https://www.stepsecurity.io/blog/how-harden-runner-detected-the-sha1-hulud-supply-chain-attack-in-cncfs-backstage-repository)
|
||||
- [Harden-Runner Detected the NX Build System compromise](https://www.stepsecurity.io/blog/supply-chain-security-alert-popular-nx-build-system-package-compromised-with-data-stealing-malware)
|
||||
|
|
@ -32,7 +34,7 @@ StepSecurity Harden-Runner addresses this gap by providing security monitoring t
|
|||
- [Harden-Runner Flagged an Anomalous Outbound Call, Leading to a Docker Documentation Update](https://www.stepsecurity.io/blog/harden-runner-flags-anomalous-outbound-call-leading-to-docker-documentation-update)
|
||||
|
||||
### See It in Action
|
||||
Harden-Runner secures over **18 million CI/CD workflow runs every week**, protecting thousands of pipelines, including those from popular open-source projects by **Microsoft, Google, and CISA**. See how top projects are using Harden-Runner and explore the insights:
|
||||
Harden-Runner secures over **25 million CI/CD workflow runs every week**, protecting thousands of pipelines, including those from popular open-source projects by **Microsoft, Google, and CISA**. See how top projects are using Harden-Runner and explore the insights:
|
||||
➡️ [Who's using Harden-Runner?](https://docs.stepsecurity.io/whos-using-harden-runner)
|
||||
|
||||
## Quick Links
|
||||
|
|
@ -70,7 +72,7 @@ To integrate Harden-Runner, follow these steps:
|
|||
```yaml
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
|
||||
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
|
@ -149,11 +151,11 @@ Harden-Runner is trusted by over 11,000 leading open-source projects and enterpr
|
|||
|
||||
### Enterprise Case Studies
|
||||
|
||||
- [How Mercari Secures GitHub Actions with StepSecurity](https://www.stepsecurity.io/case-studies/mercari)
|
||||
- [How Omnissa Secures GitHub Actions with StepSecurity](https://www.stepsecurity.io/case-studies/omnissa)
|
||||
- [Chainguard Secures GitHub Actions with StepSecurity](https://www.stepsecurity.io/case-studies/chainguard)
|
||||
- [How Coveo Strengthened GitHub Actions Security with StepSecurity](https://www.stepsecurity.io/case-studies/coveo)
|
||||
- [Hashgraph Achieves Comprehensive CI/CD Security Without Compromising Development Speed](https://www.stepsecurity.io/case-studies/hashgraph)
|
||||
- [Chainguard Secures GitHub Actions with StepSecurity](https://www.stepsecurity.io/case-studies/chainguard)
|
||||
- [Kapiche secures their GitHub Actions software supply chain with Harden-Runner](https://www.stepsecurity.io/case-studies/kapiche)
|
||||
- [Arcjet Enhances CI/CD Security with Harden-Runner](https://www.stepsecurity.io/case-studies/arcjet)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -161,7 +163,7 @@ Harden-Runner is trusted by over 11,000 leading open-source projects and enterpr
|
|||
|
||||
Harden-Runner is designed to work seamlessly across a variety of runner environments, providing consistent security insights and protections regardless of where your workflows execute. For self-hosted runners, audit mode is deployed directly to the runner infrastructure without requiring any changes to your existing workflows. For more details, refer to the [official documentation](https://docs.stepsecurity.io/harden-runner).
|
||||
|
||||
| Environment Type | Compatibility | Audit Mode Deployment | Workflow Changes for Audit Mode |
|
||||
| Environment Type | Compatibility | Audit Mode Deployment | Workflow Changes for Audit/Block Mode |
|
||||
|------------------|---------------|--------------------------|-------------------|
|
||||
| GitHub-hosted runners (Linux) | ✅ Full support | Add Harden-Runner Action to workflow | Yes |
|
||||
| GitHub-hosted runners (Windows, macOS) | ✅ Audit mode only | Add Harden-Runner Action to workflow | Yes |
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ inputs:
|
|||
description: "Set to true to fetch policy from the policy store using the API key. This is the preferred method over the policy input which requires id-token: write permission. Policies can be defined and attached at workflow, repo, org, or cluster (for ARC) level in the policy store. The most granular policy will apply."
|
||||
required: false
|
||||
default: "false"
|
||||
deploy-on-self-hosted-vm:
|
||||
description: "Set to true to deploy the Harden Runner agent directly on a self-hosted runner VM (Linux only). The recommended approach for self-hosted VMs is to bake the agent into the VM image; see docs.stepsecurity.io. Use this option only if baking is not possible, and only for ephemeral runners."
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
branding:
|
||||
icon: "check-square"
|
||||
|
|
|
|||
3
dist/index.js
vendored
3
dist/index.js
vendored
|
|
@ -31910,6 +31910,9 @@ function isAgentInstalled(platform) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
function shouldDeployAgentOnSelfHosted(deployOnSelfHostedVm, isContainer, agentAlreadyInstalled) {
|
||||
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
|
||||
}
|
||||
function utils_getAnnotationLogs(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
|
|
|
|||
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
3
dist/post/index.js
vendored
3
dist/post/index.js
vendored
|
|
@ -31916,6 +31916,9 @@ function isAgentInstalled(platform) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
function shouldDeployAgentOnSelfHosted(deployOnSelfHostedVm, isContainer, agentAlreadyInstalled) {
|
||||
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
|
||||
}
|
||||
function getAnnotationLogs(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
|
|
|
|||
2
dist/post/index.js.map
vendored
2
dist/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
114
dist/pre/index.js
vendored
114
dist/pre/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/pre/index.js.map
vendored
2
dist/pre/index.js.map
vendored
File diff suppressed because one or more lines are too long
143
package-lock.json
generated
143
package-lock.json
generated
|
|
@ -2852,9 +2852,10 @@
|
|||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -3765,21 +3766,9 @@
|
|||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fast-xml-builder": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz",
|
||||
"integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz",
|
||||
"integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
|
||||
"integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
|
@ -3788,8 +3777,24 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-xml-builder": "^1.0.0",
|
||||
"strnum": "^2.1.2"
|
||||
"path-expression-matcher": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "5.5.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.11.tgz",
|
||||
"integrity": "sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-xml-builder": "^1.1.4",
|
||||
"path-expression-matcher": "^1.4.0",
|
||||
"strnum": "^2.2.3"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "src/cli/cli.js"
|
||||
|
|
@ -3886,10 +3891,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz",
|
||||
"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
|
||||
"dev": true
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "2.5.5",
|
||||
|
|
@ -6079,9 +6085,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
|
@ -6448,6 +6454,21 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-expression-matcher": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
|
||||
"integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
|
|
@ -6487,10 +6508,11 @@
|
|||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
|
|
@ -6966,9 +6988,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/strnum": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
|
||||
"integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz",
|
||||
"integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
|
@ -9700,9 +9722,9 @@
|
|||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -10371,17 +10393,21 @@
|
|||
"dev": true
|
||||
},
|
||||
"fast-xml-builder": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz",
|
||||
"integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ=="
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
|
||||
"integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
|
||||
"requires": {
|
||||
"path-expression-matcher": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"fast-xml-parser": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz",
|
||||
"integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==",
|
||||
"version": "5.5.11",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.11.tgz",
|
||||
"integrity": "sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA==",
|
||||
"requires": {
|
||||
"fast-xml-builder": "^1.0.0",
|
||||
"strnum": "^2.1.2"
|
||||
"fast-xml-builder": "^1.1.4",
|
||||
"path-expression-matcher": "^1.4.0",
|
||||
"strnum": "^2.2.3"
|
||||
}
|
||||
},
|
||||
"fastq": {
|
||||
|
|
@ -10450,9 +10476,9 @@
|
|||
}
|
||||
},
|
||||
"flatted": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz",
|
||||
"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true
|
||||
},
|
||||
"form-data": {
|
||||
|
|
@ -12089,9 +12115,9 @@
|
|||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.memoize": {
|
||||
|
|
@ -12358,6 +12384,11 @@
|
|||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"path-expression-matcher": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
|
||||
"integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
|
|
@ -12388,9 +12419,9 @@
|
|||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true
|
||||
},
|
||||
"pirates": {
|
||||
|
|
@ -12717,9 +12748,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"strnum": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
|
||||
"integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg=="
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz",
|
||||
"integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import * as fs from "fs";
|
|||
|
||||
const CHECKSUMS = {
|
||||
tls: {
|
||||
amd64: "d4b80f15758bb950787000e802cc58a565919a8cb9ecf405777b304ef42911fe", // v1.7.15
|
||||
arm64: "3c224ea1da1776d1ba9f70b8dd8f0d8432230a7c2d464bca84bbdee8b7d46f6c",
|
||||
amd64: "86d042adcdc03eb1ea50d35d265da47622a6d0aedef9657f84ce1eb7f04d6057", // v1.8.0
|
||||
arm64: "ea1074a2358d50db9a9fe18ae3971b87305cda63f262c494a5f43b25f4e524ce",
|
||||
},
|
||||
non_tls: {
|
||||
amd64: "4aaaeebbe10e619d8ce13e8cc4a1acbafc8f891e8cdd319984480b9ec08407b8", // v0.15.0
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export async function installAgent(
|
|||
|
||||
if (isTLS) {
|
||||
downloadPath = await tc.downloadTool(
|
||||
`https://github.com/step-security/agent-ebpf/releases/download/v1.7.15/harden-runner_1.7.15_linux_${variant}.tar.gz`,
|
||||
`https://github.com/step-security/agent-ebpf/releases/download/v1.8.0/harden-runner_1.8.0_linux_${variant}.tar.gz`,
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export interface Configuration {
|
|||
one_time_key: string;
|
||||
api_key: string;
|
||||
use_policy_store: boolean;
|
||||
deploy_on_self_hosted_vm: boolean;
|
||||
}
|
||||
|
||||
export interface PolicyResponse {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ test("merge configs", async () => {
|
|||
one_time_key: "",
|
||||
api_key: "",
|
||||
use_policy_store: false,
|
||||
deploy_on_self_hosted_vm: false,
|
||||
};
|
||||
let policyResponse: PolicyResponse = {
|
||||
owner: "h0x0er",
|
||||
|
|
@ -75,6 +76,7 @@ test("merge configs", async () => {
|
|||
one_time_key: "",
|
||||
api_key: "",
|
||||
use_policy_store: false,
|
||||
deploy_on_self_hosted_vm: false,
|
||||
};
|
||||
|
||||
localConfig = mergeConfigs(localConfig, policyResponse);
|
||||
|
|
@ -314,6 +316,7 @@ test("mergeConfigs does not override local allowed_endpoints if not empty", () =
|
|||
one_time_key: "",
|
||||
api_key: "",
|
||||
use_policy_store: false,
|
||||
deploy_on_self_hosted_vm: false,
|
||||
};
|
||||
let policyResponse: PolicyResponse = {
|
||||
allowed_endpoints: ["remote.endpoint:443"],
|
||||
|
|
@ -345,6 +348,7 @@ test("mergeConfigs overrides disable_sudo_and_containers from remote", () => {
|
|||
one_time_key: "",
|
||||
api_key: "",
|
||||
use_policy_store: false,
|
||||
deploy_on_self_hosted_vm: false,
|
||||
};
|
||||
let policyResponse: PolicyResponse = {
|
||||
allowed_endpoints: [],
|
||||
|
|
@ -375,6 +379,7 @@ test("mergeConfigs does not override fields when remote values are undefined", (
|
|||
one_time_key: "",
|
||||
api_key: "",
|
||||
use_policy_store: false,
|
||||
deploy_on_self_hosted_vm: false,
|
||||
};
|
||||
let policyResponse: PolicyResponse = {
|
||||
allowed_endpoints: [],
|
||||
|
|
|
|||
83
src/setup.ts
83
src/setup.ts
|
|
@ -37,7 +37,7 @@ import {
|
|||
installWindowsAgent,
|
||||
} from "./install-agent";
|
||||
|
||||
import { chownForFolder, isAgentInstalled, isPlatformSupported } from "./utils";
|
||||
import { chownForFolder, isAgentInstalled, isPlatformSupported, shouldDeployAgentOnSelfHosted } from "./utils";
|
||||
|
||||
interface MonitorResponse {
|
||||
runner_ip_address?: string;
|
||||
|
|
@ -89,6 +89,7 @@ interface MonitorResponse {
|
|||
one_time_key: "",
|
||||
api_key: core.getInput("api-key"),
|
||||
use_policy_store: core.getBooleanInput("use-policy-store"),
|
||||
deploy_on_self_hosted_vm: core.getBooleanInput("deploy-on-self-hosted-vm"),
|
||||
};
|
||||
|
||||
if (confg.api_key !== "") {
|
||||
|
|
@ -294,7 +295,26 @@ interface MonitorResponse {
|
|||
|
||||
core.info(common.SELF_HOSTED_RUNNER_MESSAGE);
|
||||
|
||||
if (confg.egress_policy === "block") {
|
||||
const inContainer = isDocker();
|
||||
const alreadyInstalled = isAgentInstalled(process.platform);
|
||||
|
||||
if (shouldDeployAgentOnSelfHosted(confg.deploy_on_self_hosted_vm, inContainer, alreadyInstalled)) {
|
||||
if (process.platform !== "linux") {
|
||||
core.info("deploy-on-self-hosted-vm is only supported on Linux. Skipping agent deployment.");
|
||||
} else {
|
||||
core.info("deploy-on-self-hosted-vm is enabled. Installing agent on self-hosted runner.");
|
||||
await installAgentForSelfHosted(context.repo.owner, confg);
|
||||
}
|
||||
} else {
|
||||
if (confg.deploy_on_self_hosted_vm && inContainer) {
|
||||
core.info("Skipping agent deployment: running inside a container.");
|
||||
}
|
||||
if (confg.deploy_on_self_hosted_vm && alreadyInstalled) {
|
||||
core.info("Agent already installed on self-hosted runner, skipping installation.");
|
||||
}
|
||||
}
|
||||
|
||||
if (confg.egress_policy === "block" && !confg.deploy_on_self_hosted_vm) {
|
||||
sendAllowedEndpoints(confg.allowed_endpoints);
|
||||
await sleep(5000);
|
||||
}
|
||||
|
|
@ -449,3 +469,62 @@ export function sleep(ms: number) {
|
|||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
export async function installAgentForSelfHosted(owner: string, confg: Configuration) {
|
||||
try {
|
||||
console.log("Installing Harden Runner agent for self-hosted runner");
|
||||
|
||||
let isTLS = await isTLSEnabled(owner);
|
||||
|
||||
if (!isTLS) {
|
||||
console.log("TLS is not enabled for this organization. Agent installation skipped for self-hosted runner.");
|
||||
return;
|
||||
}
|
||||
|
||||
const selfHostedConfig = {
|
||||
customer: owner,
|
||||
working_directory: confg.working_directory,
|
||||
api_url: confg.api_url,
|
||||
api_key: uuidv4(),
|
||||
allowed_endpoints: confg.allowed_endpoints,
|
||||
egress_policy: confg.egress_policy,
|
||||
disable_telemetry: confg.disable_telemetry,
|
||||
disable_sudo: confg.disable_sudo,
|
||||
disable_sudo_and_containers: confg.disable_sudo_and_containers,
|
||||
disable_file_monitoring: confg.disable_file_monitoring,
|
||||
is_github_hosted: false,
|
||||
};
|
||||
const selfHostedConfigStr = JSON.stringify(selfHostedConfig);
|
||||
|
||||
cp.execSync("sudo mkdir -p /home/agent");
|
||||
chownForFolder(process.env.USER, "/home/agent");
|
||||
|
||||
const agentInstalled = await installAgent(isTLS, selfHostedConfigStr);
|
||||
|
||||
if (agentInstalled) {
|
||||
const statusFile = "/home/agent/agent.status";
|
||||
const logFile = "/home/agent/agent.log";
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!fs.existsSync(statusFile)) {
|
||||
counter++;
|
||||
if (counter > 30) {
|
||||
console.log("timed out");
|
||||
if (fs.existsSync(logFile)) {
|
||||
const content = fs.readFileSync(logFile, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
break;
|
||||
}
|
||||
await sleep(300);
|
||||
} else {
|
||||
const content = fs.readFileSync(statusFile, "utf-8");
|
||||
console.log(content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Failed to install agent for self-hosted runner: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
92
src/utils.test.ts
Normal file
92
src/utils.test.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { shouldDeployAgentOnSelfHosted, isAgentInstalled, isPlatformSupported, getAnnotationLogs } from "./utils";
|
||||
import * as fs from "fs";
|
||||
|
||||
jest.mock("fs", () => ({
|
||||
...jest.requireActual("fs"),
|
||||
existsSync: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockedExistsSync = fs.existsSync as jest.MockedFunction<typeof fs.existsSync>;
|
||||
|
||||
describe("shouldDeployAgentOnSelfHosted", () => {
|
||||
test("returns true when deploy flag is true, not container, agent not installed", () => {
|
||||
expect(shouldDeployAgentOnSelfHosted(true, false, false)).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false when deploy flag is false", () => {
|
||||
expect(shouldDeployAgentOnSelfHosted(false, false, false)).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false when running in a container", () => {
|
||||
expect(shouldDeployAgentOnSelfHosted(true, true, false)).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false when agent is already installed", () => {
|
||||
expect(shouldDeployAgentOnSelfHosted(true, false, true)).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false when in container and agent installed", () => {
|
||||
expect(shouldDeployAgentOnSelfHosted(true, true, true)).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false when all conditions are negative", () => {
|
||||
expect(shouldDeployAgentOnSelfHosted(false, true, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isAgentInstalled", () => {
|
||||
afterEach(() => {
|
||||
mockedExistsSync.mockReset();
|
||||
});
|
||||
|
||||
test("returns false for linux when status file does not exist", () => {
|
||||
mockedExistsSync.mockReturnValue(false);
|
||||
expect(isAgentInstalled("linux")).toBe(false);
|
||||
expect(mockedExistsSync).toHaveBeenCalledWith("/home/agent/agent.status");
|
||||
});
|
||||
|
||||
test("returns true for linux when status file exists", () => {
|
||||
mockedExistsSync.mockReturnValue(true);
|
||||
expect(isAgentInstalled("linux")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for win32 when status file does not exist", () => {
|
||||
mockedExistsSync.mockReturnValue(false);
|
||||
expect(isAgentInstalled("win32")).toBe(false);
|
||||
expect(mockedExistsSync).toHaveBeenCalledWith("C:\\agent\\agent.status");
|
||||
});
|
||||
|
||||
test("returns false for darwin when status file does not exist", () => {
|
||||
mockedExistsSync.mockReturnValue(false);
|
||||
expect(isAgentInstalled("darwin")).toBe(false);
|
||||
expect(mockedExistsSync).toHaveBeenCalledWith("/opt/step-security/agent.status");
|
||||
});
|
||||
|
||||
test("returns false for unsupported platform", () => {
|
||||
expect(isAgentInstalled("freebsd" as NodeJS.Platform)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isPlatformSupported", () => {
|
||||
test("returns true for linux", () => {
|
||||
expect(isPlatformSupported("linux")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns true for win32", () => {
|
||||
expect(isPlatformSupported("win32")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns true for darwin", () => {
|
||||
expect(isPlatformSupported("darwin")).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for unsupported platform", () => {
|
||||
expect(isPlatformSupported("freebsd" as NodeJS.Platform)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAnnotationLogs", () => {
|
||||
test("throws for unsupported platform", () => {
|
||||
expect(() => getAnnotationLogs("freebsd" as NodeJS.Platform)).toThrow("platform not supported");
|
||||
});
|
||||
});
|
||||
|
|
@ -32,6 +32,14 @@ export function isAgentInstalled(platform: NodeJS.Platform) {
|
|||
}
|
||||
}
|
||||
|
||||
export function shouldDeployAgentOnSelfHosted(
|
||||
deployOnSelfHostedVm: boolean,
|
||||
isContainer: boolean,
|
||||
agentAlreadyInstalled: boolean
|
||||
): boolean {
|
||||
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
|
||||
}
|
||||
|
||||
export function getAnnotationLogs(platform: NodeJS.Platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue