Obtain job-level token by default for less privileged runs (#198)
This commit is contained in:
committed by
pullfrog[bot]
parent
bb7e7584d4
commit
2daab6fc78
@@ -30,6 +30,10 @@ inputs:
|
|||||||
bash:
|
bash:
|
||||||
description: "Bash permission: disabled, restricted (filters secrets from env vars), or enabled. Public repos default to restricted for security; private repos default to enabled."
|
description: "Bash permission: disabled, restricted (filters secrets from env vars), or enabled. Public repos default to restricted for security; private repos default to enabled."
|
||||||
required: false
|
required: false
|
||||||
|
token:
|
||||||
|
description: "GitHub-provided token with job-scoped permissions. Do not set this unless you know what you are doing."
|
||||||
|
required: false
|
||||||
|
default: ${{ github.token }}
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "node24"
|
using: "node24"
|
||||||
|
|||||||
@@ -19732,7 +19732,7 @@ var require_core = __commonJS({
|
|||||||
process.env["PATH"] = `${inputPath}${path4.delimiter}${process.env["PATH"]}`;
|
process.env["PATH"] = `${inputPath}${path4.delimiter}${process.env["PATH"]}`;
|
||||||
}
|
}
|
||||||
exports.addPath = addPath;
|
exports.addPath = addPath;
|
||||||
function getInput2(name, options) {
|
function getInput3(name, options) {
|
||||||
const val = process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
const val = process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||||
if (options && options.required && !val) {
|
if (options && options.required && !val) {
|
||||||
throw new Error(`Input required and not supplied: ${name}`);
|
throw new Error(`Input required and not supplied: ${name}`);
|
||||||
@@ -19742,9 +19742,9 @@ var require_core = __commonJS({
|
|||||||
}
|
}
|
||||||
return val.trim();
|
return val.trim();
|
||||||
}
|
}
|
||||||
exports.getInput = getInput2;
|
exports.getInput = getInput3;
|
||||||
function getMultilineInput(name, options) {
|
function getMultilineInput(name, options) {
|
||||||
const inputs = getInput2(name, options).split("\n").filter((x) => x !== "");
|
const inputs = getInput3(name, options).split("\n").filter((x) => x !== "");
|
||||||
if (options && options.trimWhitespace === false) {
|
if (options && options.trimWhitespace === false) {
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
@@ -19754,7 +19754,7 @@ var require_core = __commonJS({
|
|||||||
function getBooleanInput(name, options) {
|
function getBooleanInput(name, options) {
|
||||||
const trueValue = ["true", "True", "TRUE"];
|
const trueValue = ["true", "True", "TRUE"];
|
||||||
const falseValue = ["false", "False", "FALSE"];
|
const falseValue = ["false", "False", "FALSE"];
|
||||||
const val = getInput2(name, options);
|
const val = getInput3(name, options);
|
||||||
if (trueValue.includes(val))
|
if (trueValue.includes(val))
|
||||||
return true;
|
return true;
|
||||||
if (falseValue.includes(val))
|
if (falseValue.includes(val))
|
||||||
@@ -141999,29 +141999,40 @@ function AwaitDependencyInstallationTool(ctx) {
|
|||||||
var core3 = __toESM(require_core(), 1);
|
var core3 = __toESM(require_core(), 1);
|
||||||
import assert2 from "node:assert/strict";
|
import assert2 from "node:assert/strict";
|
||||||
var githubInstallationToken;
|
var githubInstallationToken;
|
||||||
|
function setEnvironmentVariable(name, value2) {
|
||||||
|
const hadValue = Object.hasOwn(process.env, name);
|
||||||
|
const originalValue = process.env[name];
|
||||||
|
if (typeof value2 === "string") {
|
||||||
|
process.env[name] = value2;
|
||||||
|
} else {
|
||||||
|
delete process.env[name];
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (hadValue) {
|
||||||
|
process.env[name] = originalValue;
|
||||||
|
} else {
|
||||||
|
delete process.env[name];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
async function resolveInstallationToken() {
|
async function resolveInstallationToken() {
|
||||||
assert2(!githubInstallationToken, "GitHub installation token is already set.");
|
assert2(!githubInstallationToken, "GitHub installation token is already set.");
|
||||||
const originalToken = process.env.GITHUB_TOKEN;
|
const githubJobToken = core3.getInput("token");
|
||||||
if (originalToken) {
|
|
||||||
process.env.ORIGINAL_GITHUB_TOKEN = originalToken;
|
|
||||||
}
|
|
||||||
const externalToken = process.env.GH_TOKEN;
|
const externalToken = process.env.GH_TOKEN;
|
||||||
const token = externalToken || await acquireNewToken();
|
const token = externalToken || await acquireNewToken();
|
||||||
process.env.GITHUB_TOKEN = token;
|
const revertGithubToken = setEnvironmentVariable("GITHUB_TOKEN", token);
|
||||||
githubInstallationToken = token;
|
githubInstallationToken = token;
|
||||||
if (isGitHubActions) {
|
if (isGitHubActions) {
|
||||||
core3.setSecret(token);
|
core3.setSecret(token);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
originalToken,
|
// in GitHub Actions environment this fallback token should always come from the action's input
|
||||||
|
// but in other environments there is no secondary token like this so we just use the installation token itself
|
||||||
|
githubJobToken,
|
||||||
async [Symbol.asyncDispose]() {
|
async [Symbol.asyncDispose]() {
|
||||||
githubInstallationToken = void 0;
|
githubInstallationToken = void 0;
|
||||||
if (originalToken) {
|
revertGithubToken();
|
||||||
process.env.GITHUB_TOKEN = originalToken;
|
|
||||||
} else {
|
|
||||||
delete process.env.GITHUB_TOKEN;
|
|
||||||
}
|
|
||||||
if (externalToken) {
|
if (externalToken) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -162542,7 +162553,11 @@ async function setupGit(params) {
|
|||||||
} catch {
|
} catch {
|
||||||
log.debug("\xBB no existing authentication headers to remove");
|
log.debug("\xBB no existing authentication headers to remove");
|
||||||
}
|
}
|
||||||
const originToken = params.bashPermission === "enabled" ? params.token : params.originalToken || params.token;
|
const originToken = params.bashPermission === "enabled" ? params.token : (
|
||||||
|
// in GitHub Actions environment this less-capable job token should always be available in the action's input
|
||||||
|
// but in other environments there is no secondary token like this so we just use the installation token itself
|
||||||
|
params.githubJobToken || params.token
|
||||||
|
);
|
||||||
if (params.event.is_pr !== true || !params.event.issue_number) {
|
if (params.event.is_pr !== true || !params.event.issue_number) {
|
||||||
const originUrl2 = `https://x-access-token:${originToken}@github.com/${params.owner}/${params.name}.git`;
|
const originUrl2 = `https://x-access-token:${originToken}@github.com/${params.owner}/${params.name}.git`;
|
||||||
$("git", ["remote", "set-url", "origin", originUrl2], { cwd: repoDir });
|
$("git", ["remote", "set-url", "origin", originUrl2], { cwd: repoDir });
|
||||||
@@ -162651,7 +162666,6 @@ async function main() {
|
|||||||
normalizeEnv();
|
normalizeEnv();
|
||||||
const timer = new Timer();
|
const timer = new Timer();
|
||||||
const tokenRef = __using(_stack2, await resolveInstallationToken(), true);
|
const tokenRef = __using(_stack2, await resolveInstallationToken(), true);
|
||||||
process.env.GITHUB_TOKEN = tokenRef.token;
|
|
||||||
const octokit = createOctokit(tokenRef.token);
|
const octokit = createOctokit(tokenRef.token);
|
||||||
const runInfo = await resolveRun({ octokit });
|
const runInfo = await resolveRun({ octokit });
|
||||||
const toolState = initToolState({ runInfo });
|
const toolState = initToolState({ runInfo });
|
||||||
@@ -162686,7 +162700,7 @@ async function main() {
|
|||||||
});
|
});
|
||||||
await setupGit({
|
await setupGit({
|
||||||
token: tokenRef.token,
|
token: tokenRef.token,
|
||||||
originalToken: tokenRef.originalToken,
|
githubJobToken: tokenRef.githubJobToken,
|
||||||
bashPermission: payload.bash,
|
bashPermission: payload.bash,
|
||||||
owner: runContext.repo.owner,
|
owner: runContext.repo.owner,
|
||||||
name: runContext.repo.name,
|
name: runContext.repo.name,
|
||||||
|
|||||||
@@ -19680,7 +19680,7 @@ var require_core = __commonJS({
|
|||||||
process.env["PATH"] = `${inputPath}${path.delimiter}${process.env["PATH"]}`;
|
process.env["PATH"] = `${inputPath}${path.delimiter}${process.env["PATH"]}`;
|
||||||
}
|
}
|
||||||
exports.addPath = addPath;
|
exports.addPath = addPath;
|
||||||
function getInput2(name, options) {
|
function getInput3(name, options) {
|
||||||
const val = process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
const val = process.env[`INPUT_${name.replace(/ /g, "_").toUpperCase()}`] || "";
|
||||||
if (options && options.required && !val) {
|
if (options && options.required && !val) {
|
||||||
throw new Error(`Input required and not supplied: ${name}`);
|
throw new Error(`Input required and not supplied: ${name}`);
|
||||||
@@ -19690,9 +19690,9 @@ var require_core = __commonJS({
|
|||||||
}
|
}
|
||||||
return val.trim();
|
return val.trim();
|
||||||
}
|
}
|
||||||
exports.getInput = getInput2;
|
exports.getInput = getInput3;
|
||||||
function getMultilineInput(name, options) {
|
function getMultilineInput(name, options) {
|
||||||
const inputs = getInput2(name, options).split("\n").filter((x) => x !== "");
|
const inputs = getInput3(name, options).split("\n").filter((x) => x !== "");
|
||||||
if (options && options.trimWhitespace === false) {
|
if (options && options.trimWhitespace === false) {
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
@@ -19702,7 +19702,7 @@ var require_core = __commonJS({
|
|||||||
function getBooleanInput(name, options) {
|
function getBooleanInput(name, options) {
|
||||||
const trueValue = ["true", "True", "TRUE"];
|
const trueValue = ["true", "True", "TRUE"];
|
||||||
const falseValue = ["false", "False", "FALSE"];
|
const falseValue = ["false", "False", "FALSE"];
|
||||||
const val = getInput2(name, options);
|
const val = getInput3(name, options);
|
||||||
if (trueValue.includes(val))
|
if (trueValue.includes(val))
|
||||||
return true;
|
return true;
|
||||||
if (falseValue.includes(val))
|
if (falseValue.includes(val))
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export async function main(): Promise<MainResult> {
|
|||||||
const timer = new Timer();
|
const timer = new Timer();
|
||||||
|
|
||||||
await using tokenRef = await resolveInstallationToken();
|
await using tokenRef = await resolveInstallationToken();
|
||||||
process.env.GITHUB_TOKEN = tokenRef.token;
|
|
||||||
|
|
||||||
const octokit = createOctokit(tokenRef.token);
|
const octokit = createOctokit(tokenRef.token);
|
||||||
const runInfo = await resolveRun({ octokit });
|
const runInfo = await resolveRun({ octokit });
|
||||||
@@ -81,7 +80,7 @@ export async function main(): Promise<MainResult> {
|
|||||||
|
|
||||||
await setupGit({
|
await setupGit({
|
||||||
token: tokenRef.token,
|
token: tokenRef.token,
|
||||||
originalToken: tokenRef.originalToken,
|
githubJobToken: tokenRef.githubJobToken,
|
||||||
bashPermission: payload.bash,
|
bashPermission: payload.bash,
|
||||||
owner: runContext.repo.owner,
|
owner: runContext.repo.owner,
|
||||||
name: runContext.repo.name,
|
name: runContext.repo.name,
|
||||||
|
|||||||
+6
-2
@@ -44,7 +44,7 @@ export function setupTestRepo(options: SetupOptions): void {
|
|||||||
|
|
||||||
interface SetupGitParams {
|
interface SetupGitParams {
|
||||||
token: string;
|
token: string;
|
||||||
originalToken: string | undefined;
|
githubJobToken: string | undefined;
|
||||||
bashPermission: BashPermission;
|
bashPermission: BashPermission;
|
||||||
owner: string;
|
owner: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -134,7 +134,11 @@ export async function setupGit(params: SetupGitParams): Promise<void> {
|
|||||||
// - restricted/disabled: workflow token (limited by permissions block)
|
// - restricted/disabled: workflow token (limited by permissions block)
|
||||||
// this protects the base repo while allowing fork PR edits via fork remote
|
// this protects the base repo while allowing fork PR edits via fork remote
|
||||||
const originToken =
|
const originToken =
|
||||||
params.bashPermission === "enabled" ? params.token : params.originalToken || params.token;
|
params.bashPermission === "enabled"
|
||||||
|
? params.token
|
||||||
|
: // in GitHub Actions environment this less-capable job token should always be available in the action's input
|
||||||
|
// but in other environments there is no secondary token like this so we just use the installation token itself
|
||||||
|
params.githubJobToken || params.token;
|
||||||
|
|
||||||
// non-PR events: set up origin with token, stay on default branch
|
// non-PR events: set up origin with token, stay on default branch
|
||||||
if (params.event.is_pr !== true || !params.event.issue_number) {
|
if (params.event.is_pr !== true || !params.event.issue_number) {
|
||||||
|
|||||||
+26
-11
@@ -11,18 +11,35 @@ export { revokeGitHubInstallationToken as revokeInstallationToken };
|
|||||||
// store token in memory instead of process.env
|
// store token in memory instead of process.env
|
||||||
let githubInstallationToken: string | undefined;
|
let githubInstallationToken: string | undefined;
|
||||||
|
|
||||||
|
function setEnvironmentVariable(name: string, value: string | undefined) {
|
||||||
|
const hadValue = Object.hasOwn(process.env, name);
|
||||||
|
const originalValue = process.env[name];
|
||||||
|
|
||||||
|
if (typeof value === "string") {
|
||||||
|
process.env[name] = value;
|
||||||
|
} else {
|
||||||
|
delete process.env[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (hadValue) {
|
||||||
|
process.env[name] = originalValue;
|
||||||
|
} else {
|
||||||
|
delete process.env[name];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup GitHub installation token for the action
|
* Setup GitHub installation token for the action
|
||||||
*/
|
*/
|
||||||
export async function resolveInstallationToken() {
|
export async function resolveInstallationToken() {
|
||||||
assert(!githubInstallationToken, "GitHub installation token is already set.");
|
assert(!githubInstallationToken, "GitHub installation token is already set.");
|
||||||
const originalToken = process.env.GITHUB_TOKEN;
|
const githubJobToken = core.getInput("token");
|
||||||
if (originalToken) {
|
|
||||||
process.env.ORIGINAL_GITHUB_TOKEN = originalToken;
|
|
||||||
}
|
|
||||||
const externalToken = process.env.GH_TOKEN;
|
const externalToken = process.env.GH_TOKEN;
|
||||||
const token = externalToken || (await acquireNewToken());
|
const token = externalToken || (await acquireNewToken());
|
||||||
process.env.GITHUB_TOKEN = token;
|
|
||||||
|
const revertGithubToken = setEnvironmentVariable("GITHUB_TOKEN", token);
|
||||||
githubInstallationToken = token;
|
githubInstallationToken = token;
|
||||||
|
|
||||||
if (isGitHubActions) {
|
if (isGitHubActions) {
|
||||||
@@ -33,14 +50,12 @@ export async function resolveInstallationToken() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
originalToken,
|
// in GitHub Actions environment this fallback token should always come from the action's input
|
||||||
|
// but in other environments there is no secondary token like this so we just use the installation token itself
|
||||||
|
githubJobToken,
|
||||||
async [Symbol.asyncDispose]() {
|
async [Symbol.asyncDispose]() {
|
||||||
githubInstallationToken = undefined;
|
githubInstallationToken = undefined;
|
||||||
if (originalToken) {
|
revertGithubToken();
|
||||||
process.env.GITHUB_TOKEN = originalToken;
|
|
||||||
} else {
|
|
||||||
delete process.env.GITHUB_TOKEN;
|
|
||||||
}
|
|
||||||
// GH_TOKEN isn't acquired here, so it's not revoked here either
|
// GH_TOKEN isn't acquired here, so it's not revoked here either
|
||||||
if (externalToken) {
|
if (externalToken) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user