Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion steps/generate-release-summary/generateReleaseSummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const runGit = (args, options = {}) => {
const GIT_REF_RE = /^[A-Za-z0-9@._/-]{1,255}$/;
const isSafeGitRef = (ref) => typeof ref === 'string' && GIT_REF_RE.test(ref);

// A tag glob is a ref with glob syntax: '*', '?' and bracket classes like '[0-9]'. Each glob
// token stands for ref characters, so it is replaced by one before the ref check.
const isSafeTagGlob = (glob) =>
typeof glob === 'string' && isSafeGitRef(glob.replace(/\[[^\]]*\]|[*?]/g, 'x'));

// owner/repo used to linkify PR numbers. Validate strictly; fall back to a non-linking
// placeholder if the env value is malformed so we never build a broken/injected URL.
const resolveRepo = () => {
Expand Down Expand Up @@ -326,7 +331,7 @@ const generateSummary = async ({ core }) => {
const repo = resolveRepo();
let baseRef = process.env.BASE_REF;

if (!isSafeGitRef(tagGlob.replace(/\*/g, 'x'))) {
if (!isSafeTagGlob(tagGlob)) {
throw new Error(`Refusing unsafe tag glob: ${tagGlob}`);
}

Expand Down Expand Up @@ -422,6 +427,7 @@ module.exports = {
commitMatchesPathFilter,
detectLatestTag,
generateSummary,
isSafeTagGlob,
linearAuthorization,
readPathFilter,
resolveCommitTitle,
Expand Down
11 changes: 11 additions & 0 deletions steps/generate-release-summary/generateReleaseSummary.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
collectScopedCommits,
detectLatestTag,
generateSummary,
isSafeTagGlob,
linearAuthorization,
readPathFilter,
resolveCommitTitle,
Expand Down Expand Up @@ -176,6 +177,16 @@ test('monorepo: keeps release-window commits and excludes everything the package
}
});

test('accepts the glob syntax the release workflows build, refuses shell metacharacters', () => {
assert.equal(isSafeTagGlob('v*'), true);
assert.equal(isSafeTagGlob('v[0-9]*'), true);
assert.equal(isSafeTagGlob('@aragon/app@[0-9]*'), true);
assert.equal(isSafeTagGlob('v?.?.?'), true);
assert.equal(isSafeTagGlob('v*;rm -rf .'), false);
assert.equal(isSafeTagGlob('v$(id)*'), false);
assert.equal(isSafeTagGlob(''), false);
});

test('single-package: v* boundary, no path filter, drops both release-commit styles', () => {
const repository = createRepository();

Expand Down
Loading