Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions packages/bruno-cli/src/runner/run-single-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -604,9 +604,6 @@ const runSingleRequest = async function (
}

request.oauth2CredentialVariables = getFormattedOauth2Credentials();

// Remove oauth2 config from request to prevent it from being sent
delete request.oauth2;
}

let response, responseTime;
Expand All @@ -631,7 +628,6 @@ const runSingleRequest = async function (

if (request.ntlmConfig) {
axiosInstance = NtlmClient(request.ntlmConfig, axiosInstance.defaults);
delete request.ntlmConfig;
}

if (request.oauth1config) {
Expand All @@ -657,12 +653,10 @@ const runSingleRequest = async function (

request.awsv4config = await resolveAwsV4Credentials(request);
addAwsV4Interceptor(axiosInstance, request);
delete request.awsv4config;
}

if (request.digestConfig) {
addDigestInterceptor(axiosInstance, request);
delete request.digestConfig;
}

/** @type {import('axios').AxiosResponse} */
Expand Down
13 changes: 13 additions & 0 deletions packages/bruno-cli/src/utils/axios-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ const createRedirectConfig = (error, redirectUrl) => {
return requestConfig;
};

const deleteAuthConfig = (config) => {
// Keep auth configs on the runtime request for scripts, but don't pass them to transport.
delete config.ntlmConfig;
delete config.awsv4config;
delete config.digestConfig;
delete config.oauth1config;
delete config.oauth2;
delete config.apiKeyHeaderName;
delete config.apiKeyAuthValueForQueryParams;
};

/**
* Function that configures axios with timing interceptors
* Important to note here that the timings are not completely accurate.
Expand Down Expand Up @@ -98,6 +109,8 @@ function makeAxiosInstance({
};

instance.interceptors.request.use((config) => {
deleteAuthConfig(config);

config.headers['request-start-time'] = Date.now();

/**
Expand Down
66 changes: 66 additions & 0 deletions packages/bruno-cli/tests/utils/axios-instance.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
jest.mock('../../src/constants', () => ({
CLI_VERSION: '1.0.0'
}));

jest.mock('../../src/utils/cookies', () => ({
addCookieToJar: jest.fn(),
getCookieStringForUrl: jest.fn()
}));

jest.mock('../../src/utils/form-data', () => ({
createFormData: jest.fn()
}));

jest.mock('../../src/utils/proxy-util', () => ({
setupProxyAgents: jest.fn()
}));

const { makeAxiosInstance } = require('../../src/utils/axios-instance');

function createStubAdapter() {
let capturedConfig = null;

const adapter = (config) => {
capturedConfig = config;
return Promise.resolve({
data: {},
status: 200,
statusText: 'OK',
headers: {},
config
});
};

adapter.getConfig = () => capturedConfig;

return adapter;
}

describe('axios-instance: auth config cleanup', () => {
test('should remove auth config before request reaches the axios adapter', async () => {
const axiosInstance = makeAxiosInstance();
const stubAdapter = createStubAdapter();

await axiosInstance({
url: 'https://api.example.com/test',
method: 'get',
adapter: stubAdapter,
ntlmConfig: { username: 'user', password: 'pass' },
awsv4config: { accessKeyId: 'access-key', secretAccessKey: 'secret-key' },
digestConfig: { username: 'digest-user', password: 'digest-pass' },
oauth1config: { consumerSecret: 'consumer-secret' },
oauth2: { clientSecret: 'client-secret' },
apiKeyHeaderName: 'x-api-key',
apiKeyAuthValueForQueryParams: 'api-key-value'
});

const config = stubAdapter.getConfig();
expect(config.ntlmConfig).toBeUndefined();
expect(config.awsv4config).toBeUndefined();
expect(config.digestConfig).toBeUndefined();
expect(config.oauth1config).toBeUndefined();
expect(config.oauth2).toBeUndefined();
expect(config.apiKeyHeaderName).toBeUndefined();
expect(config.apiKeyAuthValueForQueryParams).toBeUndefined();
});
});
13 changes: 13 additions & 0 deletions packages/bruno-electron/src/ipc/network/axios-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ const checkConnection = (host, port) =>
}
});

const deleteAuthConfig = (config) => {
// Keep auth configs on the runtime request for scripts, but don't pass them to transport.
delete config.ntlmConfig;
delete config.awsv4config;
delete config.digestConfig;
delete config.oauth1config;
delete config.oauth2;
delete config.apiKeyHeaderName;
delete config.apiKeyAuthValueForQueryParams;
};

/**
* Function that configures axios with timing interceptors
* Important to note here that the timings are not completely accurate.
Expand Down Expand Up @@ -105,6 +116,8 @@ function makeAxiosInstance({
};

instance.interceptors.request.use(async (config) => {
deleteAuthConfig(config);

const url = URL.parse(config.url);
config.metadata = config.metadata || {};
config.metadata.startTime = new Date().getTime();
Expand Down
2 changes: 0 additions & 2 deletions packages/bruno-electron/src/ipc/network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ const configureRequest = async (

if (request.ntlmConfig) {
axiosInstance = NtlmClient(request.ntlmConfig, axiosInstance.defaults);
delete request.ntlmConfig;
}

if (request.oauth1config) {
Expand Down Expand Up @@ -301,7 +300,6 @@ const configureRequest = async (
if (request.awsv4config) {
request.awsv4config = await resolveAwsV4Credentials(request);
addAwsV4Interceptor(axiosInstance, request);
delete request.awsv4config;
}

if (request.digestConfig) {
Expand Down
29 changes: 29 additions & 0 deletions packages/bruno-electron/tests/network/axios-instance.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,32 @@ describe('axios-instance: DNS lookup behavior (GitHub #7343)', () => {
expect(config.lookup).not.toBe(inheritedLookup);
});
});

describe('axios-instance: auth config cleanup', () => {
test('should remove auth config before request reaches the axios adapter', async () => {
const axiosInstance = makeAxiosInstance();
const stubAdapter = createStubAdapter();

await axiosInstance({
url: 'https://api.example.com/test',
method: 'get',
adapter: stubAdapter,
ntlmConfig: { username: 'user', password: 'pass' },
awsv4config: { accessKeyId: 'access-key', secretAccessKey: 'secret-key' },
digestConfig: { username: 'digest-user', password: 'digest-pass' },
oauth1config: { consumerSecret: 'consumer-secret' },
oauth2: { clientSecret: 'client-secret' },
apiKeyHeaderName: 'x-api-key',
apiKeyAuthValueForQueryParams: 'api-key-value'
});

const config = stubAdapter.getConfig();
expect(config.ntlmConfig).toBeUndefined();
expect(config.awsv4config).toBeUndefined();
expect(config.digestConfig).toBeUndefined();
expect(config.oauth1config).toBeUndefined();
expect(config.oauth2).toBeUndefined();
expect(config.apiKeyHeaderName).toBeUndefined();
expect(config.apiKeyAuthValueForQueryParams).toBeUndefined();
});
});
6 changes: 4 additions & 2 deletions packages/bruno-js/src/bruno-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,18 @@ class BrunoRequest {
return 'oauth1';
} else if (this.headers?.['Authorization']?.startsWith('Bearer')) {
return 'bearer';
} else if (this.headers?.['Authorization']?.startsWith('Basic') || this.req?.auth?.username) {
} else if (this.headers?.['Authorization']?.startsWith('Basic') || this.req?.basicAuth) {
return 'basic';
} else if (this.req?.apiKeyAuthValueForQueryParams) {
return 'apikey';
} else if (this.req?.apiKeyHeaderName && this.headers?.[this.req.apiKeyHeaderName] !== undefined) {
return 'apikey';
} else if (this.req?.awsv4) {
} else if (this.req?.awsv4config) {
return 'awsv4';
} else if (this.req?.digestConfig) {
return 'digest';
} else if (this.req?.ntlmConfig) {
return 'ntlm';
} else if (this.headers?.['X-WSSE'] || this.req?.auth?.username) {
return 'wsse';
} else {
Expand Down
110 changes: 110 additions & 0 deletions packages/bruno-js/tests/bruno-request-auth-mode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,43 @@ const makeReq = (overrides = {}) => ({
});

describe('BrunoRequest - getAuthMode()', () => {
it('returns oauth2 when OAuth2 config is present', () => {
const req = new BrunoRequest(
makeReq({
oauth2: {
access_token: 'access-token'
}
})
);

expect(req.getAuthMode()).toBe('oauth2');
});

it('returns oauth1 when OAuth1 config is present', () => {
const req = new BrunoRequest(
makeReq({
oauth1config: {
consumerKey: 'consumer-key',
consumerSecret: 'consumer-secret'
}
})
);

expect(req.getAuthMode()).toBe('oauth1');
});

it('returns bearer when bearer authorization header is present', () => {
const req = new BrunoRequest(
makeReq({
headers: {
Authorization: 'Bearer token'
}
})
);

expect(req.getAuthMode()).toBe('bearer');
});

it('returns apikey for header placement when the api key header is present', () => {
const req = new BrunoRequest(
makeReq({
Expand Down Expand Up @@ -50,4 +87,77 @@ describe('BrunoRequest - getAuthMode()', () => {

expect(req.getAuthMode()).toBe('apikey');
});

it('returns awsv4 when AWS SigV4 config is present', () => {
const req = new BrunoRequest(
makeReq({
awsv4config: {
accessKeyId: 'access-key',
secretAccessKey: 'secret-key',
service: 'execute-api',
region: 'us-east-1'
}
})
);

expect(req.getAuthMode()).toBe('awsv4');
});

it('returns digest when Digest config is present', () => {
const req = new BrunoRequest(
makeReq({
digestConfig: {
username: 'user',
password: 'password'
}
})
);

expect(req.getAuthMode()).toBe('digest');
});

it('returns basic when basic auth config is present before interpolation', () => {
const req = new BrunoRequest(
makeReq({
basicAuth: {
username: 'user',
password: 'password'
}
})
);

expect(req.getAuthMode()).toBe('basic');
});

it('returns ntlm when NTLM config is present', () => {
const req = new BrunoRequest(
makeReq({
ntlmConfig: {
username: 'user',
password: 'password',
domain: 'domain'
}
})
);

expect(req.getAuthMode()).toBe('ntlm');
});

it('returns wsse when WSSE auth header is present', () => {
const req = new BrunoRequest(
makeReq({
headers: {
'X-WSSE': 'UsernameToken Username="user"'
}
})
);

expect(req.getAuthMode()).toBe('wsse');
});

it('returns none when no auth config is present', () => {
const req = new BrunoRequest(makeReq());

expect(req.getAuthMode()).toBe('none');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,6 @@ export function applyOAuth1ToRequest(request: {
version, realm, placement, includeBodyHash
} = request.oauth1config;

// Clear credentials from the request object before any operation that could throw
delete (request as any).oauth1config;

// Resolve private key: read from file if privateKeyType is 'file', otherwise use as-is
let resolvedPrivateKey: string | undefined;
if (privateKey) {
Expand Down
20 changes: 0 additions & 20 deletions tests/auth/apikey/apikey-runner.spec.ts

This file was deleted.

Loading
Loading