mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Make parsing of content-type and content-disposition headers more flexible (#7217)
fixes #7149
This commit is contained in:
parent
6bc477b50e
commit
d41546b899
|
@ -35,8 +35,6 @@
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/concat-stream": "^2.0.0",
|
"@types/concat-stream": "^2.0.0",
|
||||||
"@types/content-disposition": "^0.5.5",
|
|
||||||
"@types/content-type": "^1.1.5",
|
|
||||||
"@types/cron": "~1.7.1",
|
"@types/cron": "~1.7.1",
|
||||||
"@types/crypto-js": "^4.0.1",
|
"@types/crypto-js": "^4.0.1",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
|
@ -52,8 +50,6 @@
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"@n8n/client-oauth2": "workspace:*",
|
"@n8n/client-oauth2": "workspace:*",
|
||||||
"concat-stream": "^2.0.0",
|
"concat-stream": "^2.0.0",
|
||||||
"content-disposition": "^0.5.4",
|
|
||||||
"content-type": "^1.0.4",
|
|
||||||
"cron": "~1.7.2",
|
"cron": "~1.7.2",
|
||||||
"crypto-js": "~4.1.1",
|
"crypto-js": "~4.1.1",
|
||||||
"fast-glob": "^3.2.5",
|
"fast-glob": "^3.2.5",
|
||||||
|
|
|
@ -11,100 +11,12 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-shadow */
|
/* eslint-disable @typescript-eslint/no-shadow */
|
||||||
|
|
||||||
import type {
|
|
||||||
GenericValue,
|
|
||||||
IAdditionalCredentialOptions,
|
|
||||||
IAllExecuteFunctions,
|
|
||||||
IBinaryData,
|
|
||||||
IContextObject,
|
|
||||||
ICredentialDataDecryptedObject,
|
|
||||||
ICredentialsExpressionResolveValues,
|
|
||||||
IDataObject,
|
|
||||||
IExecuteResponsePromiseData,
|
|
||||||
IExecuteWorkflowInfo,
|
|
||||||
IHttpRequestOptions,
|
|
||||||
IN8nHttpFullResponse,
|
|
||||||
IN8nHttpResponse,
|
|
||||||
INode,
|
|
||||||
INodeCredentialDescription,
|
|
||||||
INodeCredentialsDetails,
|
|
||||||
INodeExecutionData,
|
|
||||||
IOAuth2Options,
|
|
||||||
IRunExecutionData,
|
|
||||||
ISourceData,
|
|
||||||
ITaskDataConnections,
|
|
||||||
IWebhookData,
|
|
||||||
IWebhookDescription,
|
|
||||||
IWorkflowDataProxyAdditionalKeys,
|
|
||||||
IWorkflowDataProxyData,
|
|
||||||
IWorkflowExecuteAdditionalData,
|
|
||||||
Workflow,
|
|
||||||
WorkflowActivateMode,
|
|
||||||
WorkflowExecuteMode,
|
|
||||||
IExecuteData,
|
|
||||||
IGetNodeParameterOptions,
|
|
||||||
NodeParameterValueType,
|
|
||||||
NodeExecutionWithMetadata,
|
|
||||||
IPairedItemData,
|
|
||||||
ICredentialTestFunctions,
|
|
||||||
BinaryHelperFunctions,
|
|
||||||
NodeHelperFunctions,
|
|
||||||
RequestHelperFunctions,
|
|
||||||
FunctionsBase,
|
|
||||||
IExecuteFunctions,
|
|
||||||
IExecuteSingleFunctions,
|
|
||||||
IHookFunctions,
|
|
||||||
ILoadOptionsFunctions,
|
|
||||||
IPollFunctions,
|
|
||||||
ITriggerFunctions,
|
|
||||||
IWebhookFunctions,
|
|
||||||
BinaryMetadata,
|
|
||||||
FileSystemHelperFunctions,
|
|
||||||
INodeType,
|
|
||||||
INodePropertyCollection,
|
|
||||||
INodePropertyOptions,
|
|
||||||
FieldType,
|
|
||||||
INodeProperties,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
import {
|
|
||||||
createDeferredPromise,
|
|
||||||
isObjectEmpty,
|
|
||||||
isResourceMapperValue,
|
|
||||||
NodeApiError,
|
|
||||||
NodeHelpers,
|
|
||||||
NodeOperationError,
|
|
||||||
WorkflowDataProxy,
|
|
||||||
LoggerProxy as Logger,
|
|
||||||
OAuth2GrantType,
|
|
||||||
deepCopy,
|
|
||||||
fileTypeFromMimeType,
|
|
||||||
ExpressionError,
|
|
||||||
validateFieldType,
|
|
||||||
NodeSSLError,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
import { parse as parseContentDisposition } from 'content-disposition';
|
|
||||||
import { parse as parseContentType } from 'content-type';
|
|
||||||
import pick from 'lodash/pick';
|
|
||||||
import { Agent } from 'https';
|
|
||||||
import { IncomingMessage, type IncomingHttpHeaders } from 'http';
|
|
||||||
import { stringify } from 'qs';
|
|
||||||
import type { Token } from 'oauth-1.0a';
|
|
||||||
import clientOAuth1 from 'oauth-1.0a';
|
|
||||||
import type {
|
import type {
|
||||||
ClientOAuth2Options,
|
ClientOAuth2Options,
|
||||||
ClientOAuth2RequestObject,
|
ClientOAuth2RequestObject,
|
||||||
ClientOAuth2TokenData,
|
ClientOAuth2TokenData,
|
||||||
} from '@n8n/client-oauth2';
|
} from '@n8n/client-oauth2';
|
||||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||||
import crypto, { createHmac } from 'crypto';
|
|
||||||
import get from 'lodash/get';
|
|
||||||
import type { Request, Response } from 'express';
|
|
||||||
import FormData from 'form-data';
|
|
||||||
import path from 'path';
|
|
||||||
import type { OptionsWithUri, OptionsWithUrl } from 'request';
|
|
||||||
import type { RequestPromiseOptions } from 'request-promise-native';
|
|
||||||
import FileType from 'file-type';
|
|
||||||
import { lookup, extension } from 'mime-types';
|
|
||||||
import type {
|
import type {
|
||||||
AxiosError,
|
AxiosError,
|
||||||
AxiosPromise,
|
AxiosPromise,
|
||||||
|
@ -114,34 +26,120 @@ import type {
|
||||||
Method,
|
Method,
|
||||||
} from 'axios';
|
} from 'axios';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import url, { URL, URLSearchParams } from 'url';
|
import crypto, { createHmac } from 'crypto';
|
||||||
import { Readable } from 'stream';
|
import type { Request, Response } from 'express';
|
||||||
import { access as fsAccess, writeFile as fsWriteFile } from 'fs/promises';
|
import FileType from 'file-type';
|
||||||
|
import FormData from 'form-data';
|
||||||
import { createReadStream } from 'fs';
|
import { createReadStream } from 'fs';
|
||||||
|
import { access as fsAccess, writeFile as fsWriteFile } from 'fs/promises';
|
||||||
|
import { IncomingMessage, type IncomingHttpHeaders } from 'http';
|
||||||
|
import { Agent } from 'https';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import pick from 'lodash/pick';
|
||||||
|
import { extension, lookup } from 'mime-types';
|
||||||
|
import type {
|
||||||
|
BinaryHelperFunctions,
|
||||||
|
BinaryMetadata,
|
||||||
|
FieldType,
|
||||||
|
FileSystemHelperFunctions,
|
||||||
|
FunctionsBase,
|
||||||
|
GenericValue,
|
||||||
|
IAdditionalCredentialOptions,
|
||||||
|
IAllExecuteFunctions,
|
||||||
|
IBinaryData,
|
||||||
|
IContextObject,
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
|
ICredentialTestFunctions,
|
||||||
|
ICredentialsExpressionResolveValues,
|
||||||
|
IDataObject,
|
||||||
|
IExecuteData,
|
||||||
|
IExecuteFunctions,
|
||||||
|
IExecuteResponsePromiseData,
|
||||||
|
IExecuteSingleFunctions,
|
||||||
|
IExecuteWorkflowInfo,
|
||||||
|
IGetNodeParameterOptions,
|
||||||
|
IHookFunctions,
|
||||||
|
IHttpRequestOptions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
IN8nHttpFullResponse,
|
||||||
|
IN8nHttpResponse,
|
||||||
|
INode,
|
||||||
|
INodeCredentialDescription,
|
||||||
|
INodeCredentialsDetails,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeProperties,
|
||||||
|
INodePropertyCollection,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
IOAuth2Options,
|
||||||
|
IPairedItemData,
|
||||||
|
IPollFunctions,
|
||||||
|
IRunExecutionData,
|
||||||
|
ISourceData,
|
||||||
|
ITaskDataConnections,
|
||||||
|
ITriggerFunctions,
|
||||||
|
IWebhookData,
|
||||||
|
IWebhookDescription,
|
||||||
|
IWebhookFunctions,
|
||||||
|
IWorkflowDataProxyAdditionalKeys,
|
||||||
|
IWorkflowDataProxyData,
|
||||||
|
IWorkflowExecuteAdditionalData,
|
||||||
|
NodeExecutionWithMetadata,
|
||||||
|
NodeHelperFunctions,
|
||||||
|
NodeParameterValueType,
|
||||||
|
RequestHelperFunctions,
|
||||||
|
Workflow,
|
||||||
|
WorkflowActivateMode,
|
||||||
|
WorkflowExecuteMode,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
import {
|
||||||
|
ExpressionError,
|
||||||
|
LoggerProxy as Logger,
|
||||||
|
NodeApiError,
|
||||||
|
NodeHelpers,
|
||||||
|
NodeOperationError,
|
||||||
|
NodeSSLError,
|
||||||
|
OAuth2GrantType,
|
||||||
|
WorkflowDataProxy,
|
||||||
|
createDeferredPromise,
|
||||||
|
deepCopy,
|
||||||
|
fileTypeFromMimeType,
|
||||||
|
isObjectEmpty,
|
||||||
|
isResourceMapperValue,
|
||||||
|
validateFieldType,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
import type { Token } from 'oauth-1.0a';
|
||||||
|
import clientOAuth1 from 'oauth-1.0a';
|
||||||
|
import path from 'path';
|
||||||
|
import { stringify } from 'qs';
|
||||||
|
import type { OptionsWithUri, OptionsWithUrl } from 'request';
|
||||||
|
import type { RequestPromiseOptions } from 'request-promise-native';
|
||||||
|
import { Readable } from 'stream';
|
||||||
|
import url, { URL, URLSearchParams } from 'url';
|
||||||
|
|
||||||
import { BinaryDataManager } from './BinaryDataManager';
|
import { BinaryDataManager } from './BinaryDataManager';
|
||||||
import type { ExtendedValidationResult, IResponseError, IWorkflowSettings } from './Interfaces';
|
import { binaryToBuffer } from './BinaryDataManager/utils';
|
||||||
import { extractValue } from './ExtractValue';
|
|
||||||
import { getClientCredentialsToken } from './OAuth2Helper';
|
|
||||||
import {
|
import {
|
||||||
|
BINARY_DATA_STORAGE_PATH,
|
||||||
|
BLOCK_FILE_ACCESS_TO_N8N_FILES,
|
||||||
|
CONFIG_FILES,
|
||||||
CUSTOM_EXTENSION_ENV,
|
CUSTOM_EXTENSION_ENV,
|
||||||
PLACEHOLDER_EMPTY_EXECUTION_ID,
|
PLACEHOLDER_EMPTY_EXECUTION_ID,
|
||||||
BLOCK_FILE_ACCESS_TO_N8N_FILES,
|
|
||||||
RESTRICT_FILE_ACCESS_TO,
|
RESTRICT_FILE_ACCESS_TO,
|
||||||
CONFIG_FILES,
|
|
||||||
BINARY_DATA_STORAGE_PATH,
|
|
||||||
UM_EMAIL_TEMPLATES_INVITE,
|
UM_EMAIL_TEMPLATES_INVITE,
|
||||||
UM_EMAIL_TEMPLATES_PWRESET,
|
UM_EMAIL_TEMPLATES_PWRESET,
|
||||||
} from './Constants';
|
} from './Constants';
|
||||||
import { binaryToBuffer } from './BinaryDataManager/utils';
|
import { extractValue } from './ExtractValue';
|
||||||
|
import type { ExtendedValidationResult, IResponseError, IWorkflowSettings } from './Interfaces';
|
||||||
|
import { getClientCredentialsToken } from './OAuth2Helper';
|
||||||
|
import { getSecretsProxy } from './Secrets';
|
||||||
|
import { getUserN8nFolderPath } from './UserSettings';
|
||||||
import {
|
import {
|
||||||
getAllWorkflowExecutionMetadata,
|
getAllWorkflowExecutionMetadata,
|
||||||
getWorkflowExecutionMetadata,
|
getWorkflowExecutionMetadata,
|
||||||
setAllWorkflowExecutionMetadata,
|
setAllWorkflowExecutionMetadata,
|
||||||
setWorkflowExecutionMetadata,
|
setWorkflowExecutionMetadata,
|
||||||
} from './WorkflowExecutionMetadata';
|
} from './WorkflowExecutionMetadata';
|
||||||
import { getSecretsProxy } from './Secrets';
|
|
||||||
import { getUserN8nFolderPath } from './UserSettings';
|
|
||||||
|
|
||||||
axios.defaults.timeout = 300000;
|
axios.defaults.timeout = 300000;
|
||||||
// Prevent axios from adding x-form-www-urlencoded headers by default
|
// Prevent axios from adding x-form-www-urlencoded headers by default
|
||||||
|
@ -604,26 +602,91 @@ type ConfigObject = {
|
||||||
simple?: boolean;
|
simple?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function parseIncomingMessage(message: IncomingMessage) {
|
interface IContentType {
|
||||||
if ('content-type' in message.headers) {
|
type: string;
|
||||||
const { type: contentType, parameters } = (() => {
|
parameters: {
|
||||||
try {
|
charset: string;
|
||||||
return parseContentType(message);
|
[key: string]: string;
|
||||||
} catch {
|
};
|
||||||
return { type: undefined, parameters: undefined };
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
message.contentType = contentType;
|
|
||||||
message.encoding = (parameters?.charset ?? 'utf-8').toLowerCase() as BufferEncoding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentDispositionHeader = message.headers['content-disposition'];
|
interface IContentDisposition {
|
||||||
if (contentDispositionHeader?.length) {
|
type: string;
|
||||||
const {
|
filename?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHeaderParameters(parameters: string[]): Record<string, string> {
|
||||||
|
return parameters.reduce(
|
||||||
|
(acc, param) => {
|
||||||
|
const [key, value] = param.split('=');
|
||||||
|
acc[key.toLowerCase().trim()] = decodeURIComponent(value);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseContentType(contentType?: string): IContentType | null {
|
||||||
|
if (!contentType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [type, ...parameters] = contentType.split(';');
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: type.toLowerCase(),
|
||||||
|
parameters: { charset: 'utf-8', ...parseHeaderParameters(parameters) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFileName(filename?: string): string | undefined {
|
||||||
|
if (filename?.startsWith('"') && filename?.endsWith('"')) {
|
||||||
|
return filename.slice(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc5987
|
||||||
|
function parseFileNameStar(filename?: string): string | undefined {
|
||||||
|
const [_encoding, _locale, content] = filename?.split("'") ?? [];
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseContentDisposition(contentDisposition?: string): IContentDisposition | null {
|
||||||
|
if (!contentDisposition) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is invalid syntax, but common
|
||||||
|
// Example 'filename="example.png"' (instead of 'attachment; filename="example.png"')
|
||||||
|
if (!contentDisposition.startsWith('attachment') && !contentDisposition.startsWith('inline')) {
|
||||||
|
contentDisposition = `attachment; ${contentDisposition}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [type, ...parameters] = contentDisposition.split(';');
|
||||||
|
|
||||||
|
const parsedParameters = parseHeaderParameters(parameters);
|
||||||
|
|
||||||
|
return {
|
||||||
type,
|
type,
|
||||||
parameters: { filename },
|
filename:
|
||||||
} = parseContentDisposition(contentDispositionHeader);
|
parseFileNameStar(parsedParameters['filename*']) ?? parseFileName(parsedParameters.filename),
|
||||||
message.contentDisposition = { type, filename };
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseIncomingMessage(message: IncomingMessage) {
|
||||||
|
const contentType = parseContentType(message.headers['content-type']);
|
||||||
|
if (contentType) {
|
||||||
|
const { type, parameters } = contentType;
|
||||||
|
message.contentType = type;
|
||||||
|
message.encoding = parameters.charset.toLowerCase() as BufferEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentDisposition = parseContentDisposition(message.headers['content-disposition']);
|
||||||
|
if (contentDisposition) {
|
||||||
|
message.contentDisposition = contentDisposition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import nock from 'nock';
|
import { BinaryDataManager } from '@/BinaryDataManager';
|
||||||
import { join } from 'path';
|
import {
|
||||||
import { tmpdir } from 'os';
|
getBinaryDataBuffer,
|
||||||
import { readFileSync, mkdtempSync } from 'fs';
|
parseIncomingMessage,
|
||||||
|
proxyRequestToAxios,
|
||||||
|
setBinaryDataBuffer,
|
||||||
|
} from '@/NodeExecuteFunctions';
|
||||||
|
import { mkdtempSync, readFileSync } from 'fs';
|
||||||
|
import type { IncomingMessage } from 'http';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type {
|
import type {
|
||||||
IBinaryData,
|
IBinaryData,
|
||||||
|
@ -11,12 +16,9 @@ import type {
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowHooks,
|
WorkflowHooks,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { BinaryDataManager } from '@/BinaryDataManager';
|
import nock from 'nock';
|
||||||
import {
|
import { tmpdir } from 'os';
|
||||||
setBinaryDataBuffer,
|
import { join } from 'path';
|
||||||
getBinaryDataBuffer,
|
|
||||||
proxyRequestToAxios,
|
|
||||||
} from '@/NodeExecuteFunctions';
|
|
||||||
import { initLogger } from './helpers/utils';
|
import { initLogger } from './helpers/utils';
|
||||||
|
|
||||||
const temporaryDir = mkdtempSync(join(tmpdir(), 'n8n'));
|
const temporaryDir = mkdtempSync(join(tmpdir(), 'n8n'));
|
||||||
|
@ -136,6 +138,75 @@ describe('NodeExecuteFunctions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parseIncomingMessage', () => {
|
||||||
|
it('parses valid content-type header', () => {
|
||||||
|
const message = mock<IncomingMessage>({
|
||||||
|
headers: { 'content-type': 'application/json', 'content-disposition': undefined },
|
||||||
|
});
|
||||||
|
parseIncomingMessage(message);
|
||||||
|
|
||||||
|
expect(message.contentType).toEqual('application/json');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses valid content-type header with parameters', () => {
|
||||||
|
const message = mock<IncomingMessage>({
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json; charset=utf-8',
|
||||||
|
'content-disposition': undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
parseIncomingMessage(message);
|
||||||
|
|
||||||
|
expect(message.contentType).toEqual('application/json');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses valid content-disposition header with filename*', () => {
|
||||||
|
const message = mock<IncomingMessage>({
|
||||||
|
headers: {
|
||||||
|
'content-type': undefined,
|
||||||
|
'content-disposition':
|
||||||
|
'attachment; filename="screenshot%20(1).png"; filename*=UTF-8\'\'screenshot%20(1).png',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
parseIncomingMessage(message);
|
||||||
|
|
||||||
|
expect(message.contentDisposition).toEqual({
|
||||||
|
filename: 'screenshot (1).png',
|
||||||
|
type: 'attachment',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses valid content-disposition header with filename and trailing ";"', () => {
|
||||||
|
const message = mock<IncomingMessage>({
|
||||||
|
headers: {
|
||||||
|
'content-type': undefined,
|
||||||
|
'content-disposition': 'inline; filename="screenshot%20(1).png";',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
parseIncomingMessage(message);
|
||||||
|
|
||||||
|
expect(message.contentDisposition).toEqual({
|
||||||
|
filename: 'screenshot (1).png',
|
||||||
|
type: 'inline',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses non standard content-disposition with missing type', () => {
|
||||||
|
const message = mock<IncomingMessage>({
|
||||||
|
headers: {
|
||||||
|
'content-type': undefined,
|
||||||
|
'content-disposition': 'filename="screenshot%20(1).png";',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
parseIncomingMessage(message);
|
||||||
|
|
||||||
|
expect(message.contentDisposition).toEqual({
|
||||||
|
filename: 'screenshot (1).png',
|
||||||
|
type: 'attachment',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('proxyRequestToAxios', () => {
|
describe('proxyRequestToAxios', () => {
|
||||||
const baseUrl = 'http://example.de';
|
const baseUrl = 'http://example.de';
|
||||||
const workflow = mock<Workflow>();
|
const workflow = mock<Workflow>();
|
||||||
|
|
|
@ -576,12 +576,6 @@ importers:
|
||||||
concat-stream:
|
concat-stream:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
content-disposition:
|
|
||||||
specifier: ^0.5.4
|
|
||||||
version: 0.5.4
|
|
||||||
content-type:
|
|
||||||
specifier: ^1.0.4
|
|
||||||
version: 1.0.4
|
|
||||||
cron:
|
cron:
|
||||||
specifier: ~1.7.2
|
specifier: ~1.7.2
|
||||||
version: 1.7.2
|
version: 1.7.2
|
||||||
|
@ -631,12 +625,6 @@ importers:
|
||||||
'@types/concat-stream':
|
'@types/concat-stream':
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
'@types/content-disposition':
|
|
||||||
specifier: ^0.5.5
|
|
||||||
version: 0.5.5
|
|
||||||
'@types/content-type':
|
|
||||||
specifier: ^1.1.5
|
|
||||||
version: 1.1.5
|
|
||||||
'@types/cron':
|
'@types/cron':
|
||||||
specifier: ~1.7.1
|
specifier: ~1.7.1
|
||||||
version: 1.7.3
|
version: 1.7.3
|
||||||
|
@ -6968,14 +6956,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.16.16
|
'@types/node': 18.16.16
|
||||||
|
|
||||||
/@types/content-disposition@0.5.5:
|
|
||||||
resolution: {integrity: sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/content-type@1.1.5:
|
|
||||||
resolution: {integrity: sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/convict@6.1.1:
|
/@types/convict@6.1.1:
|
||||||
resolution: {integrity: sha512-R+JLaTvhsD06p4jyjUDtbd5xMtZTRE3c0iI+lrFWZogSVEjgTWPYwvJPVf+t92E+yrlbXa4X4Eg9ro6gPdUt4w==}
|
resolution: {integrity: sha512-R+JLaTvhsD06p4jyjUDtbd5xMtZTRE3c0iI+lrFWZogSVEjgTWPYwvJPVf+t92E+yrlbXa4X4Eg9ro6gPdUt4w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue