Compare commits

..
2 Commits
Author SHA1 Message Date
Varun Sharma 730de486fc update agent 2025-07-14 12:29:43 -07:00
Varun Sharma 9bb6cb691f update agent 2025-07-10 22:39:29 -07:00
22 changed files with 22442 additions and 29143 deletions
+23
View File
@@ -0,0 +1,23 @@
name: Code Review
on:
pull_request:
permissions:
contents: read
jobs:
code-review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
api.github.com:443
int.api.stepsecurity.io:443
- name: Code Review
uses: step-security/ai-codewise@int
-29
View File
@@ -44,32 +44,3 @@ jobs:
with:
files: |
reports/*.xml
build-check:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
with:
disable-sudo: true
egress-policy: audit
allowed-endpoints: >
github.com:443
registry.npmjs.org:443
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install Dependencies
run: npm ci
- name: Run build
run: npm run build
- name: Check for changes in dist
run: |
if [[ `git status --porcelain dist` ]]; then
echo "Changes detected in dist directory after build:"
git status --porcelain dist
git diff dist
exit 1
else
echo "No changes in dist directory - build is clean"
fi
+3 -5
View File
@@ -24,14 +24,12 @@ StepSecurity Harden-Runner addresses this gap by providing security monitoring t
- [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 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)
- [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 **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:
Harden-Runner secures over **a 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
@@ -137,7 +135,7 @@ Explore the full feature set in the [Features Documentation](https://docs.stepse
## Trusted By and Case Studies
Harden-Runner is trusted by over 8000 leading open-source projects and enterprises, including Microsoft, Google, Kubernetes, and more.
Harden-Runner is trusted by over 6000 leading open-source projects and enterprises, including Microsoft, Google, Kubernetes, and more.
### Trusted by
+2229 -6471
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2266 -6603
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+17514 -15109
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -3,7 +3,7 @@
### 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.
* 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) as it needs sudo access on the Ubuntu VM to run. It can be used to monitor jobs that use containers to run steps. The limitation is if the entire job is run in a container. That is not common for GitHub Actions workflows, as most of them run directly on `ubuntu-latest`. Note: This is not a limitation for Self-Hosted runners.
### Self-Hosted Actions Runner Controller (ARC) Runners
+254 -541
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -26,7 +26,7 @@
"@actions/cache": "^4.0.0",
"@actions/core": "^1.5.0",
"@actions/exec": "^1.1.0",
"@actions/github": "^6.0.1",
"@actions/github": "^5.0.0",
"@actions/http-client": "^2.0.1",
"@actions/tool-cache": "^1.7.1",
"@babel/helpers": "^7.26.10",
@@ -40,7 +40,7 @@
"@types/node": "^16.9.0",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@vercel/ncc": "^0.38.3",
"@vercel/ncc": "^0.30.0",
"eslint": "^7.32.0",
"eslint-config-google": "^0.14.0",
"jest": "^29.3.1",
+7 -18
View File
@@ -4,22 +4,18 @@ import * as fs from "fs";
const CHECKSUMS = {
tls: {
amd64: "2a5be06ab620340f6957ddd180450caab414671c8b7da1996d8e2755c1cd49fa", // v1.7.9
arm64: "e9d0ccf3e4e62ba15ff208ef6e0afa0ad5e323c3dfc50527342f436a85e65a55",
amd64: "5c02a40df6e2c926c92ffc6bf02ca8a301649d44541ca57e40a87948fb0d3f2e", // v1.6.18
arm64: "b953784a468343c44a1a3ef4ec984c738a9a1e84aaf6932a2bb9dbf7ac7eab29",
},
non_tls: {
amd64: "336093af8ebe969567b66fd035af3bd4f7e1c723ce680d6b4b5b2a1f79bc329e", // v0.14.2
},
windows: {
amd64: "9e4fde66331be3261ae6ff954e531e94335b5774ac7e105f0126b391ee1c6d66", // v1.0.0-int
},
};
export function verifyChecksum(
downloadPath: string,
isTLS: boolean,
variant: string,
platform: string
variant: string
) {
const fileBuffer: Buffer = fs.readFileSync(downloadPath);
const checksum: string = crypto
@@ -29,17 +25,10 @@ export function verifyChecksum(
let expectedChecksum: string = "";
switch (platform) {
case "linux":
expectedChecksum = isTLS
? CHECKSUMS["tls"][variant]
: CHECKSUMS["non_tls"][variant];
break;
case "win32":
expectedChecksum = CHECKSUMS["windows"][variant];
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
if (isTLS) {
expectedChecksum = CHECKSUMS["tls"][variant];
} else {
expectedChecksum = CHECKSUMS["non_tls"][variant];
}
if (checksum !== expectedChecksum) {
+49 -160
View File
@@ -1,22 +1,14 @@
import * as fs from "fs";
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";
(async () => {
console.log("[harden-runner] post-step");
const customProperties = context?.payload?.repository?.custom_properties || {};
if (customProperties["skip-harden-runner"] === "true") {
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
return;
}
if (process.platform !== "linux" && process.platform !== "win32") {
console.log(common.UNSUPPORTED_PLATFORM_MESSAGE);
if (process.platform !== "linux") {
console.log(common.UBUNTU_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
@@ -33,13 +25,7 @@ import { context } from "@actions/github";
return;
}
if (process.env.STATE_customVMImage === "true") {
return;
}
if (process.platform === "linux" && process.env.STATE_isTLS === "false" && process.arch === "arm64") {
return;
} else if (process.platform === "win32" && process.arch === "arm64") {
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
return;
}
@@ -50,163 +36,66 @@ import { context } from "@actions/github";
console.log(common.HARDEN_RUNNER_UNAVAILABLE_MESSAGE);
return;
}
if (process.platform === "linux") {
if (isGithubHosted() && fs.existsSync("/home/agent/post_event.json")) {
console.log("Post step already executed, skipping");
return;
}
fs.writeFileSync(
"/home/agent/post_event.json",
JSON.stringify({ event: "post" })
);
fs.writeFileSync(
"/home/agent/post_event.json",
JSON.stringify({ event: "post" })
);
const doneFile = "/home/agent/done.json";
let counter = 0;
while (true) {
if (!fs.existsSync(doneFile)) {
counter++;
if (counter > 10) {
console.log("timed out");
const doneFile = "/home/agent/done.json";
let counter = 0;
while (true) {
if (!fs.existsSync(doneFile)) {
counter++;
if (counter > 10) {
console.log("timed out");
break;
}
await sleep(1000);
} // The file *does* exist
else {
break;
}
await sleep(1000);
} // The file *does* exist
else {
break;
}
}
const log = "/home/agent/agent.log";
if (fs.existsSync(log)) {
console.log("log:");
const content = fs.readFileSync(log, "utf-8");
console.log(content);
}
const log = "/home/agent/agent.log";
if (fs.existsSync(log)) {
console.log("log:");
var content = fs.readFileSync(log, "utf-8");
console.log(content);
}
const daemonLog = "/home/agent/daemon.log";
if (fs.existsSync(daemonLog)) {
console.log("daemonLog:");
const content = fs.readFileSync(daemonLog, "utf-8");
console.log(content);
}
const daemonLog = "/home/agent/daemon.log";
if (fs.existsSync(daemonLog)) {
console.log("daemonLog:");
var content = fs.readFileSync(daemonLog, "utf-8");
console.log(content);
}
const status = "/home/agent/agent.status";
if (fs.existsSync(status)) {
console.log("status:");
const content = fs.readFileSync(status, "utf-8");
console.log(content);
}
var status = "/home/agent/agent.status";
if (fs.existsSync(status)) {
console.log("status:");
var content = fs.readFileSync(status, "utf-8");
console.log(content);
}
const disable_sudo = process.env.STATE_disableSudo;
const disable_sudo_and_containers = process.env.STATE_disableSudoAndContainers;
if (disable_sudo !== "true" && disable_sudo_and_containers !== "true") {
try {
const journalLog = cp.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);
}
}
} else if (process.platform === "win32") {
// 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;
}
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" }));
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");
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 {
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;
var journalLog = cp.execSync(
"sudo journalctl -u agent.service --lines=1000",
{
encoding: "utf8",
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
}
}
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");
}
);
console.log("agent.service log:");
console.log(journalLog);
} catch (error) {
console.log("warning: error stopping agent process:", error.message);
}
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);
console.log("Warning: Could not fetch service logs:", error.message);
}
}
+70 -31
View File
@@ -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 { STEPSECURITY_WEB_URL } from "./configs";
export function printInfo(web_url) {
console.log(
@@ -59,12 +59,14 @@ export async function addSummary() {
return;
}
const correlation_id = process.env.STATE_correlation_id;
if (!correlation_id) {
const web_url = STEPSECURITY_WEB_URL;
const insights_url = `${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
const log = "/home/agent/agent.log";
if (!fs.existsSync(log)) {
return;
}
let needsSubscription = false;
try {
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
@@ -94,46 +96,83 @@ export async function addSummary() {
return;
}
// 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) {
const content = fs.readFileSync(log, "utf-8");
const lines = content.split("\n");
let tableEntries = [];
for (const line of lines) {
processLogLine(line, tableEntries);
}
if (tableEntries.length === 0) {
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 insightsRow = `<p><b><a href="${insights_url}">📄 View Full Report</a></b></p>`;
await core.summary.addSeparator().addRaw(`<h2>🛡 StepSecurity Report</h2>`);
tableEntries.sort((a, b) => {
if (a.status === "❌ Blocked" && b.status !== "❌ Blocked") {
return -1;
} else if (a.status !== "❌ Blocked" && b.status === "❌ Blocked") {
return 1;
} else {
return 0;
}
const markdownSummary = await response.text();
// Render the markdown summary using core.summary.addRaw
await core.summary.addRaw(markdownSummary).write();
return;
} catch (error) {
console.error(`Error fetching job summary: ${error}`);
return;
}
});
tableEntries = tableEntries.slice(0, 3);
await core.summary.addRaw(`
<blockquote>
<p>Preview of the outbound network calls during this workflow run.</p></blockquote>
<h3>Network Calls</h3>
<table>
<thead>
<tr>
<th>Process</th>
<th>Destination</th>
<th>Status</th>
</tr>
</thead>
<tbody>
${tableEntries
.map(
(entry) => `<tr>
<td><code>${entry.process}</code></td>
<td>${entry.domain.replace(/\.$/, "")}</td>
<td>${entry.status}</td>
</tr>`
)
.join("")}
<tr>
<td><code>...</code></td>
<td><code>...</code></td>
<td><code>...</code></td>
</tr>
</tbody>
</table>
${insightsRow}
`);
await core.summary
.addRaw(
`<p><i>Markdown generated by the <a href="https://github.com/step-security/harden-runner">Harden-Runner GitHub Action</a>.</i></p>`
)
.addSeparator()
.write();
}
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.";
"This job is running in a container. Harden Runner does not run in a container as it needs sudo access to run. 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 UNSUPPORTED_PLATFORM_MESSAGE =
"This job is not running on a supported platform. Harden Runner supports Linux (Ubuntu) and Windows runners. This job will not be monitored.";
export const SELF_HOSTED_RUNNER_MESSAGE =
"This job is running on a self-hosted runner.";
+2 -9
View File
@@ -3,18 +3,11 @@ import * as core from "@actions/core";
import isDocker from "is-docker";
import { STEPSECURITY_WEB_URL } from "./configs";
import { isGithubHosted } from "./tls-inspect";
import { context } from "@actions/github";
(async () => {
console.log("[harden-runner] main-step");
const customProperties = context?.payload?.repository?.custom_properties || {};
if (customProperties["skip-harden-runner"] === "true") {
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
return;
}
if (process.platform !== "linux" && process.platform !== "win32") {
console.log(common.UNSUPPORTED_PLATFORM_MESSAGE);
if (process.platform !== "linux") {
console.log(common.UBUNTU_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
+2 -79
View File
@@ -25,9 +25,7 @@ export async function installAgent(
if (isTLS) {
downloadPath = await tc.downloadTool(
`https://github.com/step-security/agent-ebpf/releases/download/v1.7.9/harden-runner_1.7.9_linux_${variant}.tar.gz`,
undefined,
auth
`https://packages.stepsecurity.io/github-hosted/harden-runner_1.6.18_linux_${variant}.tar.gz`
);
} else {
if (variant === "arm64") {
@@ -41,7 +39,7 @@ export async function installAgent(
);
}
verifyChecksum(downloadPath, isTLS, variant, process.platform);
verifyChecksum(downloadPath, isTLS, variant);
const extractPath = await tc.extractTar(downloadPath);
@@ -65,78 +63,3 @@ export async function installAgent(
cp.execSync("sudo service agent start", { timeout: 15000 });
return true;
}
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_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-int/harden-runner-agent-windows_int_windows_amd64.tar.gz `,
undefined,
auth
);
verifyChecksum(downloadPath, false, variant, process.platform);
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) {
core.setFailed(
`Failed to start Windows agent process: ${error.message}`
);
return false;
}
}
+2 -7
View File
@@ -39,12 +39,7 @@ export async function fetchPolicy(
}
if (response === undefined && err !== undefined) {
// Preserve the original error's statusCode if it exists
const error = new Error(`[Policy Fetch] ${err}`);
if (err.statusCode !== undefined) {
(error as any).statusCode = err.statusCode;
}
throw error;
throw new Error(`[Policy Fetch] ${err}`);
} else {
return response.result;
}
@@ -75,7 +70,7 @@ export function mergeConfigs(
return localConfig;
}
function sleep(ms: number) {
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
+12 -72
View File
@@ -27,7 +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, installWindowsAgent } from "./install-agent";
import { installAgent } from "./install-agent";
interface MonitorResponse {
runner_ip_address?: string;
@@ -39,14 +39,8 @@ interface MonitorResponse {
try {
console.log("[harden-runner] pre-step");
const customProperties = context?.payload?.repository?.custom_properties || {};
if (customProperties["skip-harden-runner"] === "true") {
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
return;
}
if (process.platform !== "linux" && process.platform !== "win32") {
console.log(common.UNSUPPORTED_PLATFORM_MESSAGE);
if (process.platform !== "linux") {
console.log(common.UBUNTU_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
@@ -91,18 +85,7 @@ interface MonitorResponse {
confg = mergeConfigs(confg, result);
} catch (err) {
core.info(`[!] ${err}`);
// Only fail the job if ID token is not available
if (err.message && err.message.includes('Unable to get ACTIONS_ID_TOKEN_REQUEST')) {
core.setFailed('Policy store requires id-token write permission as it uses OIDC to fetch the policy from StepSecurity API. Please add "id-token: write" to your job permissions.');
} else {
// Handle different HTTP status codes
if (err.statusCode >= 400 && err.statusCode < 500) {
core.error('Policy not found');
} else {
core.error(`Unexpected error occurred: ${err}. Falling back to egress policy audit`);
confg.egress_policy = 'audit';
}
}
core.setFailed(err);
}
}
fs.appendFileSync(
@@ -249,27 +232,8 @@ interface MonitorResponse {
return;
}
if (isGithubHosted() && process.env.STEP_SECURITY_HARDEN_RUNNER === "true") {
fs.appendFileSync(process.env.GITHUB_STATE, `customVMImage=true${EOL}`, {
encoding: "utf8",
});
core.info("This job is running on a custom VM image with Harden Runner installed.");
if (confg.egress_policy === "block") {
sendAllowedEndpoints(confg.allowed_endpoints);
await sleep(5000);
}
return;
}
if (isGithubHosted() && fs.existsSync("/home/agent/agent.status")) {
console.log("Agent already installed, skipping installation");
return;
}
let _http = new httpm.HttpClient();
let statusCode: number | undefined;
let statusCode;
_http.requestOptions = { socketTimeout: 3 * 1000 };
let addSummary = "false";
try {
@@ -307,13 +271,6 @@ interface MonitorResponse {
encoding: "utf8",
}
);
fs.appendFileSync(
process.env.GITHUB_STATE,
`correlation_id=${correlation_id}${EOL}`,
{
encoding: "utf8",
}
);
console.log(`Step Security Job Correlation ID: ${correlation_id}`);
if (String(statusCode) === common.STATUS_HARDEN_RUNNER_UNAVAILABLE) {
@@ -322,34 +279,17 @@ interface MonitorResponse {
}
const confgStr = JSON.stringify(confg);
// install agent based on platform
let agentInstalled = false;
let statusFile: string;
let logFile: string;
cp.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
if (process.platform === "win32") {
// Windows installation
core.info("Installing Windows Agent...");
agentInstalled = await installWindowsAgent(confgStr);
let isTLS = await isTLSEnabled(context.repo.owner);
const agentDir = process.env.STATE_agentDir || "C:\\agent";
statusFile = path.join(agentDir, "agent.status");
logFile = path.join(agentDir, "agent.log");
} else {
// Linux installation
cp.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
let isTLS = await isTLSEnabled(context.repo.owner);
agentInstalled = await installAgent(isTLS, confgStr);
statusFile = "/home/agent/agent.status";
logFile = "/home/agent/agent.log";
}
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)) {
@@ -379,7 +319,7 @@ interface MonitorResponse {
process.exit(0);
})();
export function sleep(ms: number) {
export function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});