Skip to content

Commit f808768

Browse files
Feature/policy store (#656)
1 parent fe10465 commit f808768

File tree

7 files changed

+521
-7
lines changed

7 files changed

+521
-7
lines changed

action.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,17 @@ inputs:
2929
required: false
3030
default: "false"
3131
policy:
32-
description: "Policy name to be used from the policy store"
32+
description: "Policy name to be used from the policy store. Requires id-token: write permission."
3333
required: false
3434
default: ""
35+
api-key:
36+
description: "StepSecurity API key for authenticating with the policy store. Required when use-policy-store is set to true."
37+
required: false
38+
default: ""
39+
use-policy-store:
40+
description: "Set to true to fetch policy from the policy store using the API key. This is the preferred method over the policy input which requires id-token: write permission. Policies can be defined and attached at workflow, repo, org, or cluster (for ARC) level in the policy store. The most granular policy will apply."
41+
required: false
42+
default: "false"
3543

3644
branding:
3745
icon: "check-square"

dist/pre/index.js

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85262,6 +85262,48 @@ function fetchPolicy(owner, policyName, idToken) {
8526285262
}
8526385263
});
8526485264
}
85265+
function fetchPolicyFromStore(owner, repo, apiKey, workflow, runId, correlationId) {
85266+
return policy_utils_awaiter(this, void 0, void 0, function* () {
85267+
if (apiKey === "") {
85268+
throw new Error("[PolicyStoreFetch]: api-key is empty");
85269+
}
85270+
let policyEndpoint = `${configs_STEPSECURITY_API_URL}/github/${owner}/${repo}/actions/policies/workflow-policy?workflow=${encodeURIComponent(workflow)}&run_id=${encodeURIComponent(runId)}&correlationId=${encodeURIComponent(correlationId)}`;
85271+
let httpClient = new lib.HttpClient();
85272+
let headers = {};
85273+
headers["Authorization"] = `vm-api-key ${apiKey}`;
85274+
headers["Source"] = "github-actions";
85275+
let response = undefined;
85276+
let err = undefined;
85277+
let retry = 0;
85278+
while (retry < 3) {
85279+
try {
85280+
console.log(`Attempt: ${retry + 1}`);
85281+
response = yield httpClient.getJson(policyEndpoint, headers);
85282+
break;
85283+
}
85284+
catch (e) {
85285+
err = e;
85286+
}
85287+
retry += 1;
85288+
yield sleep(1000);
85289+
}
85290+
if (response === undefined && err !== undefined) {
85291+
const error = new Error(`[Policy Store Fetch] ${err}`);
85292+
if (err.statusCode !== undefined) {
85293+
error.statusCode = err.statusCode;
85294+
}
85295+
throw error;
85296+
}
85297+
if (response.statusCode === 404) {
85298+
return null;
85299+
}
85300+
const result = response.result;
85301+
if (!result || (!result.egress_policy && (!result.allowed_endpoints || result.allowed_endpoints.length === 0))) {
85302+
return null;
85303+
}
85304+
return result;
85305+
});
85306+
}
8526585307
function mergeConfigs(localConfig, remoteConfig) {
8526685308
if (localConfig.allowed_endpoints === "") {
8526785309
localConfig.allowed_endpoints = remoteConfig.allowed_endpoints.join(" ");
@@ -85624,6 +85666,17 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
8562485666
step((generator = generator.apply(thisArg, _arguments || [])).next());
8562585667
});
8562685668
};
85669+
var __rest = (undefined && undefined.__rest) || function (s, e) {
85670+
var t = {};
85671+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
85672+
t[p] = s[p];
85673+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
85674+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
85675+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
85676+
t[p[i]] = s[p[i]];
85677+
}
85678+
return t;
85679+
};
8562785680

8562885681

8562985682

@@ -85683,9 +85736,47 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
8568385736
is_github_hosted: isGithubHosted(),
8568485737
is_debug: lib_core.isDebug(),
8568585738
one_time_key: "",
85739+
api_key: lib_core.getInput("api-key"),
85740+
use_policy_store: lib_core.getBooleanInput("use-policy-store"),
8568685741
};
85742+
if (confg.api_key !== "") {
85743+
lib_core.setSecret(confg.api_key);
85744+
}
8568785745
let policyName = lib_core.getInput("policy");
85688-
if (policyName !== "") {
85746+
if (confg.use_policy_store) {
85747+
console.log(`Fetching policy from policy store`);
85748+
if (confg.api_key === "") {
85749+
lib_core.setFailed("api-key is required when use-policy-store is set to true");
85750+
}
85751+
else {
85752+
try {
85753+
const repoName = (process.env["GITHUB_REPOSITORY"] || "").split("/")[1] || "";
85754+
const workflowRef = process.env["GITHUB_WORKFLOW_REF"] || "";
85755+
const workflow = workflowRef.replace(/.*\.github\/workflows\//, "").replace(/@.*/, "");
85756+
let result = yield fetchPolicyFromStore(github.context.repo.owner, repoName, confg.api_key, workflow, confg.run_id, confg.correlation_id);
85757+
if (result !== null) {
85758+
lib_core.info(`Policy found: ${result.policy_name || "unnamed"}`);
85759+
confg = mergeConfigs(confg, result);
85760+
}
85761+
else {
85762+
lib_core.info("No policy found in policy store. Defaulting to audit mode.");
85763+
confg.egress_policy = "audit";
85764+
}
85765+
}
85766+
catch (err) {
85767+
lib_core.info(`[!] ${err}`);
85768+
if (err.statusCode >= 400 && err.statusCode < 500) {
85769+
lib_core.info("Policy not found in policy store. Defaulting to audit mode.");
85770+
confg.egress_policy = "audit";
85771+
}
85772+
else {
85773+
lib_core.error(`Unexpected error fetching from policy store: ${err}. Falling back to audit mode.`);
85774+
confg.egress_policy = "audit";
85775+
}
85776+
}
85777+
}
85778+
}
85779+
else if (policyName !== "") {
8568985780
console.log(`Fetching policy from API with name: ${policyName}`);
8569085781
try {
8569185782
let idToken = yield lib_core.getIDToken();
@@ -85858,7 +85949,8 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
8585885949
console.log(HARDEN_RUNNER_UNAVAILABLE_MESSAGE);
8585985950
return;
8586085951
}
85861-
const configStr = JSON.stringify(confg);
85952+
const { api_key, use_policy_store } = confg, agentConfig = __rest(confg, ["api_key", "use_policy_store"]);
85953+
const configStr = JSON.stringify(agentConfig);
8586285954
// platform specific
8586385955
let statusFile = "";
8586485956
let logFile = "";

dist/pre/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/interfaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface Configuration {
1515
private: string;
1616
is_debug: boolean;
1717
one_time_key: string;
18+
api_key: string;
19+
use_policy_store: boolean;
1820
}
1921

2022
export interface PolicyResponse {
@@ -26,4 +28,5 @@ export interface PolicyResponse {
2628
disable_file_monitoring?: boolean;
2729
disable_telemetry?: boolean;
2830
egress_policy?: string;
31+
policy_name?: string;
2932
}

0 commit comments

Comments
 (0)