mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 01:26:44 +00:00
feat: Add N8N_GIT_NODE_DISABLE_BARE_REPOS environment variable to allow users to disable bare repositories in Git Node (#19559)
This commit is contained in:
@@ -45,4 +45,10 @@ export class SecurityConfig {
|
||||
*/
|
||||
@Env('N8N_INSECURE_DISABLE_WEBHOOK_IFRAME_SANDBOX')
|
||||
disableWebhookHtmlSandboxing: boolean = false;
|
||||
|
||||
/**
|
||||
* Whether to disable bare repositories support in the Git node.
|
||||
*/
|
||||
@Env('N8N_GIT_NODE_DISABLE_BARE_REPOS')
|
||||
disableBareRepos: boolean = false;
|
||||
}
|
||||
|
||||
@@ -307,6 +307,7 @@ describe('GlobalConfig', () => {
|
||||
contentSecurityPolicy: '{}',
|
||||
contentSecurityPolicyReportOnly: false,
|
||||
disableWebhookHtmlSandboxing: false,
|
||||
disableBareRepos: false,
|
||||
},
|
||||
executions: {
|
||||
timeout: -1,
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
This list shows all the versions which include breaking changes and how to upgrade.
|
||||
|
||||
# 1.113.0
|
||||
|
||||
### What changed?
|
||||
|
||||
Support for bare repositories in Git Node was dropped in the cloud version of n8n due to security reasons. Also, an environment variable `N8N_GIT_NODE_DISABLE_BARE_REPOS` was added that allows self-hosted users to disable bare repositories as well.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you have workflows that use the Git Node and work with bare git repositories.
|
||||
|
||||
# 1.109.0
|
||||
|
||||
### What changed?
|
||||
|
||||
@@ -20,6 +20,7 @@ describe('DeprecationService', () => {
|
||||
// this test suite.
|
||||
process.env = {
|
||||
N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false',
|
||||
N8N_GIT_NODE_DISABLE_BARE_REPOS: 'false',
|
||||
};
|
||||
|
||||
jest.resetAllMocks();
|
||||
@@ -140,6 +141,7 @@ describe('DeprecationService', () => {
|
||||
process.env = {
|
||||
N8N_RUNNERS_ENABLED: 'true',
|
||||
N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false',
|
||||
N8N_GIT_NODE_DISABLE_BARE_REPOS: 'false',
|
||||
};
|
||||
|
||||
jest.spyOn(config, 'getEnv').mockImplementation((key) => {
|
||||
@@ -239,6 +241,7 @@ describe('DeprecationService', () => {
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
N8N_RUNNERS_ENABLED: 'true',
|
||||
N8N_GIT_NODE_DISABLE_BARE_REPOS: 'false',
|
||||
};
|
||||
|
||||
jest.resetAllMocks();
|
||||
@@ -259,4 +262,29 @@ describe('DeprecationService', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('N8N_GIT_NODE_DISABLE_BARE_REPOS', () => {
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
N8N_RUNNERS_ENABLED: 'true',
|
||||
N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false',
|
||||
};
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
test('should warn when N8N_GIT_NODE_DISABLE_BARE_REPOS is not set', () => {
|
||||
delete process.env.N8N_GIT_NODE_DISABLE_BARE_REPOS;
|
||||
deprecationService.warn();
|
||||
expect(logger.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test.each(['false', 'true'])(
|
||||
'should not warn when N8N_GIT_NODE_DISABLE_BARE_REPOS is %s',
|
||||
(value) => {
|
||||
process.env.N8N_GIT_NODE_DISABLE_BARE_REPOS = value;
|
||||
deprecationService.warn();
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -109,6 +109,12 @@ export class DeprecationService {
|
||||
'The default value of N8N_BLOCK_ENV_ACCESS_IN_NODE will be changed from false to true in a future version. If you need to access environment variables from the Code Node or from expressions, please set N8N_BLOCK_ENV_ACCESS_IN_NODE=false. Learn more: https://docs.n8n.io/hosting/configuration/environment-variables/security/',
|
||||
checkValue: (value: string | undefined) => value === undefined || value === '',
|
||||
},
|
||||
{
|
||||
envVar: 'N8N_GIT_NODE_DISABLE_BARE_REPOS',
|
||||
message:
|
||||
'Support for bare repositories in the Git Node will be removed in a future version due to security concerns. If you are not using bare repositories in the Git Node, please set N8N_GIT_NODE_DISABLE_BARE_REPOS=true. Learn more: https://docs.n8n.io/hosting/configuration/environment-variables/security/',
|
||||
checkValue: (value: string | undefined) => value === undefined || value === '',
|
||||
},
|
||||
];
|
||||
|
||||
/** Runtime state of deprecation-related env vars. */
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
switchBranchFields,
|
||||
tagFields,
|
||||
} from './descriptions';
|
||||
import { Container } from '@n8n/di';
|
||||
import { DeploymentConfig, SecurityConfig } from '@n8n/config';
|
||||
|
||||
export class Git implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
@@ -291,8 +293,18 @@ export class Git implements INodeType {
|
||||
}
|
||||
}
|
||||
|
||||
const gitConfig: string[] = [];
|
||||
const deploymentConfig = Container.get(DeploymentConfig);
|
||||
const isCloud = deploymentConfig.type === 'cloud';
|
||||
const securityConfig = Container.get(SecurityConfig);
|
||||
const disableBareRepos = securityConfig.disableBareRepos;
|
||||
if (isCloud || disableBareRepos) {
|
||||
gitConfig.push('safe.bareRepository=explicit');
|
||||
}
|
||||
|
||||
const gitOptions: Partial<SimpleGitOptions> = {
|
||||
baseDir: repositoryPath,
|
||||
config: gitConfig,
|
||||
};
|
||||
|
||||
const git: SimpleGit = simpleGit(gitOptions)
|
||||
|
||||
117
packages/nodes-base/nodes/Git/__test__/Git.node.test.ts
Normal file
117
packages/nodes-base/nodes/Git/__test__/Git.node.test.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { DeploymentConfig, SecurityConfig } from '@n8n/config';
|
||||
import { Container } from '@n8n/di';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import type { SimpleGit } from 'simple-git';
|
||||
import simpleGit from 'simple-git';
|
||||
|
||||
import { Git } from '../Git.node';
|
||||
|
||||
const mockGit = {
|
||||
log: jest.fn(),
|
||||
env: jest.fn().mockReturnThis(),
|
||||
};
|
||||
|
||||
jest.mock('simple-git');
|
||||
const mockSimpleGit = simpleGit as jest.MockedFunction<typeof simpleGit>;
|
||||
mockSimpleGit.mockReturnValue(mockGit as unknown as SimpleGit);
|
||||
|
||||
describe('Git Node', () => {
|
||||
let gitNode: Git;
|
||||
let executeFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let deploymentConfig: jest.Mocked<DeploymentConfig>;
|
||||
let securityConfig: jest.Mocked<SecurityConfig>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
deploymentConfig = mock<DeploymentConfig>({
|
||||
type: 'default',
|
||||
});
|
||||
securityConfig = mock<SecurityConfig>({
|
||||
disableBareRepos: false,
|
||||
});
|
||||
Container.set(DeploymentConfig, deploymentConfig);
|
||||
Container.set(SecurityConfig, securityConfig);
|
||||
|
||||
executeFunctions = mock<IExecuteFunctions>({
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
getNodeParameter: jest.fn(),
|
||||
helpers: {
|
||||
returnJsonArray: jest
|
||||
.fn()
|
||||
.mockImplementation((data: unknown[]) => data.map((item: unknown) => ({ json: item }))),
|
||||
},
|
||||
});
|
||||
executeFunctions.getNodeParameter.mockImplementation((name: string) => {
|
||||
switch (name) {
|
||||
case 'operation':
|
||||
return 'log';
|
||||
case 'repositoryPath':
|
||||
return '/tmp/test-repo';
|
||||
case 'options':
|
||||
return {};
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
mockGit.log.mockResolvedValue({ all: [] });
|
||||
|
||||
gitNode = new Git();
|
||||
});
|
||||
|
||||
describe('Bare Repository Configuration', () => {
|
||||
it('should add safe.bareRepository=explicit when deployment type is cloud', async () => {
|
||||
deploymentConfig.type = 'cloud';
|
||||
securityConfig.disableBareRepos = false;
|
||||
|
||||
await gitNode.execute.call(executeFunctions);
|
||||
|
||||
expect(mockSimpleGit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: ['safe.bareRepository=explicit'],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should add safe.bareRepository=explicit when disableBareRepos is true', async () => {
|
||||
deploymentConfig.type = 'default';
|
||||
securityConfig.disableBareRepos = true;
|
||||
|
||||
await gitNode.execute.call(executeFunctions);
|
||||
|
||||
expect(mockSimpleGit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: ['safe.bareRepository=explicit'],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should add safe.bareRepository=explicit when both cloud and disableBareRepos are true', async () => {
|
||||
deploymentConfig.type = 'cloud';
|
||||
securityConfig.disableBareRepos = true;
|
||||
|
||||
await gitNode.execute.call(executeFunctions);
|
||||
|
||||
expect(mockSimpleGit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: ['safe.bareRepository=explicit'],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not add safe.bareRepository=explicit when neither cloud nor disableBareRepos is true', async () => {
|
||||
deploymentConfig.type = 'default';
|
||||
securityConfig.disableBareRepos = false;
|
||||
|
||||
await gitNode.execute.call(executeFunctions);
|
||||
|
||||
expect(mockSimpleGit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: [],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user