Skip to content

Commit f0ff527

Browse files
committed
fix: Resolve CVE-2025-25288 by migrating to ESM and upgrading dependencies
- Upgrade @actions/github to 6.0.1 and @octokit/rest to 22.0.1 to fix ReDoS vulnerability - Migrate source code to ESM (import/export syntax) - Replace Jest with Vitest and ncc with esbuild - Change bundle output to .cjs extension for proper CommonJS handling - All tests passing, no breaking changes to action interface
1 parent 06dc999 commit f0ff527

File tree

14 files changed

+35575
-27341
lines changed

14 files changed

+35575
-27341
lines changed

action.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@
33
* SPDX-License-Identifier: MPL-2.0
44
*/
55

6-
'use strict'
7-
8-
const fs = require('fs').promises
9-
const os = require('os')
10-
const crypto = require('crypto')
11-
12-
const core = require('@actions/core')
13-
const tc = require('@actions/tool-cache')
14-
15-
const octokit = require('./octokit')
6+
import { promises as fs } from 'fs'
7+
import os from 'os'
8+
import crypto from 'crypto'
9+
import core from '@actions/core'
10+
import tc from '@actions/tool-cache'
11+
import octokit from './octokit.js'
1612

1713
const owner = 'hashicorp'
1814
const repo = 'copywrite'
@@ -108,4 +104,4 @@ async function run () {
108104
}
109105
}
110106

111-
module.exports = run
107+
export default run

action.test.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
* SPDX-License-Identifier: MPL-2.0
44
*/
55

6-
const fs = require('fs')
7-
const nock = require('nock')
8-
const path = require('path')
9-
const os = require('os')
10-
11-
const core = require('@actions/core')
6+
import fs from 'fs'
7+
import nock from 'nock'
8+
import path from 'path'
9+
import os from 'os'
10+
import { vi, describe, test, beforeAll, beforeEach, expect } from 'vitest'
11+
import core from '@actions/core'
12+
import { fileURLToPath } from 'url'
13+
import { dirname } from 'path'
14+
import action from './action.js'
15+
16+
const __filename = fileURLToPath(import.meta.url)
17+
const __dirname = dirname(__filename)
1218

1319
const mockRelease = {
1420
assets: [
@@ -45,9 +51,9 @@ beforeEach(() => {
4551
process.env.INPUT_VERSION = 'latest'
4652
process.env['INPUT_VERSION-CHECKSUM'] = '5663389ef1a8ec48af6ca622e66bf0f54ba8f22c127f14cb8a3f429e40868582'
4753

48-
const spyOsArch = jest.spyOn(os, 'arch')
54+
const spyOsArch = vi.spyOn(os, 'arch')
4955
spyOsArch.mockReturnValue('x64')
50-
const spyOsPlatform = jest.spyOn(os, 'platform')
56+
const spyOsPlatform = vi.spyOn(os, 'platform')
5157
spyOsPlatform.mockReturnValue('win32')
5258
})
5359

@@ -61,18 +67,17 @@ describe('action', () => {
6167
// const scopeWeb = nock('https://github.com')
6268
// .get('/hashicorp/copywrite/releases/download/v0.1.3/copywrite_0.1.3_windows_x86_64.zip')
6369
// .replyWithFile(200, path.resolve(__dirname, 'test.zip'), { 'content-type': 'application/octet-stream' })
64-
const spyCoreAddPath = jest.spyOn(core, 'addPath')
65-
const spyCoreSetOutput = jest.spyOn(core, 'setOutput')
70+
const spyCoreAddPath = vi.spyOn(core, 'addPath')
71+
const spyCoreSetOutput = vi.spyOn(core, 'setOutput')
6672

6773
fs.mkdtemp(path.join(os.tmpdir(), 'setup-copywrite-'), async (err, directory) => {
6874
if (err) throw err
6975

7076
process.env.RUNNER_TEMP = directory
7177

72-
const spyOsHomedir = jest.spyOn(os, 'homedir')
78+
const spyOsHomedir = vi.spyOn(os, 'homedir')
7379
spyOsHomedir.mockReturnValue(directory)
7480

75-
const action = require('./action')
7681
await expect(await action()).resolves
7782
expect(scopeAPI.isDone()).toBeTruthy()
7883
expect(spyCoreAddPath).toHaveBeenCalled()
@@ -90,19 +95,18 @@ describe('action', () => {
9095
// const scopeWeb = nock('https://github.com')
9196
// .get('/hashicorp/copywrite/releases/download/v0.1.3/copywrite_0.1.3_windows_x86_64.zip')
9297
// .replyWithFile(200, path.resolve(__dirname, 'test.zip'), { 'content-type': 'application/octet-stream' })
93-
const spyCoreAddPath = jest.spyOn(core, 'addPath')
94-
const spyCoreSetOutput = jest.spyOn(core, 'setOutput')
98+
const spyCoreAddPath = vi.spyOn(core, 'addPath')
99+
const spyCoreSetOutput = vi.spyOn(core, 'setOutput')
95100

96101
fs.mkdtemp(path.join(os.tmpdir(), 'setup-copywrite-'), async (err, directory) => {
97102
if (err) throw err
98103

99104
process.env.INPUT_VERSION = 'v0.1.3'
100105
process.env.RUNNER_TEMP = directory
101106

102-
const spyOsHomedir = jest.spyOn(os, 'homedir')
107+
const spyOsHomedir = vi.spyOn(os, 'homedir')
103108
spyOsHomedir.mockReturnValue(directory)
104109

105-
const action = require('./action')
106110
await expect(await action()).resolves
107111
expect(scopeAPI.isDone()).toBeTruthy()
108112
expect(spyCoreAddPath).toHaveBeenCalled()
@@ -126,10 +130,9 @@ describe('action', () => {
126130
process.env.INPUT_VERSION = 'v0.1.3'
127131
process.env.RUNNER_TEMP = directory
128132

129-
const spyOsHomedir = jest.spyOn(os, 'homedir')
133+
const spyOsHomedir = vi.spyOn(os, 'homedir')
130134
spyOsHomedir.mockReturnValue(directory)
131135

132-
const action = require('./action')
133136
await expect(await action()).resolves
134137
expect(scope.isDone()).toBeTruthy()
135138
done()
@@ -154,10 +157,9 @@ describe('action', () => {
154157
process.env.INPUT_VERSION = 'v0.1.3'
155158
process.env.RUNNER_TEMP = directory
156159

157-
const spyOsHomedir = jest.spyOn(os, 'homedir')
160+
const spyOsHomedir = vi.spyOn(os, 'homedir')
158161
spyOsHomedir.mockReturnValue(directory)
159162

160-
const action = require('./action')
161163
await expect(await action()).resolves
162164
expect(scope.isDone()).toBeTruthy()
163165
done()
@@ -179,10 +181,9 @@ describe('action', () => {
179181
process.env.INPUT_VERSION = 'v0.1.3'
180182
process.env.RUNNER_TEMP = directory
181183

182-
const spyOsHomedir = jest.spyOn(os, 'homedir')
184+
const spyOsHomedir = vi.spyOn(os, 'homedir')
183185
spyOsHomedir.mockReturnValue(directory)
184186

185-
const action = require('./action')
186187
await expect(await action()).resolves
187188
expect(scope.isDone()).toBeTruthy()
188189
done()

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ outputs:
2121
description: 'The version of the copywrite CLI that was installed.'
2222
runs:
2323
using: 'node20'
24-
main: 'dist/index.js'
24+
main: 'dist/index.cjs'
2525
branding:
2626
icon: "file-text"
2727
color: "gray-dark"

build.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { build } from 'esbuild'
2+
3+
// Build as CommonJS for GitHub Actions
4+
// Using .cjs extension so Node treats it as CommonJS regardless of package.json "type"
5+
await build({
6+
entryPoints: ['index.js'],
7+
bundle: true,
8+
platform: 'node',
9+
target: 'node20',
10+
format: 'cjs',
11+
outfile: 'dist/index.cjs',
12+
sourcemap: true,
13+
banner: {
14+
js: '// Copyright IBM Corp. 2023, 2025\n// SPDX-License-Identifier: MPL-2.0\n'
15+
}
16+
})
17+
18+
console.log('✓ Bundle created successfully: dist/index.cjs')

0 commit comments

Comments
 (0)