Skip to content

Commit

Permalink
fix: use token param for upstream proxy auth
Browse files Browse the repository at this point in the history
  • Loading branch information
SUNx2YCH authored and drazisil-codecov committed Jun 20, 2023
1 parent acf4d83 commit b448744
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 81 deletions.
41 changes: 30 additions & 11 deletions src/helpers/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
import { ProxyAgent } from 'undici';
import { UploaderArgs, UploaderEnvs } from '../types.js';
import { IncomingHttpHeaders } from 'undici/types/header.js';
import { logError } from './logger'

export function addProxyIfNeeded(envs: UploaderEnvs, args: UploaderArgs): ProxyAgent | undefined {
return args.upstream ? new ProxyAgent({ uri: args.upstream }) : undefined;
export function getBasicAuthToken(username: string, password: string): string {
const authString = Buffer.from(`${username}:${password}`).toString('base64')
return `Basic ${authString}`
}

export function removeUrlAuth(url: string | URL): string {
const noAuthUrl = new URL(url)
noAuthUrl.username = ''
noAuthUrl.password = ''
return noAuthUrl.href
}

export function addProxyHeaders(envs: UploaderEnvs, initialHeaders: IncomingHttpHeaders): IncomingHttpHeaders {
let headers: IncomingHttpHeaders
if (envs['PROXY_BASIC_USER'] && envs['PROXY_BASIC_PASS']) {
const authString = Buffer.from(`${envs['PROXY_BASIC_USER']}:${envs['PROXY_BASIC_PASS']}`).toString("base64")
headers = { ...initialHeaders, Authorization: `Basic ${authString}` }
} else {
headers = initialHeaders
export function addProxyIfNeeded(envs: UploaderEnvs, args: UploaderArgs): ProxyAgent | undefined {
if (!args.upstream) {
return undefined
}
return headers

// https://github.com/nodejs/undici/blob/main/docs/api/ProxyAgent.md#example---basic-proxy-request-with-authentication
try {
const proxyUrl = new URL(args.upstream)
if (proxyUrl.username && proxyUrl.password) {
return new ProxyAgent({
uri: removeUrlAuth(proxyUrl),
token: getBasicAuthToken(proxyUrl.username, proxyUrl.password),
})
}
return new ProxyAgent({ uri: args.upstream })
} catch (err) {
logError(`Couldn't set upstream proxy: ${err}`)
}

return undefined
}
11 changes: 3 additions & 8 deletions src/helpers/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {
UploaderInputs,
} from '../types'
import { info } from './logger'
import { addProxyHeaders, addProxyIfNeeded } from './proxy'
import { IncomingHttpHeaders } from 'undici/types/header.js'
import { addProxyIfNeeded } from './proxy'

/**
*
Expand Down Expand Up @@ -175,13 +174,11 @@ export function generateRequestHeadersPOST(
source,
)}&token=${token}&${query}`, postURL)

const initialHeaders = {
const headers = {
'X-Upload-Token': token,
'X-Reduced-Redundancy': 'false',
}

const headers: IncomingHttpHeaders = addProxyHeaders(envs, initialHeaders)

return {
agent: addProxyIfNeeded(envs, args),
url: url,
Expand All @@ -202,12 +199,10 @@ export function generateRequestHeadersPUT(
envs: UploaderEnvs,
args: UploaderArgs,
): IRequestHeaders {
const initialHeaders = {
const headers = {
'Content-Type': 'text/plain',
'Content-Encoding': 'gzip',
}

const headers = addProxyHeaders(envs, initialHeaders)

return {
agent: addProxyIfNeeded(envs, args),
Expand Down
64 changes: 64 additions & 0 deletions test/helpers/proxy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ProxyAgent } from 'undici'
import { addProxyIfNeeded, getBasicAuthToken, removeUrlAuth } from '../../src/helpers/proxy'
import { UploaderArgs } from '../../src/types'

describe('Proxy - getBasicAuthToken', () => {
it('should make base64-encoded auth token from username and password', () => {
const token = getBasicAuthToken('alice', 'pa$$w0rd')
expect(token).toEqual('Basic YWxpY2U6cGEkJHcwcmQ=')
})
})

describe('Proxy - removeUrlAuth', () => {
it('should remove auth data from url', () => {
const proxyUrl = 'http://alice:[email protected]:1234/'
expect(removeUrlAuth(proxyUrl)).toEqual('http://proxy.local:1234/')
})
})

// copied from undici sources, as not all of them are in module exports
const kProxy = Symbol('proxy agent options')
const kProxyHeaders = Symbol('proxy headers')
const proxyAuthHeader = 'proxy-authorization'

// helper to get ProxyAgent property indexed with given Symbol
function getAgentProperty(agent: ProxyAgent, property: Symbol): any {
const originalSymbol = Reflect.ownKeys(agent).filter((item) => item.toString() === property.toString())?.[0]
return originalSymbol ? Reflect.get(agent, originalSymbol) : undefined
}

describe('Proxy - addProxyIfNeeded', () => {
it.each([
[''],
['invalid'],
])('should not return proxy if upstream argument is not set or invalid (%p)', (proxyUrl: string) => {
const args: UploaderArgs = {
flags: [],
slug: '',
upstream: proxyUrl,
}

const proxy = addProxyIfNeeded({}, args)
expect(proxy).toBeUndefined()
})

it.each([
['http://proxy.local:1234/', 'http://proxy.local:1234/', undefined],
['http://alice:[email protected]:1234/', 'http://proxy.local:1234/', 'Basic YWxpY2U6cGEkJHcwcmQ='],
])('should return proxy if upstream argument is set (%p)', (proxyUrl: string, expectedUri: string, expectedAuthHeader: any) => {
const args: UploaderArgs = {
flags: [],
slug: '',
upstream: proxyUrl,
}

const proxy = addProxyIfNeeded({}, args)
expect(proxy).toBeDefined()

const agentOptions = getAgentProperty(proxy as ProxyAgent, kProxy)
expect(agentOptions?.['uri']).toEqual(expectedUri)

const agentHeaders = getAgentProperty(proxy as ProxyAgent, kProxyHeaders)
expect(agentHeaders?.[proxyAuthHeader]).toEqual(expectedAuthHeader)
})
})
62 changes: 0 additions & 62 deletions test/proxy.test.ts

This file was deleted.

0 comments on commit b448744

Please sign in to comment.