Compare commits

...

32 commits

Author SHA1 Message Date
Varun Sharma
550c2d137a update 2023-11-13 19:48:31 -08:00
Varun Sharma
a2d24fd62e add timeout 2023-11-13 19:47:23 -08:00
Varun Sharma
7bca9467a9 update 2023-11-13 19:43:53 -08:00
Varun Sharma
787deb7075 update 2023-11-13 19:34:10 -08:00
Varun Sharma
34aaaff088 update 2023-11-13 19:14:30 -08:00
Varun Sharma
1cc5c91d22 Update 2023-11-13 18:57:27 -08:00
Varun Sharma
310d9e1272 update 2023-11-13 18:48:43 -08:00
Varun Sharma
36b4d31475 Update 2023-11-13 18:43:32 -08:00
Varun Sharma
061aec38f6 Older ncc 2023-11-13 16:17:39 -08:00
Varun Sharma
70e522ccac update 2023-11-13 16:08:04 -08:00
Varun Sharma
b15422ae8d Remove while loop 2023-11-13 16:02:18 -08:00
Varun Sharma
7ceaa3a76e Add return 2023-11-13 15:53:52 -08:00
Varun Sharma
830048ccdb Add logs 2023-11-13 15:41:47 -08:00
Varun Sharma
d1ed228523 Update dependencies 2023-11-13 13:59:57 -08:00
Varun Sharma
7fd1ef6945 Update packages 2023-11-13 13:14:11 -08:00
Varun Sharma
987bde6ebb Use node20 2023-11-13 13:08:25 -08:00
Varun Sharma
2750bff0ce Update README 2023-11-08 16:07:41 -08:00
Varun Sharma
7c994b37b5 Update markdown 2023-11-08 14:41:52 -08:00
Varun Sharma
ddd6620b25 Update markdown 2023-11-05 21:42:42 -08:00
Varun Sharma
b11d6ca1b9 Update dist and version 2023-10-27 15:33:57 -07:00
Varun Sharma
9cef6384fa
Merge pull request #355 from step-security/update-packages
Update package-lock.json
2023-10-27 15:31:13 -07:00
Varun Sharma
b2d3a9faf3
Merge pull request #354 from step-security/fix-cache
Fix cache
2023-10-27 15:30:45 -07:00
Varun Sharma
03369ff6e2 Merge branch 'rc-v2.6.1' into fix-cache 2023-10-27 15:30:21 -07:00
Varun Sharma
b750996025 Merge branch 'rc-v2.6.1' into fix-cache 2023-10-27 15:28:53 -07:00
Varun Sharma
d4ca02bcc4
Merge pull request #353 from step-security/markdown
Update markdown
2023-10-27 15:27:42 -07:00
Varun Sharma
fc9d972ad4 Merge branch 'rc-v2.6.1' into markdown 2023-10-27 15:26:34 -07:00
Varun Sharma
5037832655 Update package-lock.json 2023-10-27 15:17:30 -07:00
Varun Sharma
27f4fdf2ed Update dist 2023-10-27 14:46:17 -07:00
Varun Sharma
4f41e47d74 Fix cache issue 2023-10-27 14:43:31 -07:00
Varun Sharma
0827586e44 Update markdown 2023-10-27 13:37:20 -07:00
Varun Sharma
f5cfd3fe85 Update markdown 2023-10-27 13:19:55 -07:00
jatin
abb3730f28
Add k8s-mode detection (#345) 2023-10-27 12:58:37 -07:00
19 changed files with 128521 additions and 119948 deletions

View file

@ -140,6 +140,20 @@ Once allowed endpoints are set in the policy in the workflow file, or in the [Po
<img src="images/blocked-outbound-call-2.png" alt="Policy recommended by harden-runner" > <img src="images/blocked-outbound-call-2.png" alt="Policy recommended by harden-runner" >
</p> </p>
### 🤖 Select GitHub Actions with Confidence
> Applies to both GitHub-hosted and self-hosted runners
Before integrating a GitHub Action into your workflow, evaluate its network activity.
- Access network behavior data for hundreds of GitHub Actions, compiled from thousands of workflow runs that use Harden-Runner in public repositories
- Search for a GitHub Action and view a comprehensive report of its outbound network traffic
- Use this insight to make educated choices about incorporating GitHub Actions into your projects
<p align="left">
<img src="images/network-behavior.png" alt="Networking behavior of an Action" >
</p>
### 📁 Detect tampering of source code during build ### 📁 Detect tampering of source code during build
> Applies to both GitHub-hosted and self-hosted runners > Applies to both GitHub-hosted and self-hosted runners

View file

@ -33,7 +33,7 @@ branding:
icon: "check-square" icon: "check-square"
color: "green" color: "green"
runs: runs:
using: "node16" using: "node20"
pre: "dist/pre/index.js" pre: "dist/pre/index.js"
main: "dist/index.js" main: "dist/index.js"
post: "dist/post/index.js" post: "dist/post/index.js"

24652
dist/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

82429
dist/post/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

65530
dist/pre/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
images/network-behavior.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

10221
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "step-security-harden-runner", "name": "step-security-harden-runner",
"version": "2.5.1", "version": "2.6.1",
"description": "Security agent for GitHub-hosted runner: block egress traffic & detect code overwrite to prevent breaches", "description": "Security agent for GitHub-hosted runner: block egress traffic & detect code overwrite to prevent breaches",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -26,27 +26,27 @@
"@actions/cache": "^3.1.4", "@actions/cache": "^3.1.4",
"@actions/core": "^1.5.0", "@actions/core": "^1.5.0",
"@actions/exec": "^1.1.0", "@actions/exec": "^1.1.0",
"@actions/github": "^5.0.0", "@actions/github": "^6.0.0",
"@actions/http-client": "^2.0.1", "@actions/tool-cache": "^2.0.1",
"@actions/tool-cache": "^1.7.1",
"ansi-regex": ">=5.0.1", "ansi-regex": ">=5.0.1",
"axios": "^1.6.1",
"is-docker": "^3.0.0", "is-docker": "^3.0.0",
"node-fetch": ">=3.2.0", "node-fetch": ">=3.2.0",
"uuid": "^8.3.2" "uuid": "^9.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.5.2", "@types/jest": "^29.5.8",
"@types/node": "^16.9.0", "@types/node": "^20.9.0",
"@typescript-eslint/eslint-plugin": "^6.1.0", "@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0", "@typescript-eslint/parser": "^6.1.0",
"@vercel/ncc": "^0.30.0", "@vercel/ncc": "0.38.0",
"eslint": "^7.32.0", "eslint": "^8.53.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"jest": "^29.3.1", "jest": "^29.3.1",
"jest-junit": ">=13.0.0", "jest-junit": ">=13.0.0",
"nock": "^13.3.0", "nock": "^13.3.0",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.3.5" "typescript": "^5.2.2"
} }
} }

View file

@ -1,14 +1,24 @@
import * as cp from "child_process"; import * as cp from "child_process";
import * as fs from "fs";
import { sleep } from "./setup"; import { sleep } from "./setup";
export function isArcRunner(): boolean { export function isArcRunner(): boolean {
const runnerUserAgent = process.env["GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT"]; const runnerUserAgent = process.env["GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT"];
let isARC = false;
if (!runnerUserAgent) { if (!runnerUserAgent) {
return false; isARC = false;
} else {
isARC = runnerUserAgent.includes("actions-runner-controller/");
} }
return runnerUserAgent.includes("actions-runner-controller/"); return isARC || isSecondaryPod();
}
function isSecondaryPod(): boolean {
const workDir = "/__w";
return fs.existsSync(workDir);
} }
function getRunnerTempDir(): string { function getRunnerTempDir(): string {

View file

@ -1,11 +1,7 @@
import * as fs from "fs"; import * as fs from "fs";
import * as cp from "child_process"; import * as cp from "child_process";
import * as core from "@actions/core";
import * as common from "./common"; import * as common from "./common";
import isDocker from "is-docker"; import isDocker from "is-docker";
import * as cache from "@actions/cache";
import { cacheFile, cacheKey, isValidEvent } from "./cache";
import path from "path";
import { arcCleanUp, isArcRunner, removeStepPolicyFiles } from "./arc-runner"; import { arcCleanUp, isArcRunner, removeStepPolicyFiles } from "./arc-runner";
(async () => { (async () => {
@ -18,18 +14,6 @@ import { arcCleanUp, isArcRunner, removeStepPolicyFiles } from "./arc-runner";
return; return;
} }
if (isValidEvent()) {
try {
const cacheResult = await cache.saveCache(
[path.join(__dirname, "cache.txt")],
cacheKey
);
console.log(cacheResult);
} catch (exception) {
console.log(exception);
}
}
if (isArcRunner()) { if (isArcRunner()) {
console.log(`[!] ${common.ARC_RUNNER_MESSAGE}`); console.log(`[!] ${common.ARC_RUNNER_MESSAGE}`);
arcCleanUp(); arcCleanUp();
@ -54,8 +38,8 @@ import { arcCleanUp, isArcRunner, removeStepPolicyFiles } from "./arc-runner";
JSON.stringify({ event: "post" }) JSON.stringify({ event: "post" })
); );
var doneFile = "/home/agent/done.json"; const doneFile = "/home/agent/done.json";
var counter = 0; let counter = 0;
while (true) { while (true) {
if (!fs.existsSync(doneFile)) { if (!fs.existsSync(doneFile)) {
counter++; counter++;
@ -71,7 +55,7 @@ import { arcCleanUp, isArcRunner, removeStepPolicyFiles } from "./arc-runner";
} }
} }
var log = "/home/agent/agent.log"; const log = "/home/agent/agent.log";
if (fs.existsSync(log)) { if (fs.existsSync(log)) {
console.log("log:"); console.log("log:");
var content = fs.readFileSync(log, "utf-8"); var content = fs.readFileSync(log, "utf-8");

View file

@ -101,9 +101,9 @@ export async function addSummary() {
return; return;
} }
const insightsRow = `<h4><a href="${insights_url}">View Full Runtime Security Report & Recommended Policy</a></h4>`; const insightsRow = `<p><b><a href="${insights_url}">📄 View Full Report</a></b></p>`;
await core.summary.addSeparator().addRaw(`<h2>StepSecurity Report</h2>`); await core.summary.addSeparator().addRaw(`<h2>🛡 StepSecurity Report</h2>`);
tableEntries.sort((a, b) => { tableEntries.sort((a, b) => {
if (a.status === "❌ Blocked" && b.status !== "❌ Blocked") { if (a.status === "❌ Blocked" && b.status !== "❌ Blocked") {
@ -118,8 +118,9 @@ export async function addSummary() {
tableEntries = tableEntries.slice(0, 3); tableEntries = tableEntries.slice(0, 3);
await core.summary.addRaw(` await core.summary.addRaw(`
<p>Preview of the network events that occurred on the GitHub-hosted runner during this workflow run.</p> <blockquote>
<h3>🌐 Network Events</h3> <p>Preview of the outbound network calls during this workflow run.</p></blockquote>
<h3>Network Calls</h3>
<table> <table>
<thead> <thead>
<tr> <tr>
@ -132,16 +133,16 @@ export async function addSummary() {
${tableEntries ${tableEntries
.map( .map(
(entry) => `<tr> (entry) => `<tr>
<td>${entry.process}</td> <td><code>${entry.process}</code></td>
<td>${entry.domain.replace(/\.$/, "")}</td> <td>${entry.domain.replace(/\.$/, "")}</td>
<td>${entry.status}</td> <td>${entry.status}</td>
</tr>` </tr>`
) )
.join("")} .join("")}
<tr> <tr>
<td>...</td> <td><code>...</code></td>
<td>...</td> <td><code>...</code></td>
<td>...</td> <td><code>...</code></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -150,7 +151,7 @@ export async function addSummary() {
await core.summary await core.summary
.addRaw( .addRaw(
`<p>Markdown generated by the <a href="https://github.com/step-security/harden-runner">Harden-Runner GitHub Action</a></p>` `<p><i>Markdown generated by the <a href="https://github.com/step-security/harden-runner">Harden-Runner GitHub Action</a>.</i></p>`
) )
.addSeparator() .addSeparator()
.write(); .write();

View file

@ -1,7 +1,7 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as cp from "child_process"; import * as cp from "child_process";
import * as fs from "fs"; import * as fs from "fs";
import * as httpm from "@actions/http-client"; import axios from "axios";
import * as path from "path"; import * as path from "path";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import * as common from "./common"; import * as common from "./common";
@ -19,7 +19,7 @@ import {
} from "./cache"; } from "./cache";
import { Configuration, PolicyResponse } from "./interfaces"; import { Configuration, PolicyResponse } from "./interfaces";
import { fetchPolicy, mergeConfigs } from "./policy-utils"; import { fetchPolicy, mergeConfigs } from "./policy-utils";
import * as cache from "@actions/cache";
import { getCacheEntry } from "@actions/cache/lib/internal/cacheHttpClient"; import { getCacheEntry } from "@actions/cache/lib/internal/cacheHttpClient";
import * as utils from "@actions/cache/lib/internal/cacheUtils"; import * as utils from "@actions/cache/lib/internal/cacheUtils";
import { isArcRunner, sendAllowedEndpoints } from "./arc-runner"; import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
@ -54,22 +54,6 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
private: context?.payload?.repository?.private || false, private: context?.payload?.repository?.private || false,
}; };
let policyName = core.getInput("policy");
if (policyName !== "") {
console.log(`Fetching policy from API with name: ${policyName}`);
try {
let idToken: string = await core.getIDToken();
let result: PolicyResponse = await fetchPolicy(
context.repo.owner,
policyName,
idToken
);
confg = mergeConfigs(confg, result);
} catch (err) {
core.info(`[!] ${err}`);
core.setFailed(err);
}
}
fs.appendFileSync( fs.appendFileSync(
process.env.GITHUB_STATE, process.env.GITHUB_STATE,
`disableSudo=${confg.disable_sudo}${EOL}`, `disableSudo=${confg.disable_sudo}${EOL}`,
@ -93,13 +77,20 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
core.setFailed("disable-telemetry must be a boolean value"); core.setFailed("disable-telemetry must be a boolean value");
} }
if (isValidEvent()) { if (isValidEvent() && confg.egress_policy === "block") {
try { try {
let compressionMethod: CompressionMethod = const cacheResult = await cache.saveCache(
[path.join(__dirname, "cache.txt")],
cacheKey
);
console.log(cacheResult);
} catch (exception) {
console.log(exception);
}
try {
const compressionMethod: CompressionMethod =
await utils.getCompressionMethod(); await utils.getCompressionMethod();
const cacheFilePath = path.join(__dirname, "cache.txt");
let cacheFilePath = path.join(__dirname, "cache.txt");
cacheFilePath = cacheFilePath.replace("/pre/", "/post/");
core.info(`cacheFilePath ${cacheFilePath}`); core.info(`cacheFilePath ${cacheFilePath}`);
const cacheEntry: ArtifactCacheEntry = await getCacheEntry( const cacheEntry: ArtifactCacheEntry = await getCacheEntry(
[cacheKey], [cacheKey],
@ -158,15 +149,18 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
} }
return; return;
} }
//return;
let _http = new httpm.HttpClient();
let statusCode; let statusCode;
_http.requestOptions = { socketTimeout: 3 * 1000 }; //_http.requestOptions = { socketTimeout: 3 * 1000 };
try { try {
const resp: httpm.HttpClientResponse = await _http.get( const resp = await axios.get(
`${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor` `${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor`,
{ timeout: 3000 }
); );
statusCode = resp.message.statusCode; // adding error code to check whether agent is getting installed or not.
statusCode = resp.status; // adding error code to check whether agent is getting installed or not.
console.log(`statuscode: ${statusCode}`);
fs.appendFileSync( fs.appendFileSync(
process.env.GITHUB_STATE, process.env.GITHUB_STATE,
`monitorStatusCode=${statusCode}${EOL}`, `monitorStatusCode=${statusCode}${EOL}`,
@ -177,13 +171,13 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
} catch (e) { } catch (e) {
console.log(`error in connecting to ${api_url}: ${e}`); console.log(`error in connecting to ${api_url}: ${e}`);
} }
//return;
console.log(`Step Security Job Correlation ID: ${correlation_id}`); console.log(`Step Security Job Correlation ID: ${correlation_id}`);
if (String(statusCode) === common.STATUS_HARDEN_RUNNER_UNAVAILABLE) { if (String(statusCode) === common.STATUS_HARDEN_RUNNER_UNAVAILABLE) {
console.log(common.HARDEN_RUNNER_UNAVAILABLE_MESSAGE); console.log(common.HARDEN_RUNNER_UNAVAILABLE_MESSAGE);
return; return;
} }
//return;
const confgStr = JSON.stringify(confg); const confgStr = JSON.stringify(confg);
cp.execSync("sudo mkdir -p /home/agent"); cp.execSync("sudo mkdir -p /home/agent");
cp.execSync("sudo chown -R $USER /home/agent"); cp.execSync("sudo chown -R $USER /home/agent");
@ -200,7 +194,7 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
verifyChecksum(downloadPath); // NOTE: verifying agent's checksum, before extracting verifyChecksum(downloadPath); // NOTE: verifying agent's checksum, before extracting
const extractPath = await tc.extractTar(downloadPath); const extractPath = await tc.extractTar(downloadPath);
//return;
let cmd = "cp", let cmd = "cp",
args = [path.join(extractPath, "agent"), "/home/agent/agent"]; args = [path.join(extractPath, "agent"), "/home/agent/agent"];
cp.execFileSync(cmd, args); cp.execFileSync(cmd, args);
@ -222,6 +216,7 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
var statusFile = "/home/agent/agent.status"; var statusFile = "/home/agent/agent.status";
var logFile = "/home/agent/agent.log"; var logFile = "/home/agent/agent.log";
var counter = 0; var counter = 0;
await sleep(5000);
while (true) { while (true) {
if (!fs.existsSync(statusFile)) { if (!fs.existsSync(statusFile)) {
counter++; counter++;
@ -233,6 +228,7 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
} }
break; break;
} }
console.log("sleeping");
await sleep(300); await sleep(300);
} // The file *does* exist } // The file *does* exist
else { else {
@ -245,6 +241,8 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
} }
console.log("method done");
return;
})(); })();
export function sleep(ms) { export function sleep(ms) {