Skip to content

Commit

Permalink
fix(audit): audit should work with a proxy
Browse files Browse the repository at this point in the history
close #3755
  • Loading branch information
zkochan committed Dec 1, 2021
1 parent 4c175e5 commit c111227
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/brown-spies-stare.md
@@ -0,0 +1,5 @@
---
"@pnpm/audit": minor
---

Added options for the HTTP agent.
5 changes: 5 additions & 0 deletions .changeset/giant-toes-notice.md
@@ -0,0 +1,5 @@
---
"@pnpm/fetch": minor
---

Add `fetchWithAgent()`.
5 changes: 5 additions & 0 deletions .changeset/hungry-dragons-wait.md
@@ -0,0 +1,5 @@
---
"pnpm": patch
---

`pnpm audit` should work when a proxy is configured for the registry [#3755](https://github.com/pnpm/pnpm/issues/3755).
6 changes: 4 additions & 2 deletions packages/audit/src/index.ts
@@ -1,5 +1,5 @@
import PnpmError from '@pnpm/error'
import fetch, { RetryTimeoutOptions } from '@pnpm/fetch'
import { AgentOptions, fetchWithAgent, RetryTimeoutOptions } from '@pnpm/fetch'
import { Lockfile } from '@pnpm/lockfile-types'
import { DependenciesField } from '@pnpm/types'
import lockfileToAuditTree from './lockfileToAuditTree'
Expand All @@ -10,6 +10,7 @@ export * from './types'
export default async function audit (
lockfile: Lockfile,
opts: {
agentOptions?: AgentOptions
include?: { [dependenciesField in DependenciesField]: boolean }
registry: string
retry?: RetryTimeoutOptions
Expand All @@ -19,7 +20,8 @@ export default async function audit (
const auditTree = lockfileToAuditTree(lockfile, { include: opts.include })
const registry = opts.registry.endsWith('/') ? opts.registry : `${opts.registry}/`
const auditUrl = `${registry}-/npm/v1/security/audits`
const res = await fetch(auditUrl, {
const res = await fetchWithAgent(auditUrl, {
agentOptions: opts.agentOptions ?? {},
body: JSON.stringify(auditTree),
headers: { 'Content-Type': 'application/json' },
method: 'post',
Expand Down
28 changes: 22 additions & 6 deletions packages/fetch/src/fetchFromRegistry.ts
@@ -1,14 +1,31 @@
import { URL } from 'url'
import { FetchFromRegistry } from '@pnpm/fetching-types'
import npmRegistryAgent, { AgentOptions } from '@pnpm/npm-registry-agent'
import fetch, { isRedirect, Response } from './fetch'
import fetch, { isRedirect, Response, RequestInfo, RequestInit } from './fetch'

const USER_AGENT = 'pnpm' // or maybe make it `${pkg.name}/${pkg.version} (+https://npm.im/${pkg.name})`

const CORGI_DOC = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
const JSON_DOC = 'application/json'
const MAX_FOLLOWED_REDIRECTS = 20

export type FetchWithAgentOptions = RequestInit & {
agentOptions: AgentOptions
}

export function fetchWithAgent (url: RequestInfo, opts: FetchWithAgentOptions) {
const agent = npmRegistryAgent(url.toString(), {
...opts.agentOptions,
strictSsl: opts.agentOptions.strictSsl ?? true,
} as any) // eslint-disable-line
const headers = opts.headers ?? {}
headers['connection'] = agent ? 'keep-alive' : 'close'
return fetch(url, {
...opts,
agent,
})
}

export { AgentOptions }

export default function (
Expand All @@ -31,17 +48,16 @@ export default function (
let urlObject = new URL(url)
const originalHost = urlObject.host
while (true) {
const agent = npmRegistryAgent(urlObject.href, {
const agentOptions = {
...defaultOpts,
...opts,
strictSsl: defaultOpts.strictSsl ?? true,
} as any) // eslint-disable-line
headers['connection'] = agent ? 'keep-alive' : 'close'
} as any // eslint-disable-line

// We should pass a URL object to node-fetch till this is not resolved:
// https://github.com/bitinn/node-fetch/issues/245
const response = await fetch(urlObject, {
agent,
const response = await fetchWithAgent(urlObject, {
agentOptions,
// if verifying integrity, node-fetch must not decompress
compress: opts?.compress ?? false,
headers,
Expand Down
3 changes: 2 additions & 1 deletion packages/fetch/src/index.ts
@@ -1,11 +1,12 @@
import { FetchFromRegistry } from '@pnpm/fetching-types'
import fetch, { RetryTimeoutOptions } from './fetch'
import createFetchFromRegistry, { AgentOptions } from './fetchFromRegistry'
import createFetchFromRegistry, { fetchWithAgent, AgentOptions } from './fetchFromRegistry'

export default fetch
export {
AgentOptions,
createFetchFromRegistry,
fetchWithAgent,
FetchFromRegistry,
RetryTimeoutOptions,
}
32 changes: 31 additions & 1 deletion packages/plugin-commands-audit/src/audit.ts
Expand Up @@ -106,7 +106,25 @@ export async function handler (
json?: boolean
lockfileDir?: string
registries: Registries
} & Pick<Config, 'fetchRetries' | 'fetchRetryMaxtimeout' | 'fetchRetryMintimeout' | 'fetchRetryFactor' | 'fetchTimeout' | 'production' | 'dev' | 'optional'>
} & Pick<Config, 'ca'
| 'cert'
| 'httpProxy'
| 'httpsProxy'
| 'key'
| 'localAddress'
| 'maxSockets'
| 'noProxy'
| 'strictSsl'
| 'fetchTimeout'
| 'fetchRetries'
| 'fetchRetryMaxtimeout'
| 'fetchRetryMintimeout'
| 'fetchRetryFactor'
| 'fetchTimeout'
| 'production'
| 'dev'
| 'optional'
>
) {
const lockfile = await readWantedLockfile(opts.lockfileDir ?? opts.dir, { ignoreIncompatible: true })
if (lockfile == null) {
Expand All @@ -120,6 +138,18 @@ export async function handler (
let auditReport!: AuditReport
try {
auditReport = await audit(lockfile, {
agentOptions: {
ca: opts.ca,
cert: opts.cert,
httpProxy: opts.httpProxy,
httpsProxy: opts.httpsProxy,
key: opts.key,
localAddress: opts.localAddress,
maxSockets: opts.maxSockets,
noProxy: opts.noProxy,
strictSsl: opts.strictSsl,
timeout: opts.fetchTimeout,
},
include,
registry: opts.registries.default,
retry: {
Expand Down

0 comments on commit c111227

Please sign in to comment.