fix(core): Log warning where file values contain white space at the start or end (e.g. new lines) (#24442)

This commit is contained in:
Marc Littlemore
2026-01-29 08:58:00 +00:00
committed by GitHub
parent b98d0fc98a
commit ae17e8ec61
4 changed files with 96 additions and 1 deletions
+9 -1
View File
@@ -21,7 +21,15 @@ const readEnv = (envName: string) => {
// Read the value from a file, if "_FILE" environment variable is defined
const filePath = process.env[`${envName}_FILE`];
if (filePath) return readFileSync(filePath, 'utf8');
if (filePath) {
const value = readFileSync(filePath, 'utf8');
if (value !== value.trim()) {
console.warn(
`[n8n] Warning: The file specified by ${envName}_FILE contains leading or trailing whitespace, which may cause authentication failures.`,
);
}
return value;
}
return undefined;
};
+18
View File
@@ -516,6 +516,24 @@ describe('GlobalConfig', () => {
expect(mockFs.readFileSync).toHaveBeenCalled();
});
it('should warn when _FILE env variable value contains whitespace', () => {
const passwordFile = '/path/to/postgres/password';
process.env = {
DB_POSTGRESDB_PASSWORD_FILE: passwordFile,
};
mockFs.readFileSync
.calledWith(passwordFile, 'utf8')
.mockReturnValueOnce('password-from-file\n');
const config = Container.get(GlobalConfig);
expect(config.database.postgresdb.password).toBe('password-from-file');
expect(consoleWarnMock).toHaveBeenCalledWith(
expect.stringContaining(
'DB_POSTGRESDB_PASSWORD_FILE contains leading or trailing whitespace',
),
);
});
it('should handle invalid numbers', () => {
process.env = {
DB_LOGGING_MAX_EXECUTION_TIME: 'abcd',
@@ -1,10 +1,22 @@
import { Container } from '@n8n/di';
import fs from 'fs';
import { Config, Env } from '../src/decorators';
jest.mock('fs');
const mockFs = jest.mocked(fs);
describe('decorators', () => {
const originalEnv = process.env;
beforeEach(() => {
Container.reset();
process.env = {};
jest.clearAllMocks();
});
afterEach(() => {
process.env = originalEnv;
});
it('should throw when explicit typing is missing', () => {
@@ -19,4 +31,56 @@ describe('decorators', () => {
'Invalid decorator metadata on key "value" on InvalidConfig\n Please use explicit typing on all config fields',
);
});
it('should read value from _FILE env variable', () => {
const filePath = '/path/to/secret';
process.env.TEST_VALUE_FILE = filePath;
mockFs.readFileSync.mockReturnValueOnce('secret-value');
@Config
class TestConfig {
@Env('TEST_VALUE')
value: string = 'default';
}
const config = Container.get(TestConfig);
expect(config.value).toBe('secret-value');
expect(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
});
it('should warn when _FILE env variable value contains whitespace', () => {
const filePath = '/path/to/secret';
process.env.TEST_VALUE_FILE = filePath;
mockFs.readFileSync.mockReturnValueOnce('secret-value\n');
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
@Config
class TestConfig {
@Env('TEST_VALUE')
value: string = 'default';
}
const config = Container.get(TestConfig);
expect(config.value).toBe('secret-value');
expect(consoleWarnSpy).toHaveBeenCalledWith(
expect.stringContaining('TEST_VALUE_FILE contains leading or trailing whitespace'),
);
consoleWarnSpy.mockRestore();
});
it('should prefer direct env variable over _FILE variant', () => {
const filePath = '/path/to/secret';
process.env.TEST_VALUE = 'direct-value';
process.env.TEST_VALUE_FILE = filePath;
@Config
class TestConfig {
@Env('TEST_VALUE')
value: string = 'default';
}
const config = Container.get(TestConfig);
expect(config.value).toBe('direct-value');
expect(mockFs.readFileSync).not.toHaveBeenCalled();
});
});
+5
View File
@@ -49,6 +49,11 @@ if (!inE2ETests && !inTest) {
}
throw error;
}
if (value !== value.trim()) {
console.warn(
`[n8n] Warning: The file specified by ${envName} contains leading or trailing whitespace, which may cause authentication failures.`,
);
}
config.set(key, value);
}
}