chore(deps): update golangci/golangci-lint-action action to v9.3.0 - #367
Merged
Merged
Conversation
|
[puLL-Merge] - golangci/golangci-lint-action@v9.2.1..v9.3.0 Diffdiff --git .github/workflows/codeql.yaml .github/workflows/codeql.yaml
index d3c8d2de5d..594321e239 100644
--- .github/workflows/codeql.yaml
+++ .github/workflows/codeql.yaml
@@ -35,7 +35,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
+ uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
# Override language selection by uncommenting this and choosing your languages
with:
languages: 'javascript-typescript'
@@ -45,4 +45,4 @@ jobs:
npm run all
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
+ uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
diff --git README.md README.md
index 0b18ece950..c487872fe6 100644
--- README.md
+++ README.md
@@ -616,6 +616,23 @@ with:
</details>
+#### `no-run-logs-group`
+
+(optional)
+
+This option disables the grouping of logs from golangci-lint run.
+
+<details>
+<summary>Example</summary>
+
+```yaml
+uses: golangci/golangci-lint-action@v9
+with:
+ experimental: "no-run-logs-group"
+```
+
+</details>
+
## Annotations
Currently, GitHub parses the action's output and creates [annotations](https://github.blog/2018-12-14-introducing-check-runs-and-annotations/).
diff --git action.yml action.yml
index 48e8c9bc30..283e626dba 100644
--- action.yml
+++ action.yml
@@ -76,6 +76,7 @@ inputs:
description: |
Experimental options for the action.
List of comma separated options.
+ Available options: `automatic-module-directories`, `no-run-logs-group`
default: ""
required: false
runs:
diff --git dist/post_run/index.js dist/post_run/index.js
index 28547fb706..997d59a46e 100644
--- dist/post_run/index.js
+++ dist/post_run/index.js
@@ -29642,6 +29642,29 @@ function _generateTmpName(opts) {
return path.join(tmpDir, opts.dir, name);
}
+/**
+ * Check the prefix, postfix, and template options.
+ *
+ * Rejects non-string inputs so that a non-string `.includes('..')` cannot evade
+ * the substring check (e.g. an Array whose `.includes('..')` is element-wise,
+ * or a duck-typed object with a custom `.includes`), and so that the value is
+ * not later coerced to a string with traversal sequences via `Array.prototype.join`
+ * or `path.join`.
+ *
+ * @private
+ */
+function _assertPath(option, value) {
+ if (typeof value !== 'string') {
+ throw new Error(`${option} option must be a string, got "${typeof value}".`);
+ }
+
+ if (value.includes("..")) {
+ throw new Error("Relative value not allowed");
+ }
+
+ return value;
+}
+
/**
* Asserts and sanitizes the basic options.
*
@@ -29656,13 +29679,19 @@ function _assertOptionsBase(options) {
// must not fail on valid .<name> or ..<name> or similar such constructs
const basename = path.basename(name);
- if (basename === '..' || basename === '.' || basename !== name)
+ if (basename === '..' || basename === '.' || basename !== name) {
throw new Error(`name option must not contain a path, found "${name}".`);
+ }
}
/* istanbul ignore else */
- if (!_isUndefined(options.template) && !options.template.match(TEMPLATE_PATTERN)) {
- throw new Error(`Invalid template, found "${options.template}".`);
+ if (!_isUndefined(options.template)) {
+ if (typeof options.template !== 'string') {
+ throw new Error(`template option must be a string, got "${typeof options.template}".`);
+ }
+ if (!options.template.match(TEMPLATE_PATTERN)) {
+ throw new Error(`Invalid template, found "${options.template}".`);
+ }
}
/* istanbul ignore else */
@@ -29678,8 +29707,9 @@ function _assertOptionsBase(options) {
options.unsafeCleanup = !!options.unsafeCleanup;
// for completeness' sake only, also keep (multiple) blanks if the user, purportedly sane, requests us to
- options.prefix = _isUndefined(options.prefix) ? '' : options.prefix;
- options.postfix = _isUndefined(options.postfix) ? '' : options.postfix;
+ options.prefix = _isUndefined(options.prefix) ? '' : _assertPath('prefix', options.prefix);
+ options.postfix = _isUndefined(options.postfix) ? '' : _assertPath('postfix', options.postfix);
+ options.template = _isUndefined(options.template) ? undefined : _assertPath('template', options.template);
}
/**
@@ -29695,7 +29725,7 @@ function _getRelativePath(option, name, tmpDir, cb) {
const relativePath = path.relative(tmpDir, resolvedPath);
- if (!resolvedPath.startsWith(tmpDir)) {
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
return cb(new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`));
}
@@ -29714,7 +29744,7 @@ function _getRelativePathSync(option, name, tmpDir) {
const resolvedPath = _resolvePathSync(name, tmpDir);
const relativePath = path.relative(tmpDir, resolvedPath);
- if (!resolvedPath.startsWith(tmpDir)) {
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
throw new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`);
}
@@ -34810,7 +34840,6 @@ function defaultFactory (origin, opts) {
class Agent extends DispatcherBase {
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
-
if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
@@ -35198,6 +35227,9 @@ const EMPTY_BUF = Buffer.alloc(0)
const FastBuffer = Buffer[Symbol.species]
const addListener = util.addListener
const removeAllListeners = util.removeAllListeners
+const kIdleSocketValidation = Symbol('kIdleSocketValidation')
+const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
+const kSocketUsed = Symbol('kSocketUsed')
let extractBody
@@ -35420,29 +35452,71 @@ class Parser {
const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
- if (ret === constants.ERROR.PAUSED_UPGRADE) {
- this.onUpgrade(data.slice(offset))
- } else if (ret === constants.ERROR.PAUSED) {
- this.paused = true
- socket.unshift(data.slice(offset))
- } else if (ret !== constants.ERROR.OK) {
- const ptr = llhttp.llhttp_get_error_reason(this.ptr)
- let message = ''
- /* istanbul ignore else: difficult to make a test case for */
- if (ptr) {
- const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
- message =
- 'Response does not match the HTTP/1.1 protocol (' +
- Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
- ')'
- }
- throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))
+ if (ret !== constants.ERROR.OK) {
+ const body = data.subarray(offset)
+
+ if (ret === constants.ERROR.PAUSED_UPGRADE) {
+ this.onUpgrade(body)
+ } else if (ret === constants.ERROR.PAUSED) {
+ this.paused = true
+ socket.unshift(body)
+ } else {
+ throw this.createError(ret, body)
+ }
}
} catch (err) {
util.destroy(socket, err)
}
}
+ finish () {
+ assert(currentParser === null)
+ assert(this.ptr != null)
+ assert(!this.paused)
+
+ const { llhttp } = this
+
+ let ret
+
+ try {
+ currentParser = this
+ ret = llhttp.llhttp_finish(this.ptr)
+ } finally {
+ currentParser = null
+ }
+
+ if (ret === constants.ERROR.OK) {
+ return null
+ }
+
+ if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) {
+ this.paused = true
+ return null
+ }
+
+ return this.createError(ret, EMPTY_BUF)
+ }
+
+ createError (ret, data) {
+ const { llhttp, contentLength, bytesRead } = this
+
+ if (contentLength && bytesRead !== parseInt(contentLength, 10)) {
+ return new ResponseContentLengthMismatchError()
+ }
+
+ const ptr = llhttp.llhttp_get_error_reason(this.ptr)
+ let message = ''
+ if (ptr) {
+ const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
+ message =
+ 'Response does not match the HTTP/1.1 protocol (' +
+ Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
+ ')'
+ }
+
+ return new HTTPParserError(message, constants.ERROR[ret], data)
+ }
+
destroy () {
assert(this.ptr != null)
assert(currentParser == null)
@@ -35470,6 +35544,11 @@ class Parser {
return -1
}
+ if (client[kRunning] === 0) {
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
+ return -1
+ }
+
const request = client[kQueue][client[kRunningIdx]]
if (!request) {
return -1
@@ -35573,6 +35652,11 @@ class Parser {
return -1
}
+ if (client[kRunning] === 0) {
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
+ return -1
+ }
+
const request = client[kQueue][client[kRunningIdx]]
/* istanbul ignore next: difficult to make a test case for */
@@ -35746,6 +35830,7 @@ class Parser {
request.onComplete(headers)
client[kQueue][client[kRunningIdx]++] = null
+ socket[kSocketUsed] = true
if (socket[kWriting]) {
assert(client[kRunning] === 0)
@@ -35804,6 +35889,9 @@ async function connectH1 (client, socket) {
socket[kWriting] = false
socket[kReset] = false
socket[kBlocking] = false
+ socket[kIdleSocketValidation] = 0
+ socket[kIdleSocketValidationTimeout] = null
+ socket[kSocketUsed] = false
socket[kParser] = new Parser(client, socket, llhttpInstance)
addListener(socket, 'error', function (err) {
@@ -35814,8 +35902,11 @@ async function connectH1 (client, socket) {
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
// to the user.
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so for as a valid response.
- parser.onMessageComplete()
+ const parserErr = parser.finish()
+ if (parserErr) {
+ this[kError] = parserErr
+ this[kClient][kOnError](parserErr)
+ }
return
}
@@ -35834,8 +35925,10 @@ async function connectH1 (client, socket) {
const parser = this[kParser]
if (parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so far as a valid response.
- parser.onMessageComplete()
+ const parserErr = parser.finish()
+ if (parserErr) {
+ util.destroy(this, parserErr)
+ }
return
}
@@ -35845,10 +35938,11 @@ async function connectH1 (client, socket) {
const client = this[kClient]
const parser = this[kParser]
+ clearIdleSocketValidation(this)
+
if (parser) {
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so far as a valid response.
- parser.onMessageComplete()
+ this[kError] = parser.finish() || this[kError]
}
this[kParser].destroy()
@@ -35911,7 +36005,7 @@ async function connectH1 (client, socket) {
return socket.destroyed
},
busy (request) {
- if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
+ if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
return true
}
@@ -35949,6 +36043,31 @@ async function connectH1 (client, socket) {
}
}
+function clearIdleSocketValidation (socket) {
+ if (socket[kIdleSocketValidationTimeout]) {
+ clearTimeout(socket[kIdleSocketValidationTimeout])
+ socket[kIdleSocketValidationTimeout] = null
+ }
+
+ socket[kIdleSocketValidation] = 0
+}
+
+function scheduleIdleSocketValidation (client, socket) {
+ socket[kIdleSocketValidation] = 1
+ socket[kIdleSocketValidationTimeout] = setTimeout(() => {
+ socket[kIdleSocketValidationTimeout] = null
+ socket[kIdleSocketValidation] = 2
+
+ if (client[kSocket] === socket && !socket.destroyed) {
+ client[kResume]()
+ }
+ }, 0)
+ socket[kIdleSocketValidationTimeout].unref?.()
+}
+
+/**
+ * @param {import('./client.js')} client
+ */
function resumeH1 (client) {
const socket = client[kSocket]
@@ -35963,6 +36082,32 @@ function resumeH1 (client) {
socket[kNoRef] = false
}
+ if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
+ if (socket[kIdleSocketValidation] === 0) {
+ scheduleIdleSocketValidation(client, socket)
+ socket[kParser].readMore()
+ if (socket.destroyed) {
+ return
+ }
+ return
+ }
+
+ if (socket[kIdleSocketValidation] === 1) {
+ socket[kParser].readMore()
+ if (socket.destroyed) {
+ return
+ }
+ return
+ }
+ }
+
+ if (client[kRunning] === 0) {
+ socket[kParser].readMore()
+ if (socket.destroyed) {
+ return
+ }
+ }
+
if (client[kSize] === 0) {
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
@@ -36056,6 +36201,7 @@ function writeH1 (client, request) {
}
const socket = client[kSocket]
+ clearIdleSocketValidation(socket)
const abort = (err) => {
if (request.aborted || request.completed) {
@@ -37928,6 +38074,7 @@ class DispatcherBase extends Dispatcher {
get webSocketOptions () {
return {
+ maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024
}
}
@@ -43864,32 +44011,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {})
// If the attribute-name case-insensitively matches the string
// "SameSite", the user agent MUST process the cookie-av as follows:
- // 1. Let enforcement be "Default".
- let enforcement = 'Default'
-
const attributeValueLowercase = attributeValue.toLowerCase()
- // 2. If cookie-av's attribute-value is a case-insensitive match for
- // "None", set enforcement to "None".
- if (attributeValueLowercase.includes('none')) {
- enforcement = 'None'
- }
- // 3. If cookie-av's attribute-value is a case-insensitive match for
- // "Strict", set enforcement to "Strict".
- if (attributeValueLowercase.includes('strict')) {
- enforcement = 'Strict'
+ // 1. If cookie-av's attribute-value is a case-insensitive match for
+ // "None", append an attribute to the cookie-attribute-list with an
+ // attribute-name of "SameSite" and an attribute-value of "None".
+ if (attributeValueLowercase === 'none') {
+ cookieAttributeList.sameSite = 'None'
+ } else if (attributeValueLowercase === 'strict') {
+ // 2. If cookie-av's attribute-value is a case-insensitive match for
+ // "Strict", append an attribute to the cookie-attribute-list with
+ // an attribute-name of "SameSite" and an attribute-value of
+ // "Strict".
+ cookieAttributeList.sameSite = 'Strict'
+ } else if (attributeValueLowercase === 'lax') {
+ // 3. If cookie-av's attribute-value is a case-insensitive match for
+ // "Lax", append an attribute to the cookie-attribute-list with an
+ // attribute-name of "SameSite" and an attribute-value of "Lax".
+ cookieAttributeList.sameSite = 'Lax'
}
-
- // 4. If cookie-av's attribute-value is a case-insensitive match for
- // "Lax", set enforcement to "Lax".
- if (attributeValueLowercase.includes('lax')) {
- enforcement = 'Lax'
- }
-
- // 5. Append an attribute to the cookie-attribute-list with an
- // attribute-name of "SameSite" and an attribute-value of
- // enforcement.
- cookieAttributeList.sameSite = enforcement
} else {
cookieAttributeList.unparsed ??= []
@@ -56715,6 +56855,11 @@ const { closeWebSocketConnection } = __nccwpck_require__(86897)
const { PerMessageDeflate } = __nccwpck_require__(19469)
const { MessageSizeExceededError } = __nccwpck_require__(68707)
+function failWebsocketConnectionWithCode (ws, code, reason) {
+ closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason))
+ failWebsocketConnection(ws, reason)
+}
+
// This code was influenced by ws released under the MIT license.
// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
// Copyright (c) 2013 Arnout Kazemier and contributors
@@ -56734,19 +56879,23 @@ class ByteParser extends Writable {
/** @type {Map<string, PerMessageDeflate>} */
#extensions
+ /** @type {number} */
+ #maxFragments
+
/** @type {number} */
#maxPayloadSize
/**
* @param {import('./websocket').WebSocket} ws
* @param {Map<string, string>|null} extensions
- * @param {{ maxPayloadSize?: number }} [options]
+ * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options]
*/
constructor (ws, extensions, options = {}) {
super()
this.ws = ws
this.#extensions = extensions == null ? new Map() : extensions
+ this.#maxFragments = options.maxFragments ?? 0
this.#maxPayloadSize = options.maxPayloadSize ?? 0
if (this.#extensions.has('permessage-deflate')) {
@@ -56770,9 +56919,9 @@ class ByteParser extends Writable {
if (
this.#maxPayloadSize > 0 &&
!isControlFrame(this.#info.opcode) &&
- this.#info.payloadLength > this.#maxPayloadSize
+ this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize
) {
- failWebsocketConnection(this.ws, 'Payload size exceeds maximum allowed size')
+ failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size')
return false
}
@@ -56937,10 +57086,12 @@ class ByteParser extends Writable {
this.#state = parserStates.INFO
} else {
if (!this.#info.compressed) {
- this.writeFragments(body)
+ if (!this.writeFragments(body)) {
+ return
+ }
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
- failWebsocketConnection(this.ws, new MessageSizeExceededError().message)
+ failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
return
}
@@ -56959,14 +57110,17 @@ class ByteParser extends Writable {
this.#info.fin,
(error, data) => {
if (error) {
- failWebsocketConnection(this.ws, error.message)
+ const code = error instanceof MessageSizeExceededError ? 1009 : 1007
+ failWebsocketConnectionWithCode(this.ws, code, error.message)
return
}
- this.writeFragments(data)
+ if (!this.writeFragments(data)) {
+ return
+ }
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
- failWebsocketConnection(this.ws, new MessageSizeExceededError().message)
+ failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
return
}
@@ -57036,8 +57190,17 @@ class ByteParser extends Writable {
}
writeFragments (fragment) {
+ if (
+ this.#maxFragments > 0 &&
+ this.#fragments.length === this.#maxFragments
+ ) {
+ failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments')
+ return false
+ }
+
this.#fragmentsBytes += fragment.length
this.#fragments.push(fragment)
+ return true
}
consumeFragments () {
@@ -58090,9 +58253,12 @@ class WebSocket extends EventTarget {
// once this happens, the connection is open
this[kResponse] = response
- const maxPayloadSize = this[kController]?.dispatcher?.webSocketOptions?.maxPayloadSize
+ const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions
+ const maxFragments = webSocketOptions?.maxFragments
+ const maxPayloadSize = webSocketOptions?.maxPayloadSize
const parser = new ByteParser(this, parsedExtensions, {
+ maxFragments,
maxPayloadSize
})
parser.on('drain', onParserDrain)
@@ -59301,15 +59467,23 @@ function modulesAutoDetection(rootDir) {
async function runLint(binPath) {
const workingDirectory = getWorkingDirectory();
const experimental = core.getInput(`experimental`).split(`,`);
+ const noGroup = experimental.includes(`no-run-logs-group`);
if (experimental.includes(`automatic-module-directories`)) {
const wds = modulesAutoDetection(workingDirectory);
const cwd = process.cwd();
for (const wd of wds) {
- await core.group(`run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd));
+ await optionalGroup(noGroup, `run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd));
}
return;
}
- await core.group(`run golangci-lint`, () => runGolangciLint(binPath, workingDirectory));
+ await optionalGroup(noGroup, `run golangci-lint`, () => runGolangciLint(binPath, workingDirectory));
+}
+async function optionalGroup(noGroup, name, fn) {
+ if (noGroup) {
+ core.info(name);
+ return fn();
+ }
+ return core.group(name, fn);
}
async function run() {
try {
diff --git dist/run/index.js dist/run/index.js
index cfaeba7952..318d9a3dd4 100644
--- dist/run/index.js
+++ dist/run/index.js
@@ -29642,6 +29642,29 @@ function _generateTmpName(opts) {
return path.join(tmpDir, opts.dir, name);
}
+/**
+ * Check the prefix, postfix, and template options.
+ *
+ * Rejects non-string inputs so that a non-string `.includes('..')` cannot evade
+ * the substring check (e.g. an Array whose `.includes('..')` is element-wise,
+ * or a duck-typed object with a custom `.includes`), and so that the value is
+ * not later coerced to a string with traversal sequences via `Array.prototype.join`
+ * or `path.join`.
+ *
+ * @private
+ */
+function _assertPath(option, value) {
+ if (typeof value !== 'string') {
+ throw new Error(`${option} option must be a string, got "${typeof value}".`);
+ }
+
+ if (value.includes("..")) {
+ throw new Error("Relative value not allowed");
+ }
+
+ return value;
+}
+
/**
* Asserts and sanitizes the basic options.
*
@@ -29656,13 +29679,19 @@ function _assertOptionsBase(options) {
// must not fail on valid .<name> or ..<name> or similar such constructs
const basename = path.basename(name);
- if (basename === '..' || basename === '.' || basename !== name)
+ if (basename === '..' || basename === '.' || basename !== name) {
throw new Error(`name option must not contain a path, found "${name}".`);
+ }
}
/* istanbul ignore else */
- if (!_isUndefined(options.template) && !options.template.match(TEMPLATE_PATTERN)) {
- throw new Error(`Invalid template, found "${options.template}".`);
+ if (!_isUndefined(options.template)) {
+ if (typeof options.template !== 'string') {
+ throw new Error(`template option must be a string, got "${typeof options.template}".`);
+ }
+ if (!options.template.match(TEMPLATE_PATTERN)) {
+ throw new Error(`Invalid template, found "${options.template}".`);
+ }
}
/* istanbul ignore else */
@@ -29678,8 +29707,9 @@ function _assertOptionsBase(options) {
options.unsafeCleanup = !!options.unsafeCleanup;
// for completeness' sake only, also keep (multiple) blanks if the user, purportedly sane, requests us to
- options.prefix = _isUndefined(options.prefix) ? '' : options.prefix;
- options.postfix = _isUndefined(options.postfix) ? '' : options.postfix;
+ options.prefix = _isUndefined(options.prefix) ? '' : _assertPath('prefix', options.prefix);
+ options.postfix = _isUndefined(options.postfix) ? '' : _assertPath('postfix', options.postfix);
+ options.template = _isUndefined(options.template) ? undefined : _assertPath('template', options.template);
}
/**
@@ -29695,7 +29725,7 @@ function _getRelativePath(option, name, tmpDir, cb) {
const relativePath = path.relative(tmpDir, resolvedPath);
- if (!resolvedPath.startsWith(tmpDir)) {
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
return cb(new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`));
}
@@ -29714,7 +29744,7 @@ function _getRelativePathSync(option, name, tmpDir) {
const resolvedPath = _resolvePathSync(name, tmpDir);
const relativePath = path.relative(tmpDir, resolvedPath);
- if (!resolvedPath.startsWith(tmpDir)) {
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
throw new Error(`${option} option must be relative to "${tmpDir}", found "${relativePath}".`);
}
@@ -34810,7 +34840,6 @@ function defaultFactory (origin, opts) {
class Agent extends DispatcherBase {
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
-
if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
@@ -35198,6 +35227,9 @@ const EMPTY_BUF = Buffer.alloc(0)
const FastBuffer = Buffer[Symbol.species]
const addListener = util.addListener
const removeAllListeners = util.removeAllListeners
+const kIdleSocketValidation = Symbol('kIdleSocketValidation')
+const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout')
+const kSocketUsed = Symbol('kSocketUsed')
let extractBody
@@ -35420,29 +35452,71 @@ class Parser {
const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr
- if (ret === constants.ERROR.PAUSED_UPGRADE) {
- this.onUpgrade(data.slice(offset))
- } else if (ret === constants.ERROR.PAUSED) {
- this.paused = true
- socket.unshift(data.slice(offset))
- } else if (ret !== constants.ERROR.OK) {
- const ptr = llhttp.llhttp_get_error_reason(this.ptr)
- let message = ''
- /* istanbul ignore else: difficult to make a test case for */
- if (ptr) {
- const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
- message =
- 'Response does not match the HTTP/1.1 protocol (' +
- Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
- ')'
- }
- throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset))
+ if (ret !== constants.ERROR.OK) {
+ const body = data.subarray(offset)
+
+ if (ret === constants.ERROR.PAUSED_UPGRADE) {
+ this.onUpgrade(body)
+ } else if (ret === constants.ERROR.PAUSED) {
+ this.paused = true
+ socket.unshift(body)
+ } else {
+ throw this.createError(ret, body)
+ }
}
} catch (err) {
util.destroy(socket, err)
}
}
+ finish () {
+ assert(currentParser === null)
+ assert(this.ptr != null)
+ assert(!this.paused)
+
+ const { llhttp } = this
+
+ let ret
+
+ try {
+ currentParser = this
+ ret = llhttp.llhttp_finish(this.ptr)
+ } finally {
+ currentParser = null
+ }
+
+ if (ret === constants.ERROR.OK) {
+ return null
+ }
+
+ if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) {
+ this.paused = true
+ return null
+ }
+
+ return this.createError(ret, EMPTY_BUF)
+ }
+
+ createError (ret, data) {
+ const { llhttp, contentLength, bytesRead } = this
+
+ if (contentLength && bytesRead !== parseInt(contentLength, 10)) {
+ return new ResponseContentLengthMismatchError()
+ }
+
+ const ptr = llhttp.llhttp_get_error_reason(this.ptr)
+ let message = ''
+ if (ptr) {
+ const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0)
+ message =
+ 'Response does not match the HTTP/1.1 protocol (' +
+ Buffer.from(llhttp.memory.buffer, ptr, len).toString() +
+ ')'
+ }
+
+ return new HTTPParserError(message, constants.ERROR[ret], data)
+ }
+
destroy () {
assert(this.ptr != null)
assert(currentParser == null)
@@ -35470,6 +35544,11 @@ class Parser {
return -1
}
+ if (client[kRunning] === 0) {
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
+ return -1
+ }
+
const request = client[kQueue][client[kRunningIdx]]
if (!request) {
return -1
@@ -35573,6 +35652,11 @@ class Parser {
return -1
}
+ if (client[kRunning] === 0) {
+ util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket)))
+ return -1
+ }
+
const request = client[kQueue][client[kRunningIdx]]
/* istanbul ignore next: difficult to make a test case for */
@@ -35746,6 +35830,7 @@ class Parser {
request.onComplete(headers)
client[kQueue][client[kRunningIdx]++] = null
+ socket[kSocketUsed] = true
if (socket[kWriting]) {
assert(client[kRunning] === 0)
@@ -35804,6 +35889,9 @@ async function connectH1 (client, socket) {
socket[kWriting] = false
socket[kReset] = false
socket[kBlocking] = false
+ socket[kIdleSocketValidation] = 0
+ socket[kIdleSocketValidationTimeout] = null
+ socket[kSocketUsed] = false
socket[kParser] = new Parser(client, socket, llhttpInstance)
addListener(socket, 'error', function (err) {
@@ -35814,8 +35902,11 @@ async function connectH1 (client, socket) {
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
// to the user.
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so for as a valid response.
- parser.onMessageComplete()
+ const parserErr = parser.finish()
+ if (parserErr) {
+ this[kError] = parserErr
+ this[kClient][kOnError](parserErr)
+ }
return
}
@@ -35834,8 +35925,10 @@ async function connectH1 (client, socket) {
const parser = this[kParser]
if (parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so far as a valid response.
- parser.onMessageComplete()
+ const parserErr = parser.finish()
+ if (parserErr) {
+ util.destroy(this, parserErr)
+ }
return
}
@@ -35845,10 +35938,11 @@ async function connectH1 (client, socket) {
const client = this[kClient]
const parser = this[kParser]
+ clearIdleSocketValidation(this)
+
if (parser) {
if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) {
- // We treat all incoming data so far as a valid response.
- parser.onMessageComplete()
+ this[kError] = parser.finish() || this[kError]
}
this[kParser].destroy()
@@ -35911,7 +36005,7 @@ async function connectH1 (client, socket) {
return socket.destroyed
},
busy (request) {
- if (socket[kWriting] || socket[kReset] || socket[kBlocking]) {
+ if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) {
return true
}
@@ -35949,6 +36043,31 @@ async function connectH1 (client, socket) {
}
}
+function clearIdleSocketValidation (socket) {
+ if (socket[kIdleSocketValidationTimeout]) {
+ clearTimeout(socket[kIdleSocketValidationTimeout])
+ socket[kIdleSocketValidationTimeout] = null
+ }
+
+ socket[kIdleSocketValidation] = 0
+}
+
+function scheduleIdleSocketValidation (client, socket) {
+ socket[kIdleSocketValidation] = 1
+ socket[kIdleSocketValidationTimeout] = setTimeout(() => {
+ socket[kIdleSocketValidationTimeout] = null
+ socket[kIdleSocketValidation] = 2
+
+ if (client[kSocket] === socket && !socket.destroyed) {
+ client[kResume]()
+ }
+ }, 0)
+ socket[kIdleSocketValidationTimeout].unref?.()
+}
+
+/**
+ * @param {import('./client.js')} client
+ */
function resumeH1 (client) {
const socket = client[kSocket]
@@ -35963,6 +36082,32 @@ function resumeH1 (client) {
socket[kNoRef] = false
}
+ if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) {
+ if (socket[kIdleSocketValidation] === 0) {
+ scheduleIdleSocketValidation(client, socket)
+ socket[kParser].readMore()
+ if (socket.destroyed) {
+ return
+ }
+ return
+ }
+
+ if (socket[kIdleSocketValidation] === 1) {
+ socket[kParser].readMore()
+ if (socket.destroyed) {
+ return
+ }
+ return
+ }
+ }
+
+ if (client[kRunning] === 0) {
+ socket[kParser].readMore()
+ if (socket.destroyed) {
+ return
+ }
+ }
+
if (client[kSize] === 0) {
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
@@ -36056,6 +36201,7 @@ function writeH1 (client, request) {
}
const socket = client[kSocket]
+ clearIdleSocketValidation(socket)
const abort = (err) => {
if (request.aborted || request.completed) {
@@ -37928,6 +38074,7 @@ class DispatcherBase extends Dispatcher {
get webSocketOptions () {
return {
+ maxFragments: this[kWebSocketOptions].maxFragments ?? 131072,
maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024
}
}
@@ -43864,32 +44011,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {})
// If the attribute-name case-insensitively matches the string
// "SameSite", the user agent MUST process the cookie-av as follows:
- // 1. Let enforcement be "Default".
- let enforcement = 'Default'
-
const attributeValueLowercase = attributeValue.toLowerCase()
- // 2. If cookie-av's attribute-value is a case-insensitive match for
- // "None", set enforcement to "None".
- if (attributeValueLowercase.includes('none')) {
- enforcement = 'None'
- }
- // 3. If cookie-av's attribute-value is a case-insensitive match for
- // "Strict", set enforcement to "Strict".
- if (attributeValueLowercase.includes('strict')) {
- enforcement = 'Strict'
+ // 1. If cookie-av's attribute-value is a case-insensitive match for
+ // "None", append an attribute to the cookie-attribute-list with an
+ // attribute-name of "SameSite" and an attribute-value of "None".
+ if (attributeValueLowercase === 'none') {
+ cookieAttributeList.sameSite = 'None'
+ } else if (attributeValueLowercase === 'strict') {
+ // 2. If cookie-av's attribute-value is a case-insensitive match for
+ // "Strict", append an attribute to the cookie-attribute-list with
+ // an attribute-name of "SameSite" and an attribute-value of
+ // "Strict".
+ cookieAttributeList.sameSite = 'Strict'
+ } else if (attributeValueLowercase === 'lax') {
+ // 3. If cookie-av's attribute-value is a case-insensitive match for
+ // "Lax", append an attribute to the cookie-attribute-list with an
+ // attribute-name of "SameSite" and an attribute-value of "Lax".
+ cookieAttributeList.sameSite = 'Lax'
}
-
- // 4. If cookie-av's attribute-value is a case-insensitive match for
- // "Lax", set enforcement to "Lax".
- if (attributeValueLowercase.includes('lax')) {
- enforcement = 'Lax'
- }
-
- // 5. Append an attribute to the cookie-attribute-list with an
- // attribute-name of "SameSite" and an attribute-value of
- // enforcement.
- cookieAttributeList.sameSite = enforcement
} else {
cookieAttributeList.unparsed ??= []
@@ -56715,6 +56855,11 @@ const { closeWebSocketConnection } = __nccwpck_require__(86897)
const { PerMessageDeflate } = __nccwpck_require__(19469)
const { MessageSizeExceededError } = __nccwpck_require__(68707)
+function failWebsocketConnectionWithCode (ws, code, reason) {
+ closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason))
+ failWebsocketConnection(ws, reason)
+}
+
// This code was influenced by ws released under the MIT license.
// Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
// Copyright (c) 2013 Arnout Kazemier and contributors
@@ -56734,19 +56879,23 @@ class ByteParser extends Writable {
/** @type {Map<string, PerMessageDeflate>} */
#extensions
+ /** @type {number} */
+ #maxFragments
+
/** @type {number} */
#maxPayloadSize
/**
* @param {import('./websocket').WebSocket} ws
* @param {Map<string, string>|null} extensions
- * @param {{ maxPayloadSize?: number }} [options]
+ * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options]
*/
constructor (ws, extensions, options = {}) {
super()
this.ws = ws
this.#extensions = extensions == null ? new Map() : extensions
+ this.#maxFragments = options.maxFragments ?? 0
this.#maxPayloadSize = options.maxPayloadSize ?? 0
if (this.#extensions.has('permessage-deflate')) {
@@ -56770,9 +56919,9 @@ class ByteParser extends Writable {
if (
this.#maxPayloadSize > 0 &&
!isControlFrame(this.#info.opcode) &&
- this.#info.payloadLength > this.#maxPayloadSize
+ this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize
) {
- failWebsocketConnection(this.ws, 'Payload size exceeds maximum allowed size')
+ failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size')
return false
}
@@ -56937,10 +57086,12 @@ class ByteParser extends Writable {
this.#state = parserStates.INFO
} else {
if (!this.#info.compressed) {
- this.writeFragments(body)
+ if (!this.writeFragments(body)) {
+ return
+ }
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
- failWebsocketConnection(this.ws, new MessageSizeExceededError().message)
+ failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
return
}
@@ -56959,14 +57110,17 @@ class ByteParser extends Writable {
this.#info.fin,
(error, data) => {
if (error) {
- failWebsocketConnection(this.ws, error.message)
+ const code = error instanceof MessageSizeExceededError ? 1009 : 1007
+ failWebsocketConnectionWithCode(this.ws, code, error.message)
return
}
- this.writeFragments(data)
+ if (!this.writeFragments(data)) {
+ return
+ }
if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {
- failWebsocketConnection(this.ws, new MessageSizeExceededError().message)
+ failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message)
return
}
@@ -57036,8 +57190,17 @@ class ByteParser extends Writable {
}
writeFragments (fragment) {
+ if (
+ this.#maxFragments > 0 &&
+ this.#fragments.length === this.#maxFragments
+ ) {
+ failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments')
+ return false
+ }
+
this.#fragmentsBytes += fragment.length
this.#fragments.push(fragment)
+ return true
}
consumeFragments () {
@@ -58090,9 +58253,12 @@ class WebSocket extends EventTarget {
// once this happens, the connection is open
this[kResponse] = response
- const maxPayloadSize = this[kController]?.dispatcher?.webSocketOptions?.maxPayloadSize
+ const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions
+ const maxFragments = webSocketOptions?.maxFragments
+ const maxPayloadSize = webSocketOptions?.maxPayloadSize
const parser = new ByteParser(this, parsedExtensions, {
+ maxFragments,
maxPayloadSize
})
parser.on('drain', onParserDrain)
@@ -59301,15 +59467,23 @@ function modulesAutoDetection(rootDir) {
async function runLint(binPath) {
const workingDirectory = getWorkingDirectory();
const experimental = core.getInput(`experimental`).split(`,`);
+ const noGroup = experimental.includes(`no-run-logs-group`);
if (experimental.includes(`automatic-module-directories`)) {
const wds = modulesAutoDetection(workingDirectory);
const cwd = process.cwd();
for (const wd of wds) {
- await core.group(`run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd));
+ await optionalGroup(noGroup, `run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd));
}
return;
}
- await core.group(`run golangci-lint`, () => runGolangciLint(binPath, workingDirectory));
+ await optionalGroup(noGroup, `run golangci-lint`, () => runGolangciLint(binPath, workingDirectory));
+}
+async function optionalGroup(noGroup, name, fn) {
+ if (noGroup) {
+ core.info(name);
+ return fn();
+ }
+ return core.group(name, fn);
}
async function run() {
try {
diff --git package.json package.json
index 5f8a8a228f..d82096b761 100644
--- package.json
+++ package.json
@@ -1,6 +1,6 @@
{
"name": "golanci-lint-action",
- "version": "9.2.1",
+ "version": "9.3.0",
"private": true,
"description": "golangci-lint github action",
"main": "dist/main.js",
@@ -38,7 +38,7 @@
"@types/semver": "^7.7.1",
"@types/tmp": "^0.2.6",
"@types/which": "^3.0.4",
- "tmp": "^0.2.5",
+ "tmp": "^0.2.7",
"which": "^7.0.0",
"yaml": "^2.9.0"
},
@@ -59,6 +59,6 @@
"typescript": "^5.9.3"
},
"overrides": {
- "undici": "^6.24.0"
+ "undici": "^6.27.0"
}
}
diff --git src/run.ts src/run.ts
index 7faeafb5d1..031bdf40f1 100644
--- src/run.ts
+++ src/run.ts
@@ -205,19 +205,31 @@ async function runLint(binPath: string): Promise<void> {
const experimental = core.getInput(`experimental`).split(`,`)
+ const noGroup = experimental.includes(`no-run-logs-group`)
+
if (experimental.includes(`automatic-module-directories`)) {
const wds = modulesAutoDetection(workingDirectory)
const cwd = process.cwd()
for (const wd of wds) {
- await core.group(`run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd))
+ await optionalGroup(noGroup, `run golangci-lint in ${path.relative(cwd, wd)}`, () => runGolangciLint(binPath, wd))
}
return
}
- await core.group(`run golangci-lint`, () => runGolangciLint(binPath, workingDirectory))
+ await optionalGroup(noGroup, `run golangci-lint`, () => runGolangciLint(binPath, workingDirectory))
+}
+
+async function optionalGroup<T>(noGroup: boolean, name: string, fn: () => Promise<T>): Promise<T> {
+ if (noGroup) {
+ core.info(name)
+
+ return fn()
+ }
+
+ return core.group(name, fn)
}
export async function run(): Promise<void> {
DescriptionAdds Possible Issues
Security Hotspots
None introduced by the source change itself. ChangesChanges
sequenceDiagram
participant Action
participant runLint
participant optionalGroup
participant core
participant golangci
Action->>runLint: invoke(binPath)
runLint->>core: getInput("experimental")
runLint->>runLint: noGroup = includes("no-run-logs-group")
alt automatic-module-directories
loop each working dir
runLint->>optionalGroup: (noGroup, name, fn)
end
else single dir
runLint->>optionalGroup: (noGroup, name, fn)
end
alt noGroup
optionalGroup->>core: info(name)
optionalGroup->>golangci: fn()
else grouped
optionalGroup->>core: group(name, fn)
core->>golangci: fn()
end
golangci-->>optionalGroup: result
optionalGroup-->>runLint: result
|
jwadolowski
approved these changes
Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
v9.2.1→v9.3.0Release Notes
golangci/golangci-lint-action (golangci/golangci-lint-action)
v9.3.0Compare Source
What's Changed
Changes
Dependencies
Full Changelog: golangci/golangci-lint-action@v9.2.1...v9.3.0
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.