Compare commits

...
17 Commits
Author SHA1 Message Date
Varun Sharma 9da995c3c2 Merge pull request #638 from h0x0er/rc-35
Add mac and windows changes
2026-02-24 14:14:35 -08:00
Jatin 853e846a24 bump agent-macos installer to v0.0.4 2026-02-24 16:54:36 +05:30
Jatin 0d7fdc082e use agent from prod-releases & resolved comments 2026-02-22 13:07:56 +05:30
Jatin 25b1b6b099 build changes 2026-02-20 12:02:25 +05:30
Jatin 5d1eb77a99 add windows and macos support 2026-02-20 11:58:10 +05:30
Varun Sharma bc791947a3 Merge pull request #634 from h0x0er/jatin/winmac
Add combined windows and macos changes
2026-02-07 22:10:19 -08:00
Varun Sharma 5ef0c079ce Merge pull request #635 from step-security/rc-34
v2.14.2
2026-02-06 17:26:07 -08:00
Varun Sharma eb43c7b3fd update agent 2026-02-06 16:51:21 -08:00
Jatin ec2fee99eb wait for done.json only if agent is installed 2026-02-03 17:59:09 +05:30
Jatin e823d39c9a Fixed verifyChecksum logic and updated macos checksum 2026-02-03 16:06:10 +05:30
Jatin 9a13b2d01e updated macos-installer download-url 2026-02-03 15:33:26 +05:30
Jatin 6061152f29 addressed comments 2026-02-03 12:04:06 +05:30
Jatin ac1419447b Read platform specific annotation-logs & added post-step check in
macos-cleanup
2026-02-02 17:40:08 +05:30
Jatin ef9c6372b3 combined windows and macos changes 2026-02-02 15:04:58 +05:30
Varun Sharma e3f713f2d8 Merge pull request #631 from step-security/rc-31
Release v2.14.1
2026-01-25 21:01:21 -08:00
Varun Sharma 423acdda6f chore: fix npm audit vulnerabilities 2026-01-25 09:03:51 -08:00
Varun Sharma 0ddb86cf03 update agent 2026-01-23 18:38:30 -08:00
14 changed files with 1057 additions and 176 deletions
+52 -5
View File
@@ -31876,8 +31876,53 @@ 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); }); }
@@ -31930,8 +31975,9 @@ function addSummary() {
}
let needsSubscription = false;
try {
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
if (data.includes("StepSecurity Harden Runner is disabled")) {
let data = getAnnotationLogs(process.platform);
if (data !== undefined &&
data.includes("StepSecurity Harden Runner is disabled")) {
needsSubscription = true;
}
}
@@ -31978,7 +32024,7 @@ 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 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 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 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.";
@@ -32083,6 +32129,7 @@ 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");
@@ -32091,8 +32138,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 (process.platform !== "linux") {
console.log(UBUNTU_MESSAGE);
if (!isPlatformSupported(process.platform)) {
console.log(UNSUPPORTED_RUNNER_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+263 -61
View File
@@ -31876,8 +31876,6 @@ 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
@@ -31885,6 +31883,51 @@ 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); }); }
@@ -31937,8 +31980,9 @@ function addSummary() {
}
let needsSubscription = false;
try {
let data = external_fs_.readFileSync("/home/agent/annotation.log", "utf8");
if (data.includes("StepSecurity Harden Runner is disabled")) {
let data = getAnnotationLogs(process.platform);
if (data !== undefined &&
data.includes("StepSecurity Harden Runner is disabled")) {
needsSubscription = true;
}
}
@@ -31985,12 +32029,14 @@ 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 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 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 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.";
// 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
@@ -32132,6 +32178,8 @@ 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");
@@ -32140,8 +32188,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 (process.platform !== "linux") {
console.log(UBUNTU_MESSAGE);
if (!isPlatformSupported(process.platform)) {
console.log(UNSUPPORTED_RUNNER_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
@@ -32158,66 +32206,21 @@ 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;
}
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 {
switch (process.platform) {
case "linux":
yield handleLinuxCleanup();
break;
case "win32":
yield handleWindowsCleanup();
break;
case "darwin":
yield handleMacosCleanup();
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();
@@ -32226,6 +32229,205 @@ 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;
}
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);
+1 -1
View File
File diff suppressed because one or more lines are too long
+234 -33
View File
@@ -85149,6 +85149,49 @@ const validate = dist/* validate */.tf;
const stringify = dist/* stringify */.As;
const parse = dist/* parse */.qg;
;// 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];
external_child_process_.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 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); }); }
@@ -85201,8 +85244,9 @@ function addSummary() {
}
let needsSubscription = false;
try {
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
if (data.includes("StepSecurity Harden Runner is disabled")) {
let data = getAnnotationLogs(process.platform);
if (data !== undefined &&
data.includes("StepSecurity Harden Runner is disabled")) {
needsSubscription = true;
}
}
@@ -85249,7 +85293,7 @@ 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 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 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 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.";
@@ -85495,29 +85539,46 @@ var external_crypto_ = __nccwpck_require__(6982);
const CHECKSUMS = {
tls: {
amd64: "2a5be06ab620340f6957ddd180450caab414671c8b7da1996d8e2755c1cd49fa",
arm64: "e9d0ccf3e4e62ba15ff208ef6e0afa0ad5e323c3dfc50527342f436a85e65a55",
amd64: "19c35eee1347077eb71306b122ad4a1cf83f36ef0f69fd91b0c0d79ffd0eabdd",
arm64: "f9192788e86b2e44b795f072e8cc03eec9852649609aeedac0761d3b67c991fa",
},
non_tls: {
amd64: "336093af8ebe969567b66fd035af3bd4f7e1c723ce680d6b4b5b2a1f79bc329e", // v0.14.2
amd64: "23715f2485c16e2a2ad116abf0fe8443788c62e4f5f224c5858b0b41b591fc89", // v0.14.3
},
darwin: "797399a3a3f6f9c4c000a02e0d8c7b16499129c9bdc2ad9cf2a10072c10654fb",
windows: {
amd64: "e98f8b9cf9ecf6566f1e16a470fbe4aef01610a644fd8203a1bab3ff142186c8", // v1.0.0
},
};
function verifyChecksum(downloadPath, isTLS, variant) {
// verifyChecksum returns true if checksum is valid
function verifyChecksum(downloadPath, isTLS, variant, platform) {
const fileBuffer = external_fs_.readFileSync(downloadPath);
const checksum = external_crypto_.createHash("sha256")
.update(fileBuffer)
.digest("hex"); // checksum of downloaded file
let expectedChecksum = "";
if (isTLS) {
expectedChecksum = CHECKSUMS["tls"][variant];
}
else {
expectedChecksum = CHECKSUMS["non_tls"][variant];
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 (checksum !== expectedChecksum) {
lib_core.setFailed(`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`);
lib_core.setFailed(`❌ Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`);
return false;
}
lib_core.debug("Checksum verification passed.");
lib_core.info(`✅ Checksum verification passed. checksum=${checksum}`);
return true;
}
;// CONCATENATED MODULE: ./src/install-agent.ts
@@ -85538,6 +85599,7 @@ var install_agent_awaiter = (undefined && undefined.__awaiter) || function (this
function installAgent(isTLS, configStr) {
return install_agent_awaiter(this, void 0, void 0, function* () {
// Note: to avoid github rate limiting
@@ -85549,16 +85611,18 @@ function installAgent(isTLS, configStr) {
encoding: "utf8",
});
if (isTLS) {
downloadPath = yield tool_cache.downloadTool(`https://github.com/step-security/agent-ebpf/releases/download/v1.7.9/harden-runner_1.7.9_linux_${variant}.tar.gz`, undefined, auth);
downloadPath = yield tool_cache.downloadTool(`https://github.com/step-security/agent-ebpf/releases/download/v1.7.10/harden-runner_1.7.10_linux_${variant}.tar.gz`, undefined, auth);
}
else {
if (variant === "arm64") {
console.log(ARM64_RUNNER_MESSAGE);
return false;
}
downloadPath = yield tool_cache.downloadTool("https://github.com/step-security/agent/releases/download/v0.14.2/agent_0.14.2_linux_amd64.tar.gz", undefined, auth);
downloadPath = yield tool_cache.downloadTool("https://github.com/step-security/agent/releases/download/v0.14.3/agent_0.14.3_linux_amd64.tar.gz", undefined, auth);
}
if (!verifyChecksum(downloadPath, isTLS, variant, "linux")) {
return false;
}
verifyChecksum(downloadPath, isTLS, variant);
const extractPath = yield tool_cache.extractTar(downloadPath);
let cmd = "cp", args = [external_path_.join(extractPath, "agent"), "/home/agent/agent"];
external_child_process_.execFileSync(cmd, args);
@@ -85576,6 +85640,125 @@ function installAgent(isTLS, configStr) {
return true;
});
}
function installMacosAgent(configStr) {
return install_agent_awaiter(this, void 0, void 0, function* () {
const token = lib_core.getInput("token", { required: true });
const auth = `token ${token}`;
try {
// Create working directory
lib_core.info("Creating /opt/step-security directory...");
external_child_process_.execSync("sudo mkdir -p /opt/step-security");
chownForFolder(process.env.USER, "/opt/step-security");
lib_core.info("✓ Successfully created /opt/step-security directory");
// Create agent configuration file
lib_core.info("Creating agent.json");
external_fs_.writeFileSync("/opt/step-security/agent.json", configStr);
lib_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";
lib_core.info(`Downloading macOS installer.. : ${downloadUrl}`);
const downloadPath = yield tool_cache.downloadTool(downloadUrl, undefined, auth);
lib_core.info(`✓ Successfully downloaded installer to: ${downloadPath}`);
// Verify SHA256 checksum
lib_core.info("Verifying SHA256 checksum of downloaded tar file...");
if (!verifyChecksum(downloadPath, false, "", "darwin")) {
return false;
}
// Extract installer package
lib_core.info("Extracting installer...");
const extractPath = yield tool_cache.extractTar(downloadPath);
lib_core.info(`✓ Successfully extracted installer to: ${extractPath}`);
// Copy Installer binary to /opt/step-security
const installerSourcePath = external_path_.join(extractPath, "Installer");
const installerBinaryPath = "/opt/step-security/Installer";
lib_core.info(`Copying Installer from ${installerSourcePath} to /opt/step-security...`);
external_child_process_.execFileSync("cp", [installerSourcePath, installerBinaryPath]);
lib_core.info("✓ Successfully copied Installer to /opt/step-security");
// Verify installer binary exists
if (!external_fs_.existsSync(installerBinaryPath)) {
throw new Error("Installer binary not found at /opt/step-security/Installer");
}
lib_core.info("✓ Installer binary verified");
// Make installer executable
lib_core.info("Making installer executable...");
external_child_process_.execSync(`chmod +x "${installerBinaryPath}"`);
lib_core.info("✓ Installer is now executable");
// Run installer
lib_core.info("Running installer...");
external_child_process_.execSync(`sudo "${installerBinaryPath}" -workdir /opt/step-security >> /opt/step-security/agent.log 2>&1`, {
shell: "/bin/bash",
timeout: 10000, // 10 second timeout
});
lib_core.info("✓ Installer completed successfully");
lib_core.info("✅ macOS agent installation completed successfully");
return true;
}
catch (error) {
lib_core.error(`❌ Failed to install macOS agent: ${error}`);
if (error instanceof Error && error.stack) {
lib_core.debug(error.stack);
}
return false;
}
});
}
function installWindowsAgent(configStr) {
return install_agent_awaiter(this, void 0, void 0, function* () {
const token = lib_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";
lib_core.info(`Creating agent directory: ${agentDir}`);
if (!external_fs_.existsSync(agentDir)) {
external_fs_.mkdirSync(agentDir, { recursive: true });
}
external_fs_.appendFileSync(process.env.GITHUB_STATE, `agentDir=${agentDir}${external_os_.EOL}`, {
encoding: "utf8",
});
const agentExePath = external_path_.join(agentDir, "agent.exe");
const downloadPath = yield tool_cache.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 = yield tool_cache.extractTar(downloadPath);
const extractedAgentPath = external_path_.join(extractPath, "agent.exe");
external_fs_.copyFileSync(extractedAgentPath, agentExePath);
lib_core.info(`Copied agent from ${extractedAgentPath} to ${agentExePath}`);
const configPath = external_path_.join(agentDir, "config.json");
external_fs_.writeFileSync(configPath, configStr);
lib_core.info(`Created config file: ${configPath}`);
lib_core.info("Starting Windows Agent...");
try {
const logPath = external_path_.join(agentDir, "agent.log");
const logStream = external_fs_.openSync(logPath, "a");
lib_core.info(`Agent logs will be written to: ${logPath}`);
const agentProcess = external_child_process_.spawn(agentExePath, [], {
cwd: agentDir,
detached: true,
stdio: ["ignore", logStream, logStream],
windowsHide: false,
shell: false,
});
const pidFile = external_path_.join(agentDir, "agent.pid");
external_fs_.writeFileSync(pidFile, agentProcess.pid.toString());
lib_core.info(`Agent process started with PID: ${agentProcess.pid}`);
lib_core.info(`PID saved to: ${pidFile}`);
agentProcess.unref();
lib_core.info("Windows Agent process started successfully");
return true;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
lib_core.setFailed(`Failed to start Windows agent process: ${errorMessage}`);
return false;
}
});
}
;// CONCATENATED MODULE: ./src/setup.ts
var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
@@ -85606,6 +85789,7 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
(() => setup_awaiter(void 0, void 0, void 0, function* () {
@@ -85617,8 +85801,8 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
return;
}
if (process.platform !== "linux") {
console.log(UBUNTU_MESSAGE);
if (!isPlatformSupported(process.platform)) {
console.log(UNSUPPORTED_RUNNER_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
@@ -85780,7 +85964,7 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
}
return;
}
if (isGithubHosted() && external_fs_.existsSync("/home/agent/agent.status")) {
if (isGithubHosted() && isAgentInstalled(process.platform)) {
console.log("Agent already installed, skipping installation");
return;
}
@@ -85819,15 +86003,37 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
console.log(HARDEN_RUNNER_UNAVAILABLE_MESSAGE);
return;
}
const confgStr = JSON.stringify(confg);
external_child_process_.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
let isTLS = yield isTLSEnabled(github.context.repo.owner);
const agentInstalled = yield installAgent(isTLS, confgStr);
const configStr = JSON.stringify(confg);
// platform specific
let statusFile = "";
let logFile = "";
let agentInstalled = false;
switch (process.platform) {
case "linux":
statusFile = "/home/agent/agent.status";
logFile = "/home/agent/agent.log";
external_child_process_.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
let isTLS = yield isTLSEnabled(github.context.repo.owner);
agentInstalled = yield installAgent(isTLS, configStr);
break;
case "win32":
lib_core.info("Installing Windows Agent...");
agentInstalled = yield installWindowsAgent(configStr);
const agentDir = process.env.STATE_agentDir || "C:\\agent";
statusFile = external_path_.join(agentDir, "agent.status");
logFile = external_path_.join(agentDir, "agent.log");
break;
case "darwin":
const installed = yield installMacosAgent(configStr);
if (!installed) {
lib_core.warning("😭 macos agent installation failed");
}
return; // early return
default:
throw new Error(`Setup failed because of unsupported platform: ${process.platform}`);
}
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 (!external_fs_.existsSync(statusFile)) {
@@ -85862,11 +86068,6 @@ function setup_sleep(ms) {
setTimeout(resolve, ms);
});
}
function chownForFolder(newOwner, target) {
let cmd = "sudo";
let args = ["chown", "-R", newOwner, target];
external_child_process_.execFileSync(cmd, args);
}
})();
+1 -1
View File
File diff suppressed because one or more lines are too long
+22 -19
View File
@@ -3171,10 +3171,11 @@
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
@@ -5935,10 +5936,11 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -6045,10 +6047,11 @@
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"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"
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
@@ -9896,9 +9899,9 @@
"dev": true
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"dev": true
},
"diff-sequences": {
@@ -11956,9 +11959,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"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==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -12039,9 +12042,9 @@
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"dev": true
},
"lodash.memoize": {
+29 -10
View File
@@ -4,18 +4,24 @@ import * as fs from "fs";
const CHECKSUMS = {
tls: {
amd64: "2a5be06ab620340f6957ddd180450caab414671c8b7da1996d8e2755c1cd49fa", // v1.7.9
arm64: "e9d0ccf3e4e62ba15ff208ef6e0afa0ad5e323c3dfc50527342f436a85e65a55",
amd64: "19c35eee1347077eb71306b122ad4a1cf83f36ef0f69fd91b0c0d79ffd0eabdd", // v1.7.10
arm64: "f9192788e86b2e44b795f072e8cc03eec9852649609aeedac0761d3b67c991fa",
},
non_tls: {
amd64: "336093af8ebe969567b66fd035af3bd4f7e1c723ce680d6b4b5b2a1f79bc329e", // v0.14.2
amd64: "23715f2485c16e2a2ad116abf0fe8443788c62e4f5f224c5858b0b41b591fc89", // v0.14.3
},
darwin: "797399a3a3f6f9c4c000a02e0d8c7b16499129c9bdc2ad9cf2a10072c10654fb", // v0.0.4
windows: {
amd64: "e98f8b9cf9ecf6566f1e16a470fbe4aef01610a644fd8203a1bab3ff142186c8", // v1.0.0
},
};
// verifyChecksum returns true if checksum is valid
export function verifyChecksum(
downloadPath: string,
isTLS: boolean,
variant: string
variant: string,
platform: string
) {
const fileBuffer: Buffer = fs.readFileSync(downloadPath);
const checksum: string = crypto
@@ -25,17 +31,30 @@ export function verifyChecksum(
let expectedChecksum: string = "";
if (isTLS) {
expectedChecksum = CHECKSUMS["tls"][variant];
} else {
expectedChecksum = CHECKSUMS["non_tls"][variant];
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 (checksum !== expectedChecksum) {
core.setFailed(
`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`
`❌ Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`
);
return false;
}
core.debug("Checksum verification passed.");
core.info(`✅ Checksum verification passed. checksum=${checksum}`);
return true;
}
+185 -13
View File
@@ -1,10 +1,13 @@
import * as fs from "fs";
import * as cp from "child_process";
import * as common from "./common";
import * as cp from "child_process";
import * as path from "path";
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");
@@ -14,8 +17,8 @@ import { context } from "@actions/github";
return;
}
if (process.platform !== "linux") {
console.log(common.UBUNTU_MESSAGE);
if (!isPlatformSupported(process.platform)) {
console.log(common.UNSUPPORTED_RUNNER_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
@@ -36,10 +39,6 @@ import { context } from "@actions/github";
return;
}
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
return;
}
if (
String(process.env.STATE_monitorStatusCode) ===
common.STATUS_HARDEN_RUNNER_UNAVAILABLE
@@ -48,6 +47,30 @@ import { context } from "@actions/github";
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;
@@ -69,8 +92,8 @@ import { context } from "@actions/github";
break;
}
await sleep(1000);
} // The file *does* exist
else {
} else {
// The file *does* exist
break;
}
}
@@ -114,13 +137,162 @@ import { context } from "@actions/github";
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;
}
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 {
await common.addSummary();
} catch (exception) {
console.log(exception);
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);
}
})();
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) => {
+12 -9
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 { getAnnotationLogs } from "./utils";
export function printInfo(web_url) {
console.log(
@@ -67,8 +67,11 @@ export async function addSummary() {
let needsSubscription = false;
try {
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
if (data.includes("StepSecurity Harden Runner is disabled")) {
let data = getAnnotationLogs(process.platform);
if (
data !== undefined &&
data.includes("StepSecurity Harden Runner is disabled")
) {
needsSubscription = true;
}
} catch (err) {
@@ -97,23 +100,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;
@@ -128,8 +131,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 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_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 SELF_HOSTED_RUNNER_MESSAGE =
"This job is running on a self-hosted runner.";
+4 -2
View File
@@ -4,6 +4,8 @@ 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");
@@ -13,8 +15,8 @@ import { context } from "@actions/github";
return;
}
if (process.platform !== "linux") {
console.log(common.UBUNTU_MESSAGE);
if (!isPlatformSupported(process.platform)) {
console.log(common.UNSUPPORTED_RUNNER_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
+160 -3
View File
@@ -6,6 +6,7 @@ import * as fs from "fs";
import { verifyChecksum } from "./checksum";
import { EOL } from "os";
import { ARM64_RUNNER_MESSAGE } from "./common";
import { chownForFolder } from "./utils";
export async function installAgent(
isTLS: boolean,
@@ -25,7 +26,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`,
`https://github.com/step-security/agent-ebpf/releases/download/v1.7.10/harden-runner_1.7.10_linux_${variant}.tar.gz`,
undefined,
auth
);
@@ -35,13 +36,15 @@ export async function installAgent(
return false;
}
downloadPath = await tc.downloadTool(
"https://github.com/step-security/agent/releases/download/v0.14.2/agent_0.14.2_linux_amd64.tar.gz",
"https://github.com/step-security/agent/releases/download/v0.14.3/agent_0.14.3_linux_amd64.tar.gz",
undefined,
auth
);
}
verifyChecksum(downloadPath, isTLS, variant);
if (!verifyChecksum(downloadPath, isTLS, variant, "linux")) {
return false;
}
const extractPath = await tc.extractTar(downloadPath);
@@ -65,3 +68,157 @@ 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_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;
}
}
+47 -18
View File
@@ -27,7 +27,13 @@ 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 } from "./install-agent";
import {
installAgent,
installMacosAgent,
installWindowsAgent,
} from "./install-agent";
import { chownForFolder, isAgentInstalled, isPlatformSupported } from "./utils";
interface MonitorResponse {
runner_ip_address?: string;
@@ -45,8 +51,8 @@ interface MonitorResponse {
return;
}
if (process.platform !== "linux") {
console.log(common.UBUNTU_MESSAGE);
if (!isPlatformSupported(process.platform)) {
console.log(common.UNSUPPORTED_RUNNER_MESSAGE);
return;
}
if (isGithubHosted() && isDocker()) {
@@ -263,7 +269,7 @@ interface MonitorResponse {
return;
}
if (isGithubHosted() && fs.existsSync("/home/agent/agent.status")) {
if (isGithubHosted() && isAgentInstalled(process.platform)) {
console.log("Agent already installed, skipping installation");
return;
}
@@ -321,18 +327,47 @@ interface MonitorResponse {
return;
}
const confgStr = JSON.stringify(confg);
cp.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
const configStr = JSON.stringify(confg);
let isTLS = await isTLSEnabled(context.repo.owner);
// platform specific
let statusFile = "";
let logFile = "";
let agentInstalled = false;
const agentInstalled = await installAgent(isTLS, confgStr);
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}`
);
}
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)) {
@@ -367,9 +402,3 @@ 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);
}
+46
View File
@@ -0,0 +1,46 @@
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");
}
}