mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
chore: Remove xregexp from the FTP client (#40676)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: RomanDavydchuk <roman.davydchuk@n8n.io>
This commit is contained in:
parent
8297b2e6d2
commit
2e049bb3de
51
packages/nodes-base/nodes/Ftp/__test__/listParser.test.ts
Normal file
51
packages/nodes-base/nodes/Ftp/__test__/listParser.test.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { createRequire } from 'node:module';
|
||||
|
||||
// promise-ftp parses LIST output with @icetee/ftp. A pnpm patch makes that parser use native RegExp.
|
||||
const requireFromPromiseFtp = createRequire(createRequire(__filename).resolve('promise-ftp'));
|
||||
const { parseListEntry } = requireFromPromiseFtp('@icetee/ftp/lib/parser') as {
|
||||
parseListEntry: (line: string) => unknown;
|
||||
};
|
||||
|
||||
describe('FTP LIST parser', () => {
|
||||
it('parses a Unix file entry', () => {
|
||||
expect(
|
||||
parseListEntry('-rw-r--r--+ 1 ftp ftp 12345678 Mar 3 2021 archive 2021.tar.gz'),
|
||||
).toEqual({
|
||||
type: '-',
|
||||
name: 'archive 2021.tar.gz',
|
||||
target: undefined,
|
||||
sticky: false,
|
||||
rights: { user: 'rw', group: 'r', other: 'r' },
|
||||
acl: true,
|
||||
owner: 'ftp',
|
||||
group: 'ftp',
|
||||
size: 12345678,
|
||||
date: new Date('2021-03-03'),
|
||||
});
|
||||
});
|
||||
|
||||
it('parses a Unix symbolic link with a sticky bit', () => {
|
||||
expect(
|
||||
parseListEntry('lrwxrwxrwt 1 root root 7 Feb 1 2024 latest -> v2.1.0'),
|
||||
).toMatchObject({
|
||||
type: 'l',
|
||||
name: 'latest',
|
||||
target: 'v2.1.0',
|
||||
sticky: true,
|
||||
rights: { user: 'rwx', group: 'rwx', other: 'rwx' },
|
||||
});
|
||||
});
|
||||
|
||||
it('parses an MS-DOS directory entry', () => {
|
||||
expect(parseListEntry('10-07-26 02:15PM <DIR> Reports')).toEqual({
|
||||
name: 'Reports',
|
||||
type: 'd',
|
||||
size: 0,
|
||||
date: new Date(2026, 9, 7, 14, 15),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a line that it cannot parse unchanged', () => {
|
||||
expect(parseListEntry('not a listing line')).toBe('not a listing line');
|
||||
});
|
||||
});
|
||||
69
patches/@icetee__ftp@0.3.15.patch
Normal file
69
patches/@icetee__ftp@0.3.15.patch
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
diff --git a/lib/connection.js b/lib/connection.js
|
||||
index 0afc364e7fcf955b2d373ede71d77acdeddeabf7..807542043645cb0b85db0610731f481b3c60c524 100644
|
||||
--- a/lib/connection.js
|
||||
+++ b/lib/connection.js
|
||||
@@ -8,9 +8,13 @@ var fs = require('fs'),
|
||||
StringDecoder = require('string_decoder').StringDecoder;
|
||||
|
||||
var Parser = require('./parser');
|
||||
-var XRegExp = require('xregexp').XRegExp;
|
||||
+// Run a regex with named groups and return the groups, or null when it does not match.
|
||||
+function execNamed(re, str) {
|
||||
+ var m = re.exec(str);
|
||||
+ return m ? m.groups : null;
|
||||
+}
|
||||
|
||||
-var REX_TIMEVAL = XRegExp.cache('^(?<year>\\d{4})(?<month>\\d{2})(?<date>\\d{2})(?<hour>\\d{2})(?<minute>\\d{2})(?<second>\\d+)(?:.\\d+)?$'),
|
||||
+var REX_TIMEVAL = new RegExp('^(?<year>\\d{4})(?<month>\\d{2})(?<date>\\d{2})(?<hour>\\d{2})(?<minute>\\d{2})(?<second>\\d+)(?:.\\d+)?$'),
|
||||
RE_PASV = /([\d]+),([\d]+),([\d]+),([\d]+),([-\d]+),([-\d]+)/,
|
||||
RE_EPSV = /([\d]+)/,
|
||||
RE_EOL = /\r?\n/g,
|
||||
@@ -865,7 +869,7 @@ FTP.prototype.lastMod = function(path, cb) {
|
||||
}, true);
|
||||
} else if (err)
|
||||
return cb(err);
|
||||
- var val = XRegExp.exec(text, REX_TIMEVAL), ret;
|
||||
+ var val = execNamed(REX_TIMEVAL, text), ret;
|
||||
if (!val)
|
||||
return cb(new Error('Invalid date/time format from server'));
|
||||
ret = new Date(val.year + '-' + val.month + '-' + val.date + 'T' + val.hour
|
||||
diff --git a/lib/parser.js b/lib/parser.js
|
||||
index 3eae00a4ded905c9aef34c5cda743ec63b18b521..2505bc7a17445052f9deda395f90514cf3405f79 100644
|
||||
--- a/lib/parser.js
|
||||
+++ b/lib/parser.js
|
||||
@@ -3,10 +3,14 @@ var WritableStream = require('stream').Writable
|
||||
inherits = require('util').inherits,
|
||||
inspect = require('util').inspect;
|
||||
|
||||
-var XRegExp = require('xregexp').XRegExp;
|
||||
+// Run a regex with named groups and return the groups, or null when it does not match.
|
||||
+function execNamed(re, str) {
|
||||
+ var m = re.exec(str);
|
||||
+ return m ? m.groups : null;
|
||||
+}
|
||||
|
||||
-var REX_LISTUNIX = XRegExp.cache('^(?<type>[\\-ld])(?<permission>([\\-r][\\-w][\\-xstT]){3})(?<acl>(\\+))?\\s+(?<inodes>\\d+)\\s+(?<owner>\\S+)\\s+(?<group>\\S+)\\s+(?<size>\\d+)\\s+(?<timestamp>((?<month1>\\w{3})\\s+(?<date1>\\d{1,2})\\s+(?<hour>\\d{1,2}):(?<minute>\\d{2}))|((?<month2>\\w{3})\\s+(?<date2>\\d{1,2})\\s+(?<year>\\d{4})))\\s+(?<name>.+)$'),
|
||||
- REX_LISTMSDOS = XRegExp.cache('^(?<month>\\d{2})(?:\\-|\\/)(?<date>\\d{2})(?:\\-|\\/)(?<year>\\d{2,4})\\s+(?<hour>\\d{2}):(?<minute>\\d{2})\\s{0,1}(?<ampm>[AaMmPp]{1,2})\\s+(?:(?<size>\\d+)|(?<isdir>\\<DIR\\>))\\s+(?<name>.+)$'),
|
||||
+var REX_LISTUNIX = new RegExp('^(?<type>[\\-ld])(?<permission>([\\-r][\\-w][\\-xstT]){3})(?<acl>(\\+))?\\s+(?<inodes>\\d+)\\s+(?<owner>\\S+)\\s+(?<group>\\S+)\\s+(?<size>\\d+)\\s+(?<timestamp>((?<month1>\\w{3})\\s+(?<date1>\\d{1,2})\\s+(?<hour>\\d{1,2}):(?<minute>\\d{2}))|((?<month2>\\w{3})\\s+(?<date2>\\d{1,2})\\s+(?<year>\\d{4})))\\s+(?<name>.+)$'),
|
||||
+ REX_LISTMSDOS = new RegExp('^(?<month>\\d{2})(?:\\-|\\/)(?<date>\\d{2})(?:\\-|\\/)(?<year>\\d{2,4})\\s+(?<hour>\\d{2}):(?<minute>\\d{2})\\s{0,1}(?<ampm>[AaMmPp]{1,2})\\s+(?:(?<size>\\d+)|(?<isdir>\\<DIR\\>))\\s+(?<name>.+)$'),
|
||||
RE_ENTRY_TOTAL = /^total/,
|
||||
RE_RES_END = /(?:^|\r?\n)(\d{3}) [^\r\n]*\r?\n/,
|
||||
RE_EOL = /\r?\n/g,
|
||||
@@ -82,7 +86,7 @@ Parser.parseListEntry = function(line) {
|
||||
month, day, year,
|
||||
hour, mins;
|
||||
|
||||
- if (ret = XRegExp.exec(line, REX_LISTUNIX)) {
|
||||
+ if (ret = execNamed(REX_LISTUNIX, line)) {
|
||||
info = {
|
||||
type: ret.type,
|
||||
name: undefined,
|
||||
@@ -182,7 +186,7 @@ Parser.parseListEntry = function(line) {
|
||||
} else
|
||||
info.name = ret.name;
|
||||
ret = info;
|
||||
- } else if (ret = XRegExp.exec(line, REX_LISTMSDOS)) {
|
||||
+ } else if (ret = execNamed(REX_LISTMSDOS, line)) {
|
||||
info = {
|
||||
name: ret.name,
|
||||
type: (ret.isdir ? 'd' : '-'),
|
||||
|
|
@ -1061,11 +1061,13 @@ overrides:
|
|||
source-map-js: 1.2.2
|
||||
'@grpc/grpc-js@<1.14.5': 1.14.5
|
||||
prosemirror-view@<1.42.3: 1.42.3
|
||||
'@icetee/ftp>xregexp': npm:empty-npm-package@1.0.0
|
||||
|
||||
patchedDependencies:
|
||||
'@chat-adapter/teams@4.37.0': af5fb1d38a2af55399bd8b6934e77348a9b061fec6177c101dd9d7a787ae8a6c
|
||||
'@chat-adapter/whatsapp@4.37.0': 950356dafb986b5cc9bd53069f7653b47b790a60b3590495cf53392ea4df2de4
|
||||
'@confluentinc/kafka-javascript@1.9.1': c597d3e3d9d6d0d33588b7dc1e88d55f270ba155721aad464bd7fb03e28f5003
|
||||
'@icetee/ftp@0.3.15': 19c547038e6400d80f724f77c3fad97093f9233d7596bf2dba38d3e0adb7dab9
|
||||
'@langchain/google-common@2.1.24': b50436a6c7b0a82d54472dff331ba1b42ab8b816995f83295eef2ee4b482a87d
|
||||
'@langchain/openai@1.4.4': e16bcc93cf9037198faacf494fb81afba6e9abba749dc9a09c3abfb33e68d5c2
|
||||
'@lezer/highlight': 97f85e6fe46f23015ea0dd420e33d584bc2dc71633910cf131321da31b27ca8c
|
||||
|
|
@ -25014,9 +25016,6 @@ packages:
|
|||
resolution: {integrity: sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
||||
xregexp@2.0.0:
|
||||
resolution: {integrity: sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==}
|
||||
|
||||
xss@1.0.15:
|
||||
resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
|
@ -28596,10 +28595,10 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@icetee/ftp@0.3.15':
|
||||
'@icetee/ftp@0.3.15(patch_hash=19c547038e6400d80f724f77c3fad97093f9233d7596bf2dba38d3e0adb7dab9)':
|
||||
dependencies:
|
||||
readable-stream: 1.1.14
|
||||
xregexp: 2.0.0
|
||||
xregexp: empty-npm-package@1.0.0
|
||||
|
||||
'@iconify-json/lucide@1.2.114':
|
||||
dependencies:
|
||||
|
|
@ -41831,7 +41830,7 @@ snapshots:
|
|||
|
||||
promise-ftp@1.3.5(promise-ftp-common@1.1.5):
|
||||
dependencies:
|
||||
'@icetee/ftp': 0.3.15
|
||||
'@icetee/ftp': 0.3.15(patch_hash=19c547038e6400d80f724f77c3fad97093f9233d7596bf2dba38d3e0adb7dab9)
|
||||
bluebird: 2.11.0
|
||||
promise-ftp-common: 1.1.5
|
||||
|
||||
|
|
@ -45407,8 +45406,6 @@ snapshots:
|
|||
|
||||
xpath@0.0.34: {}
|
||||
|
||||
xregexp@2.0.0: {}
|
||||
|
||||
xss@1.0.15:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
|
|
|
|||
|
|
@ -484,6 +484,8 @@ overrides:
|
|||
source-map-js: 1.2.2
|
||||
'@grpc/grpc-js@<1.14.5': 1.14.5
|
||||
'prosemirror-view@<1.42.3': 1.42.3
|
||||
# The @icetee/ftp patch replaces xregexp with native RegExp.
|
||||
'@icetee/ftp>xregexp': npm:empty-npm-package@1.0.0
|
||||
|
||||
patchedDependencies:
|
||||
bull@4.16.4: patches/bull@4.16.4.patch
|
||||
|
|
@ -508,6 +510,7 @@ patchedDependencies:
|
|||
imapflow@1.7.1: patches/imapflow@1.7.1.patch
|
||||
'@chat-adapter/teams@4.37.0': patches/@chat-adapter__teams@4.37.0.patch
|
||||
'@chat-adapter/whatsapp@4.37.0': patches/@chat-adapter__whatsapp@4.37.0.patch
|
||||
'@icetee/ftp@0.3.15': patches/@icetee__ftp@0.3.15.patch
|
||||
|
||||
allowBuilds:
|
||||
'@biomejs/biome': false
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user