mirror of
https://github.com/step-security/harden-runner
synced 2026-08-09 13:11:02 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9462746bdb |
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac
|
||||
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@2d790406f505036ef40ecba973cc774a50395aac
|
||||
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -75,4 +75,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac
|
||||
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
|
||||
|
||||
@@ -62,6 +62,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # tag=v1.0.26
|
||||
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # tag=v1.0.26
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -19,20 +19,19 @@ Corporate laptops and production servers typically have robust security monitori
|
||||
|
||||
Traditional security monitoring and EDR solutions are ineffective for CI/CD runners due to their ephemeral nature. These tools also lack the necessary context to correlate events with specific workflow runs in a CI/CD environment.
|
||||
|
||||
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.
|
||||
StepSecurity Harden-Runner addresses this gap by providing security monitoring tailored for CI/CD 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 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)
|
||||
- [Harden-Runner Detected a CI/CD Supply Chain Attack in Google’s Open-Source Project Flank](https://www.stepsecurity.io/case-studies/flank)
|
||||
- [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)
|
||||
- [Harden-Runner Detected a CI/CD Supply Chain Attack in Microsoft’s Open-Source Project Azure Karpenter Provider in Real-Time](https://www.stepsecurity.io/case-studies/azure-karpenter-provider)
|
||||
- [Harden-Runner Detected Anomalous Traffic to api.ipify.org Across Multiple Customers](https://www.stepsecurity.io/blog/harden-runner-detects-anomalous-traffic-to-api-ipify-org-across-multiple-customers)
|
||||
- [Harden-Runner Detected an Unexpected Microsoft Defender Installation on GitHub-Hosted Ubuntu Runners](https://www.stepsecurity.io/blog/how-stepsecurity-harden-runner-detected-unexpected-microsoft-defender-installation-on-github-hosted-ubuntu-runners)
|
||||
- [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 **8 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 +69,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@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -138,7 +137,7 @@ Explore the full feature set in the [Features Documentation](https://docs.stepse
|
||||
|
||||
## Trusted By and Case Studies
|
||||
|
||||
Harden-Runner is trusted by over 11,000 leading open-source projects and enterprises, including Microsoft, Google, Kubernetes, and more.
|
||||
Harden-Runner is trusted by over 8000 leading open-source projects and enterprises, including Microsoft, Google, Kubernetes, and more.
|
||||
|
||||
### Trusted by
|
||||
|
||||
@@ -163,8 +162,7 @@ Harden-Runner is designed to work seamlessly across a variety of runner environm
|
||||
|
||||
| Environment Type | Compatibility | Audit Mode Deployment | Workflow Changes for Audit 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 |
|
||||
| GitHub-hosted runners | ✅ Full support | Add Harden-Runner Action to workflow | Yes |
|
||||
| Self-hosted VM runners | ✅ Full support | Include agent in runner image | No |
|
||||
| Self-hosted bare-metal runners | ✅ Full support | Install agent as a service | No |
|
||||
| Actions Runner Controller (ARC) | ✅ Full support | Deploy as DaemonSet | No |
|
||||
@@ -178,7 +176,7 @@ Want to know the technical details? Dive into the architecture of Harden-Runner
|
||||
|
||||
## Limitations
|
||||
|
||||
While Harden-Runner offers powerful features, there are certain limitations. See the complete list in [Known Limitations](docs/limitations.md).
|
||||
While Harden-Runner offers powerful features, there are certain limitations based on the environment, such as OS support. See the complete list in [Known Limitations](docs/limitations.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ branding:
|
||||
icon: "check-square"
|
||||
color: "green"
|
||||
runs:
|
||||
using: "node24"
|
||||
using: "node20"
|
||||
pre: "dist/pre/index.js"
|
||||
main: "dist/index.js"
|
||||
post: "dist/post/index.js"
|
||||
|
||||
Vendored
+5
-53
@@ -31876,53 +31876,8 @@ var __webpack_exports__ = {};
|
||||
|
||||
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js
|
||||
var lib_core = __nccwpck_require__(7484);
|
||||
// EXTERNAL MODULE: external "child_process"
|
||||
var external_child_process_ = __nccwpck_require__(5317);
|
||||
// EXTERNAL MODULE: external "fs"
|
||||
var external_fs_ = __nccwpck_require__(9896);
|
||||
;// CONCATENATED MODULE: ./src/utils.ts
|
||||
|
||||
|
||||
function isPlatformSupported(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
case "win32":
|
||||
case "darwin":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function chownForFolder(newOwner, target) {
|
||||
let cmd = "sudo";
|
||||
let args = ["chown", "-R", newOwner, target];
|
||||
cp.execFileSync(cmd, args);
|
||||
}
|
||||
function isAgentInstalled(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
return fs.existsSync("/home/agent/agent.status");
|
||||
case "win32":
|
||||
return fs.existsSync("C:\\agent\\agent.status");
|
||||
case "darwin":
|
||||
return fs.existsSync("/opt/step-security/agent.status");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function utils_getAnnotationLogs(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
return fs.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
case "win32":
|
||||
return fs.readFileSync("C:\\agent\\annotation.log", "utf8");
|
||||
case "darwin":
|
||||
return fs.readFileSync("/opt/step-security/annotation.log", "utf8");
|
||||
default:
|
||||
throw new Error("platform not supported");
|
||||
}
|
||||
}
|
||||
|
||||
;// CONCATENATED MODULE: ./src/common.ts
|
||||
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
@@ -31975,9 +31930,8 @@ function addSummary() {
|
||||
}
|
||||
let needsSubscription = false;
|
||||
try {
|
||||
let data = getAnnotationLogs(process.platform);
|
||||
if (data !== undefined &&
|
||||
data.includes("StepSecurity Harden Runner is disabled")) {
|
||||
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
if (data.includes("StepSecurity Harden Runner is disabled")) {
|
||||
needsSubscription = true;
|
||||
}
|
||||
}
|
||||
@@ -32024,12 +31978,11 @@ function addSummary() {
|
||||
}
|
||||
const STATUS_HARDEN_RUNNER_UNAVAILABLE = "409";
|
||||
const CONTAINER_MESSAGE = "This job is running in a container. Such jobs can be monitored by installing Harden Runner in a custom VM image for GitHub-hosted runners.";
|
||||
const UNSUPPORTED_RUNNER_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner. Harden Runner is only supported on GitHub-hosted runners (Ubuntu, Windows, and macOS). This job will not be monitored.";
|
||||
const UBUNTU_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
|
||||
const SELF_HOSTED_RUNNER_MESSAGE = "This job is running on a self-hosted runner.";
|
||||
const HARDEN_RUNNER_UNAVAILABLE_MESSAGE = "Sorry, we are currently experiencing issues with the Harden Runner installation process. It is currently unavailable.";
|
||||
const ARC_RUNNER_MESSAGE = "Workflow is currently being executed in ARC based runner.";
|
||||
const ARM64_RUNNER_MESSAGE = "ARM runners are not supported in the Harden-Runner community tier.";
|
||||
const ARM64_WINDOWS_RUNNER_MESSAGE = "Windows ARM runners are not yet supported by Harden-Runner.";
|
||||
|
||||
;// CONCATENATED MODULE: external "node:fs"
|
||||
const external_node_fs_namespaceObject = require("node:fs");
|
||||
@@ -32130,7 +32083,6 @@ var src_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argu
|
||||
|
||||
|
||||
|
||||
|
||||
(() => src_awaiter(void 0, void 0, void 0, function* () {
|
||||
var _a, _b;
|
||||
console.log("[harden-runner] main-step");
|
||||
@@ -32139,8 +32091,8 @@ var src_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argu
|
||||
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
|
||||
return;
|
||||
}
|
||||
if (!isPlatformSupported(process.platform)) {
|
||||
console.log(UNSUPPORTED_RUNNER_MESSAGE);
|
||||
if (process.platform !== "linux") {
|
||||
console.log(UBUNTU_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (isGithubHosted() && isDocker()) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+61
-268
@@ -31876,6 +31876,8 @@ var __webpack_exports__ = {};
|
||||
|
||||
// EXTERNAL MODULE: external "fs"
|
||||
var external_fs_ = __nccwpck_require__(9896);
|
||||
// EXTERNAL MODULE: external "child_process"
|
||||
var external_child_process_ = __nccwpck_require__(5317);
|
||||
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js
|
||||
var lib_core = __nccwpck_require__(7484);
|
||||
;// CONCATENATED MODULE: ./src/configs.ts
|
||||
@@ -31883,51 +31885,6 @@ const STEPSECURITY_ENV = "agent"; // agent or int
|
||||
const configs_STEPSECURITY_API_URL = `https://${STEPSECURITY_ENV}.api.stepsecurity.io/v1`;
|
||||
const STEPSECURITY_WEB_URL = "https://app.stepsecurity.io";
|
||||
|
||||
// EXTERNAL MODULE: external "child_process"
|
||||
var external_child_process_ = __nccwpck_require__(5317);
|
||||
;// CONCATENATED MODULE: ./src/utils.ts
|
||||
|
||||
|
||||
function isPlatformSupported(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
case "win32":
|
||||
case "darwin":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function chownForFolder(newOwner, target) {
|
||||
let cmd = "sudo";
|
||||
let args = ["chown", "-R", newOwner, target];
|
||||
cp.execFileSync(cmd, args);
|
||||
}
|
||||
function isAgentInstalled(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
return external_fs_.existsSync("/home/agent/agent.status");
|
||||
case "win32":
|
||||
return external_fs_.existsSync("C:\\agent\\agent.status");
|
||||
case "darwin":
|
||||
return external_fs_.existsSync("/opt/step-security/agent.status");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function getAnnotationLogs(platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
return external_fs_.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
case "win32":
|
||||
return external_fs_.readFileSync("C:\\agent\\annotation.log", "utf8");
|
||||
case "darwin":
|
||||
return external_fs_.readFileSync("/opt/step-security/annotation.log", "utf8");
|
||||
default:
|
||||
throw new Error("platform not supported");
|
||||
}
|
||||
}
|
||||
|
||||
;// CONCATENATED MODULE: ./src/common.ts
|
||||
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
@@ -31980,9 +31937,8 @@ function addSummary() {
|
||||
}
|
||||
let needsSubscription = false;
|
||||
try {
|
||||
let data = getAnnotationLogs(process.platform);
|
||||
if (data !== undefined &&
|
||||
data.includes("StepSecurity Harden Runner is disabled")) {
|
||||
let data = external_fs_.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
if (data.includes("StepSecurity Harden Runner is disabled")) {
|
||||
needsSubscription = true;
|
||||
}
|
||||
}
|
||||
@@ -32029,15 +31985,12 @@ function addSummary() {
|
||||
}
|
||||
const STATUS_HARDEN_RUNNER_UNAVAILABLE = "409";
|
||||
const CONTAINER_MESSAGE = "This job is running in a container. Such jobs can be monitored by installing Harden Runner in a custom VM image for GitHub-hosted runners.";
|
||||
const UNSUPPORTED_RUNNER_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner. Harden Runner is only supported on GitHub-hosted runners (Ubuntu, Windows, and macOS). This job will not be monitored.";
|
||||
const UBUNTU_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
|
||||
const SELF_HOSTED_RUNNER_MESSAGE = "This job is running on a self-hosted runner.";
|
||||
const HARDEN_RUNNER_UNAVAILABLE_MESSAGE = "Sorry, we are currently experiencing issues with the Harden Runner installation process. It is currently unavailable.";
|
||||
const ARC_RUNNER_MESSAGE = "Workflow is currently being executed in ARC based runner.";
|
||||
const ARM64_RUNNER_MESSAGE = "ARM runners are not supported in the Harden-Runner community tier.";
|
||||
const ARM64_WINDOWS_RUNNER_MESSAGE = "Windows ARM runners are not yet supported by Harden-Runner.";
|
||||
|
||||
// EXTERNAL MODULE: external "path"
|
||||
var external_path_ = __nccwpck_require__(6928);
|
||||
;// CONCATENATED MODULE: external "node:fs"
|
||||
const external_node_fs_namespaceObject = require("node:fs");
|
||||
;// CONCATENATED MODULE: ./node_modules/is-docker/index.js
|
||||
@@ -32179,8 +32132,6 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(() => cleanup_awaiter(void 0, void 0, void 0, function* () {
|
||||
var _a, _b;
|
||||
console.log("[harden-runner] post-step");
|
||||
@@ -32189,8 +32140,8 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
|
||||
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
|
||||
return;
|
||||
}
|
||||
if (!isPlatformSupported(process.platform)) {
|
||||
console.log(UNSUPPORTED_RUNNER_MESSAGE);
|
||||
if (process.platform !== "linux") {
|
||||
console.log(UBUNTU_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (isGithubHosted() && isDocker()) {
|
||||
@@ -32207,21 +32158,66 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
|
||||
if (process.env.STATE_customVMImage === "true") {
|
||||
return;
|
||||
}
|
||||
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
|
||||
return;
|
||||
}
|
||||
if (String(process.env.STATE_monitorStatusCode) ===
|
||||
STATUS_HARDEN_RUNNER_UNAVAILABLE) {
|
||||
console.log(HARDEN_RUNNER_UNAVAILABLE_MESSAGE);
|
||||
return;
|
||||
}
|
||||
switch (process.platform) {
|
||||
case "linux":
|
||||
yield handleLinuxCleanup();
|
||||
break;
|
||||
case "win32":
|
||||
yield handleWindowsCleanup();
|
||||
break;
|
||||
case "darwin":
|
||||
yield handleMacosCleanup();
|
||||
if (isGithubHosted() && external_fs_.existsSync("/home/agent/post_event.json")) {
|
||||
console.log("Post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
external_fs_.writeFileSync("/home/agent/post_event.json", JSON.stringify({ event: "post" }));
|
||||
const doneFile = "/home/agent/done.json";
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!external_fs_.existsSync(doneFile)) {
|
||||
counter++;
|
||||
if (counter > 10) {
|
||||
console.log("timed out");
|
||||
break;
|
||||
}
|
||||
yield sleep(1000);
|
||||
} // The file *does* exist
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const log = "/home/agent/agent.log";
|
||||
if (external_fs_.existsSync(log)) {
|
||||
console.log("log:");
|
||||
var content = external_fs_.readFileSync(log, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
const daemonLog = "/home/agent/daemon.log";
|
||||
if (external_fs_.existsSync(daemonLog)) {
|
||||
console.log("daemonLog:");
|
||||
var content = external_fs_.readFileSync(daemonLog, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
var status = "/home/agent/agent.status";
|
||||
if (external_fs_.existsSync(status)) {
|
||||
console.log("status:");
|
||||
var content = external_fs_.readFileSync(status, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
var disable_sudo = process.env.STATE_disableSudo;
|
||||
var disable_sudo_and_containers = process.env.STATE_disableSudoAndContainers;
|
||||
if (disable_sudo !== "true" && disable_sudo_and_containers !== "true") {
|
||||
try {
|
||||
var journalLog = external_child_process_.execSync("sudo journalctl -u agent.service --lines=1000", {
|
||||
encoding: "utf8",
|
||||
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
||||
});
|
||||
console.log("agent.service log:");
|
||||
console.log(journalLog);
|
||||
}
|
||||
catch (error) {
|
||||
console.log("Warning: Could not fetch service logs:", error.message);
|
||||
}
|
||||
}
|
||||
try {
|
||||
yield addSummary();
|
||||
@@ -32230,209 +32226,6 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
|
||||
console.log(exception);
|
||||
}
|
||||
}))();
|
||||
function handleLinuxCleanup() {
|
||||
return cleanup_awaiter(this, void 0, void 0, function* () {
|
||||
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
|
||||
return;
|
||||
}
|
||||
if (isGithubHosted() && external_fs_.existsSync("/home/agent/post_event.json")) {
|
||||
console.log("Post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
external_fs_.writeFileSync("/home/agent/post_event.json", JSON.stringify({ event: "post" }));
|
||||
const doneFile = "/home/agent/done.json";
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!external_fs_.existsSync(doneFile)) {
|
||||
counter++;
|
||||
if (counter > 10) {
|
||||
console.log("timed out");
|
||||
break;
|
||||
}
|
||||
yield sleep(1000);
|
||||
}
|
||||
else {
|
||||
// The file *does* exist
|
||||
break;
|
||||
}
|
||||
}
|
||||
const log = "/home/agent/agent.log";
|
||||
if (external_fs_.existsSync(log)) {
|
||||
console.log("log:");
|
||||
var content = external_fs_.readFileSync(log, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
const daemonLog = "/home/agent/daemon.log";
|
||||
if (external_fs_.existsSync(daemonLog)) {
|
||||
console.log("daemonLog:");
|
||||
var content = external_fs_.readFileSync(daemonLog, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
var status = "/home/agent/agent.status";
|
||||
if (external_fs_.existsSync(status)) {
|
||||
console.log("status:");
|
||||
var content = external_fs_.readFileSync(status, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
var disable_sudo = process.env.STATE_disableSudo;
|
||||
var disable_sudo_and_containers = process.env.STATE_disableSudoAndContainers;
|
||||
if (disable_sudo !== "true" && disable_sudo_and_containers !== "true") {
|
||||
try {
|
||||
var journalLog = external_child_process_.execSync("sudo journalctl -u agent.service --lines=1000", {
|
||||
encoding: "utf8",
|
||||
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
||||
});
|
||||
console.log("agent.service log:");
|
||||
console.log(journalLog);
|
||||
}
|
||||
catch (error) {
|
||||
console.log("Warning: Could not fetch service logs:", error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleMacosCleanup() {
|
||||
return cleanup_awaiter(this, void 0, void 0, function* () {
|
||||
const post_event = "/opt/step-security/post_event.json";
|
||||
if (isGithubHosted() && external_fs_.existsSync(post_event)) {
|
||||
console.log("Post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
external_fs_.writeFileSync(post_event, JSON.stringify({ event: "post" }));
|
||||
// if agent is installed; wait for it to create done.json
|
||||
if (isAgentInstalled(process.platform)) {
|
||||
let macDone = "/opt/step-security/done.json";
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!external_fs_.existsSync(macDone)) {
|
||||
counter++;
|
||||
if (counter > 10) {
|
||||
console.log("timed out");
|
||||
break;
|
||||
}
|
||||
yield sleep(1000);
|
||||
}
|
||||
else {
|
||||
// The file *does* exist
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let macAgentLog = "/opt/step-security/agent.log";
|
||||
if (external_fs_.existsSync(macAgentLog)) {
|
||||
console.log("macAgentLog:");
|
||||
var content = external_fs_.readFileSync(macAgentLog, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
else {
|
||||
console.log("😭 macos agent.log file not found");
|
||||
}
|
||||
// Capture system log stream for harden-runner subsystem
|
||||
try {
|
||||
console.log("\nSystem log stream for io.stepsecurity.harden-runner:");
|
||||
const logStreamOutput = external_child_process_.execSync("log show --predicate 'subsystem == \"io.stepsecurity.harden-runner\"' --info --last 10m", {
|
||||
encoding: "utf8",
|
||||
maxBuffer: 1024 * 1024 * 10,
|
||||
timeout: 5000, // 5 seconds timeout
|
||||
});
|
||||
console.log(logStreamOutput);
|
||||
}
|
||||
catch (error) {
|
||||
console.log("Warning: Could not fetch system log stream:", error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleWindowsCleanup() {
|
||||
return cleanup_awaiter(this, void 0, void 0, function* () {
|
||||
// windows cleanup
|
||||
const agentDir = process.env.STATE_agentDir || "C:\\agent";
|
||||
const postEventFile = external_path_.join(agentDir, "post_event.json");
|
||||
if (isGithubHosted() && external_fs_.existsSync(postEventFile)) {
|
||||
console.log("Windows post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
if (process.arch === "arm64") {
|
||||
console.log(ARM64_WINDOWS_RUNNER_MESSAGE);
|
||||
return;
|
||||
}
|
||||
const p = external_child_process_.spawn("powershell.exe", [
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-Command",
|
||||
"query user; exit $LASTEXITCODE",
|
||||
], { stdio: ["ignore", "pipe", "pipe"], shell: false, windowsHide: true });
|
||||
p.unref();
|
||||
external_fs_.writeFileSync(postEventFile, JSON.stringify({ event: "post" }));
|
||||
// if agent is installed; wait for it to create done.json
|
||||
if (isAgentInstalled(process.platform)) {
|
||||
const doneFile = external_path_.join(agentDir, "done.json");
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!external_fs_.existsSync(doneFile)) {
|
||||
counter++;
|
||||
if (counter > 10) {
|
||||
console.log("timed out");
|
||||
break;
|
||||
}
|
||||
yield sleep(1000);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("stopping windows agent process...");
|
||||
const pidFile = external_path_.join(agentDir, "agent.pid");
|
||||
try {
|
||||
if (!external_fs_.existsSync(pidFile)) {
|
||||
console.log("PID file not found. Agent may not be running.");
|
||||
return;
|
||||
}
|
||||
const pid = parseInt(external_fs_.readFileSync(pidFile, "utf8").trim());
|
||||
console.log(`agent PID from file: ${pid}`);
|
||||
try {
|
||||
process.kill(pid, 0); // signal 0 just checks if process exists
|
||||
}
|
||||
catch (_a) {
|
||||
console.log("agent process not running.");
|
||||
external_fs_.unlinkSync(pidFile);
|
||||
return;
|
||||
}
|
||||
console.log(`stopping agent process (PID: ${pid})...`);
|
||||
process.kill(pid, "SIGINT");
|
||||
let gracefulShutdown = false;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
yield sleep(1000);
|
||||
try {
|
||||
process.kill(pid, 0); // check if still exists
|
||||
}
|
||||
catch (_b) {
|
||||
gracefulShutdown = true;
|
||||
console.log("agent process stopped gracefully");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gracefulShutdown) {
|
||||
console.log("graceful shutdown timeout (10s), forcing termination...");
|
||||
process.kill(pid, "SIGKILL");
|
||||
console.log("agent process terminated forcefully");
|
||||
}
|
||||
if (external_fs_.existsSync(pidFile)) {
|
||||
external_fs_.unlinkSync(pidFile);
|
||||
console.log("PID file cleaned up");
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log("warning: error stopping agent process:", error.message);
|
||||
}
|
||||
const log = external_path_.join(agentDir, "agent.log");
|
||||
if (external_fs_.existsSync(log)) {
|
||||
console.log("agent log:");
|
||||
const content = external_fs_.readFileSync(log, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
});
|
||||
}
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+342
-396
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -5,7 +5,8 @@
|
||||
For GitHub-hosted runners, Harden-Runner GitHub Action downloads and installs the StepSecurity Agent.
|
||||
|
||||
- The code to monitor file, process, and network activity is in the Agent.
|
||||
- The community tier agent for Linux is open-source and can be found [here](https://github.com/step-security/agent). The enterprise agent for Linux and agents for Windows and macOS are closed-source.
|
||||
- The community tier agent is open-source and can be found [here](https://github.com/step-security/agent). The enterprise tier agent is closed-source. Both agents are written in Go.
|
||||
- The agent's build is reproducible. You can view the steps to reproduce the build [here](http://app.stepsecurity.io/github/step-security/agent/releases/latest)
|
||||
|
||||
### Self-Hosted Actions Runner Controller (ARC) Runners
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
## Limitations
|
||||
|
||||
### GitHub-Hosted Runners
|
||||
|
||||
* Only Ubuntu VM is supported. Windows and MacOS GitHub-hosted runners are not supported. There is a discussion about that [here](https://github.com/step-security/harden-runner/discussions/121).
|
||||
* Harden-Runner is not supported when [job is run in a container](https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container) with built-in labels such as `ubuntu-latest`, as it needs sudo access on the Ubuntu VM to run. The limitation is if the entire job is run in a container. However, such jobs can be monitored when using custom VM images with GitHub-hosted runners. This is also not a limitation for Self-Hosted runners.
|
||||
|
||||
### Self-Hosted Actions Runner Controller (ARC) Runners
|
||||
|
||||
* Since ARC Harden Runner uses eBPF, only Linux jobs are supported. Windows and MacOS jobs are not supported.
|
||||
|
||||
### Self-Hosted VM (e.g. on EC2) and Bare-metal Runners
|
||||
|
||||
* Only Linux jobs are supported. Windows and MacOS jobs are not supported.
|
||||
+1
-5
@@ -8,8 +8,4 @@ reason = "Untrusted headers are not processed"
|
||||
|
||||
[[IgnoredVulns]]
|
||||
id = "GHSA-xx4v-prfh-6cgc"
|
||||
reason = "Untrusted headers are not processed"
|
||||
|
||||
[[IgnoredVulns]]
|
||||
id = "GHSA-g9mf-h72j-4rw9"
|
||||
reason = "undici fetch() is only used to call GitHub API; exploitation requires a malicious server"
|
||||
reason = "Untrusted headers are not processed"
|
||||
Generated
+58
-108
@@ -2673,11 +2673,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -3172,11 +3171,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
||||
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
@@ -3747,49 +3745,18 @@
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"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": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.0.9.tgz",
|
||||
"integrity": "sha512-2mBwCiuW3ycKQQ6SOesSB8WeF+fIGb6I/GG5vU5/XEptwFFhp9PE8b9O7fbs2dpq9fXn4ULR3UsfydNUCntf5A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-xml-builder": "^1.0.0",
|
||||
"strnum": "^2.1.2"
|
||||
"strnum": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "src/cli/cli.js"
|
||||
@@ -5968,11 +5935,10 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
|
||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
@@ -6079,11 +6045,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
@@ -6210,10 +6175,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"license": "ISC",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -6966,16 +6930,15 @@
|
||||
}
|
||||
},
|
||||
"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.0.5",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.0.5.tgz",
|
||||
"integrity": "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
]
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
@@ -7018,16 +6981,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/table/node_modules/ajv": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -9566,9 +9528,9 @@
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -9934,9 +9896,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
||||
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"diff-sequences": {
|
||||
@@ -10364,24 +10326,12 @@
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||
"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=="
|
||||
},
|
||||
"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.0.9",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.0.9.tgz",
|
||||
"integrity": "sha512-2mBwCiuW3ycKQQ6SOesSB8WeF+fIGb6I/GG5vU5/XEptwFFhp9PE8b9O7fbs2dpq9fXn4ULR3UsfydNUCntf5A==",
|
||||
"requires": {
|
||||
"fast-xml-builder": "^1.0.0",
|
||||
"strnum": "^2.1.2"
|
||||
"strnum": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"fastq": {
|
||||
@@ -12006,9 +11956,9 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
|
||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
@@ -12089,9 +12039,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.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.memoize": {
|
||||
@@ -12192,9 +12142,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -12717,9 +12667,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.0.5",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.0.5.tgz",
|
||||
"integrity": "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
@@ -12750,15 +12700,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
|
||||
+10
-29
@@ -4,24 +4,18 @@ import * as fs from "fs";
|
||||
|
||||
const CHECKSUMS = {
|
||||
tls: {
|
||||
amd64: "30fca1b76d21361e4976e763e15dc939a40d9aea3b009a799af185a5344aafa7", // v1.7.14
|
||||
arm64: "766e04e216300240a577402ddea659887b22b43bcd5d436d868d0802db4a3a77",
|
||||
amd64: "2a5be06ab620340f6957ddd180450caab414671c8b7da1996d8e2755c1cd49fa", // v1.7.9
|
||||
arm64: "e9d0ccf3e4e62ba15ff208ef6e0afa0ad5e323c3dfc50527342f436a85e65a55",
|
||||
},
|
||||
non_tls: {
|
||||
amd64: "1531bda40026b799b0704d0f775c372653a91fe436628fa8b416849d9c0707a8", // v0.14.4
|
||||
},
|
||||
darwin: "797399a3a3f6f9c4c000a02e0d8c7b16499129c9bdc2ad9cf2a10072c10654fb", // v0.0.4
|
||||
windows: {
|
||||
amd64: "e98f8b9cf9ecf6566f1e16a470fbe4aef01610a644fd8203a1bab3ff142186c8", // v1.0.0
|
||||
amd64: "336093af8ebe969567b66fd035af3bd4f7e1c723ce680d6b4b5b2a1f79bc329e", // v0.14.2
|
||||
},
|
||||
};
|
||||
|
||||
// verifyChecksum returns true if checksum is valid
|
||||
export function verifyChecksum(
|
||||
downloadPath: string,
|
||||
isTLS: boolean,
|
||||
variant: string,
|
||||
platform: string
|
||||
variant: string
|
||||
) {
|
||||
const fileBuffer: Buffer = fs.readFileSync(downloadPath);
|
||||
const checksum: string = crypto
|
||||
@@ -31,30 +25,17 @@ export function verifyChecksum(
|
||||
|
||||
let expectedChecksum: string = "";
|
||||
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
expectedChecksum = isTLS
|
||||
? CHECKSUMS["tls"][variant]
|
||||
: CHECKSUMS["non_tls"][variant];
|
||||
break;
|
||||
case "darwin":
|
||||
expectedChecksum = CHECKSUMS["darwin"];
|
||||
break;
|
||||
case "win32":
|
||||
expectedChecksum = CHECKSUMS["windows"][variant];
|
||||
break;
|
||||
default:
|
||||
console.log(`Unsupported platform: ${platform}`);
|
||||
return false;
|
||||
if (isTLS) {
|
||||
expectedChecksum = CHECKSUMS["tls"][variant];
|
||||
} else {
|
||||
expectedChecksum = CHECKSUMS["non_tls"][variant];
|
||||
}
|
||||
|
||||
if (checksum !== expectedChecksum) {
|
||||
core.setFailed(
|
||||
`❌ Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`
|
||||
`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
core.info(`✅ Checksum verification passed. checksum=${checksum}`);
|
||||
return true;
|
||||
core.debug("Checksum verification passed.");
|
||||
}
|
||||
|
||||
+13
-190
@@ -1,13 +1,10 @@
|
||||
import * as fs from "fs";
|
||||
import * as common from "./common";
|
||||
import * as cp from "child_process";
|
||||
import * as path from "path";
|
||||
import * as common from "./common";
|
||||
import isDocker from "is-docker";
|
||||
import { isARCRunner } from "./arc-runner";
|
||||
import { isGithubHosted } from "./tls-inspect";
|
||||
import { context } from "@actions/github";
|
||||
import { isPlatformSupported, isAgentInstalled } from "./utils";
|
||||
|
||||
(async () => {
|
||||
console.log("[harden-runner] post-step");
|
||||
|
||||
@@ -17,8 +14,8 @@ import { isPlatformSupported, isAgentInstalled } from "./utils";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlatformSupported(process.platform)) {
|
||||
console.log(common.UNSUPPORTED_RUNNER_MESSAGE);
|
||||
if (process.platform !== "linux") {
|
||||
console.log(common.UBUNTU_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (isGithubHosted() && isDocker()) {
|
||||
@@ -39,6 +36,10 @@ import { isPlatformSupported, isAgentInstalled } from "./utils";
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
String(process.env.STATE_monitorStatusCode) ===
|
||||
common.STATUS_HARDEN_RUNNER_UNAVAILABLE
|
||||
@@ -47,30 +48,6 @@ import { isPlatformSupported, isAgentInstalled } from "./utils";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (process.platform) {
|
||||
case "linux":
|
||||
await handleLinuxCleanup();
|
||||
break;
|
||||
case "win32":
|
||||
await handleWindowsCleanup();
|
||||
break;
|
||||
case "darwin":
|
||||
await handleMacosCleanup();
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
await common.addSummary();
|
||||
} catch (exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
})();
|
||||
|
||||
async function handleLinuxCleanup() {
|
||||
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isGithubHosted() && fs.existsSync("/home/agent/post_event.json")) {
|
||||
console.log("Post step already executed, skipping");
|
||||
return;
|
||||
@@ -92,8 +69,8 @@ async function handleLinuxCleanup() {
|
||||
break;
|
||||
}
|
||||
await sleep(1000);
|
||||
} else {
|
||||
// The file *does* exist
|
||||
} // The file *does* exist
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -137,167 +114,13 @@ async function handleLinuxCleanup() {
|
||||
console.log("Warning: Could not fetch service logs:", error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMacosCleanup() {
|
||||
const post_event = "/opt/step-security/post_event.json";
|
||||
|
||||
if (isGithubHosted() && fs.existsSync(post_event)) {
|
||||
console.log("Post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFileSync(post_event, JSON.stringify({ event: "post" }));
|
||||
|
||||
// if agent is installed; wait for it to create done.json
|
||||
if (isAgentInstalled(process.platform)) {
|
||||
let macDone = "/opt/step-security/done.json";
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!fs.existsSync(macDone)) {
|
||||
counter++;
|
||||
if (counter > 10) {
|
||||
console.log("timed out");
|
||||
break;
|
||||
}
|
||||
await sleep(1000);
|
||||
} else {
|
||||
// The file *does* exist
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let macAgentLog = "/opt/step-security/agent.log";
|
||||
if (fs.existsSync(macAgentLog)) {
|
||||
console.log("macAgentLog:");
|
||||
var content = fs.readFileSync(macAgentLog, "utf-8");
|
||||
console.log(content);
|
||||
} else {
|
||||
console.log("😭 macos agent.log file not found");
|
||||
}
|
||||
|
||||
// Capture system log stream for harden-runner subsystem
|
||||
try {
|
||||
console.log("\nSystem log stream for io.stepsecurity.harden-runner:");
|
||||
const logStreamOutput = cp.execSync(
|
||||
"log show --predicate 'subsystem == \"io.stepsecurity.harden-runner\"' --info --last 10m",
|
||||
{
|
||||
encoding: "utf8",
|
||||
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
||||
timeout: 5000, // 5 seconds timeout
|
||||
}
|
||||
);
|
||||
console.log(logStreamOutput);
|
||||
} catch (error) {
|
||||
console.log("Warning: Could not fetch system log stream:", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleWindowsCleanup() {
|
||||
// windows cleanup
|
||||
const agentDir = process.env.STATE_agentDir || "C:\\agent";
|
||||
const postEventFile = path.join(agentDir, "post_event.json");
|
||||
|
||||
if (isGithubHosted() && fs.existsSync(postEventFile)) {
|
||||
console.log("Windows post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.arch === "arm64") {
|
||||
console.log(common.ARM64_WINDOWS_RUNNER_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
const p = cp.spawn(
|
||||
"powershell.exe",
|
||||
[
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-Command",
|
||||
"query user; exit $LASTEXITCODE",
|
||||
],
|
||||
{ stdio: ["ignore", "pipe", "pipe"], shell: false, windowsHide: true }
|
||||
);
|
||||
p.unref();
|
||||
|
||||
fs.writeFileSync(postEventFile, JSON.stringify({ event: "post" }));
|
||||
|
||||
// if agent is installed; wait for it to create done.json
|
||||
if (isAgentInstalled(process.platform)) {
|
||||
const doneFile = path.join(agentDir, "done.json");
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
if (!fs.existsSync(doneFile)) {
|
||||
counter++;
|
||||
if (counter > 10) {
|
||||
console.log("timed out");
|
||||
break;
|
||||
}
|
||||
await sleep(1000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("stopping windows agent process...");
|
||||
const pidFile = path.join(agentDir, "agent.pid");
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(pidFile)) {
|
||||
console.log("PID file not found. Agent may not be running.");
|
||||
return;
|
||||
}
|
||||
|
||||
const pid = parseInt(fs.readFileSync(pidFile, "utf8").trim());
|
||||
console.log(`agent PID from file: ${pid}`);
|
||||
|
||||
try {
|
||||
process.kill(pid, 0); // signal 0 just checks if process exists
|
||||
} catch {
|
||||
console.log("agent process not running.");
|
||||
fs.unlinkSync(pidFile);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`stopping agent process (PID: ${pid})...`);
|
||||
process.kill(pid, "SIGINT");
|
||||
|
||||
let gracefulShutdown = false;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await sleep(1000);
|
||||
|
||||
try {
|
||||
process.kill(pid, 0); // check if still exists
|
||||
} catch {
|
||||
gracefulShutdown = true;
|
||||
console.log("agent process stopped gracefully");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gracefulShutdown) {
|
||||
console.log("graceful shutdown timeout (10s), forcing termination...");
|
||||
process.kill(pid, "SIGKILL");
|
||||
console.log("agent process terminated forcefully");
|
||||
}
|
||||
|
||||
if (fs.existsSync(pidFile)) {
|
||||
fs.unlinkSync(pidFile);
|
||||
console.log("PID file cleaned up");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("warning: error stopping agent process:", error.message);
|
||||
await common.addSummary();
|
||||
} catch (exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
|
||||
const log = path.join(agentDir, "agent.log");
|
||||
if (fs.existsSync(log)) {
|
||||
console.log("agent log:");
|
||||
const content = fs.readFileSync(log, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
+9
-15
@@ -1,6 +1,6 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as fs from "fs";
|
||||
import { STEPSECURITY_API_URL, STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { getAnnotationLogs } from "./utils";
|
||||
|
||||
export function printInfo(web_url) {
|
||||
console.log(
|
||||
@@ -67,11 +67,8 @@ export async function addSummary() {
|
||||
|
||||
let needsSubscription = false;
|
||||
try {
|
||||
let data = getAnnotationLogs(process.platform);
|
||||
if (
|
||||
data !== undefined &&
|
||||
data.includes("StepSecurity Harden Runner is disabled")
|
||||
) {
|
||||
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
if (data.includes("StepSecurity Harden Runner is disabled")) {
|
||||
needsSubscription = true;
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -100,23 +97,23 @@ export async function addSummary() {
|
||||
// Extract owner and repo from GITHUB_REPOSITORY (format: owner/repo)
|
||||
const [owner, repo] = process.env["GITHUB_REPOSITORY"]?.split("/") || [];
|
||||
const run_id = process.env["GITHUB_RUN_ID"];
|
||||
|
||||
|
||||
if (!owner || !repo || !run_id || !correlation_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch job summary from API
|
||||
const apiUrl = `${STEPSECURITY_API_URL}/github/${owner}/${repo}/actions/runs/${run_id}/correlation/${correlation_id}/job-markdown-summary`;
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl);
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch job summary: ${response.status} ${response.statusText}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const markdownSummary = await response.text();
|
||||
|
||||
|
||||
// Render the markdown summary using core.summary.addRaw
|
||||
await core.summary.addRaw(markdownSummary).write();
|
||||
return;
|
||||
@@ -131,8 +128,8 @@ export const STATUS_HARDEN_RUNNER_UNAVAILABLE = "409";
|
||||
export const CONTAINER_MESSAGE =
|
||||
"This job is running in a container. Such jobs can be monitored by installing Harden Runner in a custom VM image for GitHub-hosted runners.";
|
||||
|
||||
export const UNSUPPORTED_RUNNER_MESSAGE =
|
||||
"This job is not running in a GitHub Actions Hosted Runner. Harden Runner is only supported on GitHub-hosted runners (Ubuntu, Windows, and macOS). This job will not be monitored.";
|
||||
export const UBUNTU_MESSAGE =
|
||||
"This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
|
||||
|
||||
export const SELF_HOSTED_RUNNER_MESSAGE =
|
||||
"This job is running on a self-hosted runner.";
|
||||
@@ -145,6 +142,3 @@ export const ARC_RUNNER_MESSAGE =
|
||||
|
||||
export const ARM64_RUNNER_MESSAGE =
|
||||
"ARM runners are not supported in the Harden-Runner community tier.";
|
||||
|
||||
export const ARM64_WINDOWS_RUNNER_MESSAGE =
|
||||
"Windows ARM runners are not yet supported by Harden-Runner.";
|
||||
|
||||
+2
-4
@@ -4,8 +4,6 @@ import isDocker from "is-docker";
|
||||
import { STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { isGithubHosted } from "./tls-inspect";
|
||||
import { context } from "@actions/github";
|
||||
import { isPlatformSupported } from "./utils";
|
||||
|
||||
(async () => {
|
||||
console.log("[harden-runner] main-step");
|
||||
|
||||
@@ -15,8 +13,8 @@ import { isPlatformSupported } from "./utils";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlatformSupported(process.platform)) {
|
||||
console.log(common.UNSUPPORTED_RUNNER_MESSAGE);
|
||||
if (process.platform !== "linux") {
|
||||
console.log(common.UBUNTU_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (isGithubHosted() && isDocker()) {
|
||||
|
||||
+4
-161
@@ -5,8 +5,7 @@ import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import { verifyChecksum } from "./checksum";
|
||||
import { EOL } from "os";
|
||||
import { ARM64_RUNNER_MESSAGE, ARM64_WINDOWS_RUNNER_MESSAGE } from "./common";
|
||||
import { chownForFolder } from "./utils";
|
||||
import { ARM64_RUNNER_MESSAGE } from "./common";
|
||||
|
||||
export async function installAgent(
|
||||
isTLS: boolean,
|
||||
@@ -26,7 +25,7 @@ export async function installAgent(
|
||||
|
||||
if (isTLS) {
|
||||
downloadPath = await tc.downloadTool(
|
||||
`https://github.com/step-security/agent-ebpf/releases/download/v1.7.14/harden-runner_1.7.14_linux_${variant}.tar.gz`,
|
||||
`https://github.com/step-security/agent-ebpf/releases/download/v1.7.9/harden-runner_1.7.9_linux_${variant}.tar.gz`,
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
@@ -36,15 +35,13 @@ export async function installAgent(
|
||||
return false;
|
||||
}
|
||||
downloadPath = await tc.downloadTool(
|
||||
"https://github.com/step-security/agent/releases/download/v0.14.4/agent_0.14.4_linux_amd64.tar.gz",
|
||||
"https://github.com/step-security/agent/releases/download/v0.14.2/agent_0.14.2_linux_amd64.tar.gz",
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
}
|
||||
|
||||
if (!verifyChecksum(downloadPath, isTLS, variant, "linux")) {
|
||||
return false;
|
||||
}
|
||||
verifyChecksum(downloadPath, isTLS, variant);
|
||||
|
||||
const extractPath = await tc.extractTar(downloadPath);
|
||||
|
||||
@@ -68,157 +65,3 @@ export async function installAgent(
|
||||
cp.execSync("sudo service agent start", { timeout: 15000 });
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function installMacosAgent(configStr: string): Promise<boolean> {
|
||||
const token = core.getInput("token", { required: true });
|
||||
const auth = `token ${token}`;
|
||||
|
||||
try {
|
||||
// Create working directory
|
||||
core.info("Creating /opt/step-security directory...");
|
||||
cp.execSync("sudo mkdir -p /opt/step-security");
|
||||
chownForFolder(process.env.USER, "/opt/step-security");
|
||||
core.info("✓ Successfully created /opt/step-security directory");
|
||||
|
||||
// Create agent configuration file
|
||||
core.info("Creating agent.json");
|
||||
fs.writeFileSync("/opt/step-security/agent.json", configStr);
|
||||
core.info(
|
||||
"✓ Successfully created agent.json at /opt/step-security/agent.json"
|
||||
);
|
||||
|
||||
// Download installer package
|
||||
const downloadUrl =
|
||||
"https://github.com/step-security/agent-releases/releases/download/v0.0.4-mac/macos-installer-0.0.4.tar.gz";
|
||||
core.info(`Downloading macOS installer.. : ${downloadUrl}`);
|
||||
const downloadPath = await tc.downloadTool(downloadUrl, undefined, auth);
|
||||
core.info(`✓ Successfully downloaded installer to: ${downloadPath}`);
|
||||
|
||||
// Verify SHA256 checksum
|
||||
core.info("Verifying SHA256 checksum of downloaded tar file...");
|
||||
if (!verifyChecksum(downloadPath, false, "", "darwin")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract installer package
|
||||
core.info("Extracting installer...");
|
||||
const extractPath = await tc.extractTar(downloadPath);
|
||||
core.info(`✓ Successfully extracted installer to: ${extractPath}`);
|
||||
|
||||
// Copy Installer binary to /opt/step-security
|
||||
const installerSourcePath = path.join(extractPath, "Installer");
|
||||
const installerBinaryPath = "/opt/step-security/Installer";
|
||||
|
||||
core.info(
|
||||
`Copying Installer from ${installerSourcePath} to /opt/step-security...`
|
||||
);
|
||||
cp.execFileSync("cp", [installerSourcePath, installerBinaryPath]);
|
||||
core.info("✓ Successfully copied Installer to /opt/step-security");
|
||||
|
||||
|
||||
// Verify installer binary exists
|
||||
if (!fs.existsSync(installerBinaryPath)) {
|
||||
throw new Error(
|
||||
"Installer binary not found at /opt/step-security/Installer"
|
||||
);
|
||||
}
|
||||
core.info("✓ Installer binary verified");
|
||||
|
||||
// Make installer executable
|
||||
core.info("Making installer executable...");
|
||||
cp.execSync(`chmod +x "${installerBinaryPath}"`);
|
||||
core.info("✓ Installer is now executable");
|
||||
|
||||
// Run installer
|
||||
core.info("Running installer...");
|
||||
cp.execSync(
|
||||
`sudo "${installerBinaryPath}" -workdir /opt/step-security >> /opt/step-security/agent.log 2>&1`,
|
||||
{
|
||||
shell: "/bin/bash",
|
||||
timeout: 10000, // 10 second timeout
|
||||
}
|
||||
);
|
||||
core.info("✓ Installer completed successfully");
|
||||
|
||||
core.info("✅ macOS agent installation completed successfully");
|
||||
return true;
|
||||
} catch (error) {
|
||||
core.error(`❌ Failed to install macOS agent: ${error}`);
|
||||
if (error instanceof Error && error.stack) {
|
||||
core.debug(error.stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function installWindowsAgent(configStr: string): Promise<boolean> {
|
||||
const token = core.getInput("token", { required: true });
|
||||
const auth = `token ${token}`;
|
||||
|
||||
const variant = process.arch === "x64" ? "amd64" : "arm64";
|
||||
if (variant === "arm64") {
|
||||
console.log(ARM64_WINDOWS_RUNNER_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
const agentDir = "C:\\agent";
|
||||
core.info(`Creating agent directory: ${agentDir}`);
|
||||
if (!fs.existsSync(agentDir)) {
|
||||
fs.mkdirSync(agentDir, { recursive: true });
|
||||
}
|
||||
fs.appendFileSync(process.env.GITHUB_STATE, `agentDir=${agentDir}${EOL}`, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
const agentExePath = path.join(agentDir, "agent.exe");
|
||||
|
||||
const downloadPath = await tc.downloadTool(
|
||||
`https://github.com/step-security/agent-releases/releases/download/v1.0.0-win/harden-runner-agent-windows_1.0.0_windows_amd64.tar.gz`,
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
|
||||
// validate the checksum
|
||||
if (!verifyChecksum(downloadPath, false, variant, process.platform)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const extractPath = await tc.extractTar(downloadPath);
|
||||
|
||||
const extractedAgentPath = path.join(extractPath, "agent.exe");
|
||||
fs.copyFileSync(extractedAgentPath, agentExePath);
|
||||
core.info(`Copied agent from ${extractedAgentPath} to ${agentExePath}`);
|
||||
|
||||
const configPath = path.join(agentDir, "config.json");
|
||||
fs.writeFileSync(configPath, configStr);
|
||||
core.info(`Created config file: ${configPath}`);
|
||||
|
||||
core.info("Starting Windows Agent...");
|
||||
|
||||
try {
|
||||
const logPath = path.join(agentDir, "agent.log");
|
||||
const logStream = fs.openSync(logPath, "a");
|
||||
core.info(`Agent logs will be written to: ${logPath}`);
|
||||
|
||||
const agentProcess = cp.spawn(agentExePath, [], {
|
||||
cwd: agentDir,
|
||||
detached: true,
|
||||
stdio: ["ignore", logStream, logStream],
|
||||
windowsHide: false,
|
||||
shell: false,
|
||||
});
|
||||
|
||||
const pidFile = path.join(agentDir, "agent.pid");
|
||||
fs.writeFileSync(pidFile, agentProcess.pid.toString());
|
||||
core.info(`Agent process started with PID: ${agentProcess.pid}`);
|
||||
core.info(`PID saved to: ${pidFile}`);
|
||||
|
||||
agentProcess.unref();
|
||||
|
||||
core.info("Windows Agent process started successfully");
|
||||
return true;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
core.setFailed(`Failed to start Windows agent process: ${errorMessage}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+18
-47
@@ -27,13 +27,7 @@ import * as utils from "@actions/cache/lib/internal/cacheUtils";
|
||||
import { isARCRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
import { STEPSECURITY_API_URL, STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { isGithubHosted, isTLSEnabled } from "./tls-inspect";
|
||||
import {
|
||||
installAgent,
|
||||
installMacosAgent,
|
||||
installWindowsAgent,
|
||||
} from "./install-agent";
|
||||
|
||||
import { chownForFolder, isAgentInstalled, isPlatformSupported } from "./utils";
|
||||
import { installAgent } from "./install-agent";
|
||||
|
||||
interface MonitorResponse {
|
||||
runner_ip_address?: string;
|
||||
@@ -51,8 +45,8 @@ interface MonitorResponse {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPlatformSupported(process.platform)) {
|
||||
console.log(common.UNSUPPORTED_RUNNER_MESSAGE);
|
||||
if (process.platform !== "linux") {
|
||||
console.log(common.UBUNTU_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (isGithubHosted() && isDocker()) {
|
||||
@@ -269,7 +263,7 @@ interface MonitorResponse {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isGithubHosted() && isAgentInstalled(process.platform)) {
|
||||
if (isGithubHosted() && fs.existsSync("/home/agent/agent.status")) {
|
||||
console.log("Agent already installed, skipping installation");
|
||||
return;
|
||||
}
|
||||
@@ -327,47 +321,18 @@ interface MonitorResponse {
|
||||
return;
|
||||
}
|
||||
|
||||
const configStr = JSON.stringify(confg);
|
||||
const confgStr = JSON.stringify(confg);
|
||||
cp.execSync("sudo mkdir -p /home/agent");
|
||||
chownForFolder(process.env.USER, "/home/agent");
|
||||
|
||||
// platform specific
|
||||
let statusFile = "";
|
||||
let logFile = "";
|
||||
let agentInstalled = false;
|
||||
let isTLS = await isTLSEnabled(context.repo.owner);
|
||||
|
||||
switch (process.platform) {
|
||||
case "linux":
|
||||
statusFile = "/home/agent/agent.status";
|
||||
logFile = "/home/agent/agent.log";
|
||||
|
||||
cp.execSync("sudo mkdir -p /home/agent");
|
||||
chownForFolder(process.env.USER, "/home/agent");
|
||||
|
||||
let isTLS = await isTLSEnabled(context.repo.owner);
|
||||
agentInstalled = await installAgent(isTLS, configStr);
|
||||
|
||||
break;
|
||||
case "win32":
|
||||
core.info("Installing Windows Agent...");
|
||||
agentInstalled = await installWindowsAgent(configStr);
|
||||
|
||||
const agentDir = process.env.STATE_agentDir || "C:\\agent";
|
||||
statusFile = path.join(agentDir, "agent.status");
|
||||
logFile = path.join(agentDir, "agent.log");
|
||||
|
||||
break;
|
||||
case "darwin":
|
||||
const installed = await installMacosAgent(configStr);
|
||||
if (!installed) {
|
||||
core.warning("😭 macos agent installation failed");
|
||||
}
|
||||
return; // early return
|
||||
default:
|
||||
throw new Error(
|
||||
`Setup failed because of unsupported platform: ${process.platform}`
|
||||
);
|
||||
}
|
||||
const agentInstalled = await installAgent(isTLS, confgStr);
|
||||
|
||||
if (agentInstalled) {
|
||||
// Check that the file exists locally
|
||||
var statusFile = "/home/agent/agent.status";
|
||||
var logFile = "/home/agent/agent.log";
|
||||
var counter = 0;
|
||||
while (true) {
|
||||
if (!fs.existsSync(statusFile)) {
|
||||
@@ -402,3 +367,9 @@ export function sleep(ms: number) {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
function chownForFolder(newOwner: string, target: string) {
|
||||
let cmd = "sudo";
|
||||
let args = ["chown", "-R", newOwner, target];
|
||||
cp.execFileSync(cmd, args);
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import * as cp from "child_process";
|
||||
import * as fs from "fs";
|
||||
|
||||
export function isPlatformSupported(platform: NodeJS.Platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
case "win32":
|
||||
case "darwin":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function chownForFolder(newOwner: string, target: string) {
|
||||
let cmd = "sudo";
|
||||
let args = ["chown", "-R", newOwner, target];
|
||||
cp.execFileSync(cmd, args);
|
||||
}
|
||||
|
||||
export function isAgentInstalled(platform: NodeJS.Platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
return fs.existsSync("/home/agent/agent.status");
|
||||
case "win32":
|
||||
return fs.existsSync("C:\\agent\\agent.status");
|
||||
case "darwin":
|
||||
return fs.existsSync("/opt/step-security/agent.status");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAnnotationLogs(platform: NodeJS.Platform) {
|
||||
switch (platform) {
|
||||
case "linux":
|
||||
return fs.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
case "win32":
|
||||
return fs.readFileSync("C:\\agent\\annotation.log", "utf8");
|
||||
case "darwin":
|
||||
return fs.readFileSync("/opt/step-security/annotation.log", "utf8");
|
||||
default:
|
||||
throw new Error("platform not supported");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user