pax_global_header00006660000000000000000000000064152521577760014533gustar00rootroot0000000000000052 comment=a11ad82545698af5c33e59f3ed0b52eab79bf610 jshttp-proxy-addr-a11ad82/000077500000000000000000000000001525215777600155415ustar00rootroot00000000000000jshttp-proxy-addr-a11ad82/.eslintignore000066400000000000000000000000421525215777600202400ustar00rootroot00000000000000.nyc_output coverage node_modules jshttp-proxy-addr-a11ad82/.eslintrc.yml000066400000000000000000000002321525215777600201620ustar00rootroot00000000000000root: true extends: - standard - plugin:markdown/recommended plugins: - markdown overrides: - files: '**/*.md' processor: 'markdown/markdown' jshttp-proxy-addr-a11ad82/.github/000077500000000000000000000000001525215777600171015ustar00rootroot00000000000000jshttp-proxy-addr-a11ad82/.github/dependabot.yml000066400000000000000000000005661525215777600217400ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: monthly - package-ecosystem: npm directory: / schedule: interval: monthly time: "23:00" timezone: Europe/London open-pull-requests-limit: 10 ignore: - dependency-name: "*" update-types: ["version-update:semver-major"] jshttp-proxy-addr-a11ad82/.github/workflows/000077500000000000000000000000001525215777600211365ustar00rootroot00000000000000jshttp-proxy-addr-a11ad82/.github/workflows/ci.yml000066400000000000000000000162401525215777600222570ustar00rootroot00000000000000name: ci on: - pull_request - push permissions: contents: read jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: name: - Node.js 0.10 - Node.js 0.12 - io.js 1.x - io.js 2.x - io.js 3.x - Node.js 4.x - Node.js 5.x - Node.js 6.x - Node.js 7.x - Node.js 8.x - Node.js 9.x - Node.js 10.x - Node.js 11.x - Node.js 12.x - Node.js 13.x - Node.js 14.x - Node.js 15.x - Node.js 16.x - Node.js 17.x - Node.js 18.x - Node.js 19.x - Node.js 20.x - Node.js 21.x - Node.js 22.x - Node.js 23.x - Node.js 24.x - Node.js 25.x - Node.js 26.x include: - name: Node.js 0.10 node-version: "0.10" npm-i: mocha@3.5.3 nyc@10.3.2 npm-rm: beautify-benchmark benchmark - name: Node.js 0.12 node-version: "0.12" npm-i: mocha@3.5.3 nyc@10.3.2 npm-rm: beautify-benchmark benchmark - name: io.js 1.x node-version: "1" npm-i: mocha@3.5.3 nyc@10.3.2 npm-rm: beautify-benchmark benchmark - name: io.js 2.x node-version: "2" npm-i: mocha@3.5.3 nyc@10.3.2 npm-rm: beautify-benchmark benchmark - name: io.js 3.x node-version: "3" npm-i: mocha@3.5.3 nyc@10.3.2 npm-rm: beautify-benchmark benchmark - name: Node.js 4.x node-version: "4" npm-i: mocha@5.2.0 nyc@11.9.0 npm-rm: beautify-benchmark benchmark - name: Node.js 5.x node-version: "5" npm-i: mocha@5.2.0 nyc@11.9.0 npm-rm: beautify-benchmark benchmark - name: Node.js 6.x node-version: "6" npm-i: mocha@6.2.2 nyc@14.1.1 npm-rm: beautify-benchmark benchmark - name: Node.js 7.x node-version: "7" npm-i: mocha@6.2.2 nyc@14.1.1 npm-rm: beautify-benchmark benchmark - name: Node.js 8.x node-version: "8" npm-i: mocha@7.2.0 nyc@14.1.1 npm-rm: beautify-benchmark benchmark - name: Node.js 9.x node-version: "9" npm-i: mocha@7.2.0 nyc@14.1.1 npm-rm: beautify-benchmark benchmark - name: Node.js 10.x node-version: "10" npm-rm: beautify-benchmark benchmark - name: Node.js 11.x node-version: "11" npm-rm: beautify-benchmark benchmark - name: Node.js 12.x node-version: "12" npm-rm: beautify-benchmark benchmark - name: Node.js 13.x node-version: "13" npm-rm: beautify-benchmark benchmark - name: Node.js 14.x node-version: "14" npm-rm: beautify-benchmark benchmark - name: Node.js 15.x node-version: "15" npm-rm: beautify-benchmark benchmark - name: Node.js 16.x node-version: "16" npm-rm: beautify-benchmark benchmark - name: Node.js 17.x node-version: "17" npm-rm: beautify-benchmark benchmark - name: Node.js 18.x node-version: "18" npm-rm: beautify-benchmark benchmark - name: Node.js 19.x node-version: "19" npm-rm: beautify-benchmark benchmark - name: Node.js 20.x node-version: "20" npm-rm: beautify-benchmark benchmark - name: Node.js 21.x node-version: "21" npm-rm: beautify-benchmark benchmark - name: Node.js 22.x node-version: "22" npm-rm: beautify-benchmark benchmark - name: Node.js 23.x node-version: "23" npm-rm: beautify-benchmark benchmark - name: Node.js 24.x node-version: "24" npm-rm: beautify-benchmark benchmark - name: Node.js 25.x node-version: "25" npm-rm: beautify-benchmark benchmark - name: Node.js 26.x node-version: "26" npm-rm: beautify-benchmark benchmark steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Node.js ${{ matrix.node-version }} shell: bash -eo pipefail -l {0} run: | nvm install --default ${{ matrix.node-version }} if [[ "${{ matrix.node-version }}" == 0.* ]]; then npm config set strict-ssl false fi dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - name: Configure npm run: | if [[ "$(npm config get package-lock)" == "true" ]]; then npm config set package-lock false else npm config set shrinkwrap false fi - name: Remove npm module(s) ${{ matrix.npm-rm }} run: npm rm --silent --save-dev ${{ matrix.npm-rm }} if: matrix.npm-rm != '' - name: Install npm module(s) ${{ matrix.npm-i }} run: npm install --save-dev ${{ matrix.npm-i }} if: matrix.npm-i != '' - name: Setup Node.js version-specific dependencies shell: bash run: | # eslint for linting # - remove on Node.js < 10 if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ grep -E '^eslint(-|$)' | \ sort -r | \ xargs -n1 npm rm --silent --save-dev fi - name: Install Node.js dependencies run: npm install - name: List environment id: list_env shell: bash run: | echo "node@$(node -v)" echo "npm@$(npm -v)" npm -s ls ||: (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" - name: Run tests shell: bash run: | if npm -ps ls nyc | grep -q nyc; then npm run test-ci else npm test fi - name: Lint code if: steps.list_env.outputs.eslint != '' run: npm run lint - name: Upload code coverage uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: steps.list_env.outputs.nyc != '' with: name: coverage-node-${{ matrix.node-version }} path: ./coverage/lcov.info retention-days: 1 coverage: needs: test runs-on: ubuntu-latest permissions: contents: read checks: write # for coverallsapp/github-action to create new checks steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install lcov shell: bash run: sudo apt-get -y install lcov - name: Collect coverage reports uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: ./coverage pattern: coverage-node-* - name: Merge coverage reports shell: bash run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info - name: Upload coverage report uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8 with: file: ./lcov.info jshttp-proxy-addr-a11ad82/.github/workflows/codeql.yml000066400000000000000000000052501525215777600231320ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: ["master"] pull_request: # The branches below must be a subset of the branches above branches: ["master"] schedule: - cron: "0 0 * * 1" permissions: contents: read jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: ["javascript"] # CodeQL supports [ $supported-codeql-languages ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5 with: category: "/language:${{matrix.language}}" jshttp-proxy-addr-a11ad82/.github/workflows/npm-publish.yml000066400000000000000000000015401525215777600241170ustar00rootroot00000000000000name: Publish package to npm on: release: types: [published] concurrency: group: "${{ github.workflow }} ✨ ${{ github.ref }}" cancel-in-progress: false permissions: contents: read jobs: publish: name: Publish to npm runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "lts/*" registry-url: "https://registry.npmjs.org" # npm stage publish requires npm >= 11.15.0 - name: Upgrade npm run: npm install -g npm@latest - name: Stage publish to npm run: npm stage publish jshttp-proxy-addr-a11ad82/.github/workflows/scorecard.yml000066400000000000000000000056441525215777600236370ustar00rootroot00000000000000# This workflow uses actions that are not certified by GitHub. They are provided # by a third-party and are governed by separate terms of service, privacy # policy, and support documentation. name: Scorecard supply-chain security on: # For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection branch_protection_rule: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: - cron: '16 21 * * 1' push: branches: [ "master" ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write # Uncomment the permissions below if installing in a private repository. # contents: read # actions: read steps: - name: "Checkout code" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: # - you want to enable the Branch-Protection check on a *public* repository, or # - you are installing Scorecard on a *private* repository # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. # repo_token: ${{ secrets.SCORECARD_TOKEN }} # Public repositories: # - Publish results to OpenSSF REST API for easy access by consumers # - Allows the repository to include the Scorecard badge. # - See https://github.com/ossf/scorecard-action#publishing-results. # For private repositories: # - `publish_results` will always be set to `false`, regardless # of the value entered here. publish_results: true # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file path: results.sarif retention-days: 5 # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5 with: sarif_file: results.sarifjshttp-proxy-addr-a11ad82/.gitignore000066400000000000000000000000671525215777600175340ustar00rootroot00000000000000.nyc_output/ coverage/ node_modules/ package-lock.json jshttp-proxy-addr-a11ad82/HISTORY.md000066400000000000000000000046601525215777600172320ustar00rootroot000000000000002.0.8 ===== * Fix [CVE-2026-90711](https://www.cve.org/CVERecord?id=CVE-2026-90711) ([GHSA-jqcg-44mw-7w3h](https://github.com/jshttp/proxy-addr/security/advisories/GHSA-jqcg-44mw-7w3h)) 2.0.7 ===== * deps: forwarded@0.2.0 - Use `req.socket` over deprecated `req.connection` 2.0.6 ===== * deps: ipaddr.js@1.9.1 2.0.5 ===== * deps: ipaddr.js@1.9.0 2.0.4 ===== * deps: ipaddr.js@1.8.0 2.0.3 ===== * deps: ipaddr.js@1.6.0 2.0.2 ===== * deps: forwarded@~0.1.2 - perf: improve header parsing - perf: reduce overhead when no `X-Forwarded-For` header 2.0.1 ===== * deps: forwarded@~0.1.1 - Fix trimming leading / trailing OWS - perf: hoist regular expression * deps: ipaddr.js@1.5.2 2.0.0 ===== * Drop support for Node.js below 0.10 1.1.5 ===== * Fix array argument being altered * deps: ipaddr.js@1.4.0 1.1.4 ===== * deps: ipaddr.js@1.3.0 1.1.3 ===== * deps: ipaddr.js@1.2.0 1.1.2 ===== * deps: ipaddr.js@1.1.1 - Fix IPv6-mapped IPv4 validation edge cases 1.1.1 ===== * Fix regression matching mixed versions against multiple subnets 1.1.0 ===== * Fix accepting various invalid netmasks - IPv4 netmasks must be contingous - IPv6 addresses cannot be used as a netmask * deps: ipaddr.js@1.1.0 1.0.10 ====== * deps: ipaddr.js@1.0.5 - Fix regression in `isValid` with non-string arguments 1.0.9 ===== * deps: ipaddr.js@1.0.4 - Fix accepting some invalid IPv6 addresses - Reject CIDRs with negative or overlong masks * perf: enable strict mode 1.0.8 ===== * deps: ipaddr.js@1.0.1 1.0.7 ===== * deps: ipaddr.js@0.1.9 - Fix OOM on certain inputs to `isValid` 1.0.6 ===== * deps: ipaddr.js@0.1.8 1.0.5 ===== * deps: ipaddr.js@0.1.6 1.0.4 ===== * deps: ipaddr.js@0.1.5 - Fix edge cases with `isValid` 1.0.3 ===== * Use `forwarded` npm module 1.0.2 ===== * Fix a global leak when multiple subnets are trusted * Support Node.js 0.6 * deps: ipaddr.js@0.1.3 1.0.1 ===== * Fix links in npm package 1.0.0 ===== * Add `trust` argument to determine proxy trust on * Accepts custom function * Accepts IPv4/IPv6 address(es) * Accepts subnets * Accepts pre-defined names * Add optional `trust` argument to `proxyaddr.all` to stop at first untrusted * Add `proxyaddr.compile` to pre-compile `trust` function to make subsequent calls faster 0.0.1 ===== * Fix bad npm publish 0.0.0 ===== * Initial release jshttp-proxy-addr-a11ad82/LICENSE000066400000000000000000000021061525215777600165450ustar00rootroot00000000000000(The MIT License) Copyright (c) 2014-2016 Douglas Christopher Wilson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jshttp-proxy-addr-a11ad82/README.md000066400000000000000000000100431525215777600170160ustar00rootroot00000000000000# proxy-addr [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Node.js Version][node-image]][node-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][coveralls-image]][coveralls-url] Determine address of proxied request ## Install This is a [Node.js](https://nodejs.org/en/) module available through the [npm registry](https://www.npmjs.com/). Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): ```sh $ npm install proxy-addr ``` ## API ```js var proxyaddr = require('proxy-addr') ``` ### proxyaddr(req, trust) Return the address of the request, using the given `trust` parameter. The `trust` argument is a function that returns `true` if you trust the address, `false` if you don't. The closest untrusted address is returned. ```js proxyaddr(req, function (addr) { return addr === '127.0.0.1' }) proxyaddr(req, function (addr, i) { return i < 1 }) ``` The `trust` argument may also be a single IP address string or an array of trusted addresses, as plain IP addresses, CIDR-formatted strings, or IP/netmask strings. ```js proxyaddr(req, '127.0.0.1') proxyaddr(req, ['127.0.0.0/8', '10.0.0.0/8']) proxyaddr(req, ['127.0.0.0/255.0.0.0', '192.168.0.0/255.255.0.0']) ``` This module also supports IPv6. Your IPv6 addresses will be normalized automatically (i.e. `fe80::00ed:1` equals `fe80:0:0:0:0:0:ed:1`). ```js proxyaddr(req, '::1') proxyaddr(req, ['::1/128', 'fe80::/10']) ``` This module will automatically work with IPv4-mapped IPv6 addresses as well to support node.js in IPv6-only mode. This means that you do not have to specify both `::ffff:a00:1` and `10.0.0.1`. As a convenience, this module also takes certain pre-defined names in addition to IP addresses, which expand into IP addresses: ```js proxyaddr(req, 'loopback') proxyaddr(req, ['loopback', 'fc00:ac:1ab5:fff::1/64']) ``` * `loopback`: IPv4 and IPv6 loopback addresses (like `::1` and `127.0.0.1`). * `linklocal`: IPv4 and IPv6 link-local addresses (like `fe80::1:1:1:1` and `169.254.0.1`). * `uniquelocal`: IPv4 private addresses and IPv6 unique-local addresses (like `fc00:ac:1ab5:fff::1` and `192.168.0.1`). When `trust` is specified as a function, it will be called for each address to determine if it is a trusted address. The function is given two arguments: `addr` and `i`, where `addr` is a string of the address to check and `i` is a number that represents the distance from the socket address. ### proxyaddr.all(req, [trust]) Return all the addresses of the request, optionally stopping at the first untrusted. This array is ordered from closest to furthest (i.e. `arr[0] === req.connection.remoteAddress`). ```js proxyaddr.all(req) ``` The optional `trust` argument takes the same arguments as `trust` does in `proxyaddr(req, trust)`. ```js proxyaddr.all(req, 'loopback') ``` ### proxyaddr.compile(val) Compiles argument `val` into a `trust` function. This function takes the same arguments as `trust` does in `proxyaddr(req, trust)` and returns a function suitable for `proxyaddr(req, trust)`. ```js var trust = proxyaddr.compile('loopback') var addr = proxyaddr(req, trust) ``` This function is meant to be optimized for use against every request. It is recommend to compile a trust function up-front for the trusted configuration and pass that to `proxyaddr(req, trust)` for each request. ## Testing ```sh $ npm test ``` ## Benchmarks ```sh $ npm run-script bench ``` ## License [MIT](LICENSE) [ci-image]: https://badgen.net/github/checks/jshttp/proxy-addr/master?label=ci [ci-url]: https://github.com/jshttp/proxy-addr/actions?query=workflow%3Aci [coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/proxy-addr/master [coveralls-url]: https://coveralls.io/r/jshttp/proxy-addr?branch=master [node-image]: https://badgen.net/npm/node/proxy-addr [node-url]: https://nodejs.org/en/download [npm-downloads-image]: https://badgen.net/npm/dm/proxy-addr [npm-url]: https://npmjs.org/package/proxy-addr [npm-version-image]: https://badgen.net/npm/v/proxy-addr jshttp-proxy-addr-a11ad82/benchmark/000077500000000000000000000000001525215777600174735ustar00rootroot00000000000000jshttp-proxy-addr-a11ad82/benchmark/compiling.js000066400000000000000000000016641525215777600220210ustar00rootroot00000000000000 /** * Globals for benchmark.js */ global.proxyaddr = require('..') global.createReq = createReq /** * Module dependencies. */ var benchmark = require('benchmark') var benchmarks = require('beautify-benchmark') var suite = new benchmark.Suite() suite.add({ name: 're-compiling', minSamples: 100, fn: 'proxyaddr(req, "loopback")', setup: 'req = createReq("127.0.0.1", "10.0.0.1")' }) suite.add({ name: 'pre-compiling', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("loopback")' }) suite.on('cycle', function onCycle (event) { benchmarks.add(event.target) }) suite.on('complete', function onComplete () { benchmarks.log() }) suite.run({ async: false }) function createReq (socketAddr, forwardedFor) { return { connection: { remoteAddress: socketAddr }, headers: { 'x-forwarded-for': (forwardedFor || '') } } } jshttp-proxy-addr-a11ad82/benchmark/index.js000066400000000000000000000012161525215777600211400ustar00rootroot00000000000000var fs = require('fs') var path = require('path') var spawn = require('child_process').spawn var exe = process.argv[0] var cwd = process.cwd() runScripts(fs.readdirSync(__dirname)) function runScripts (fileNames) { var fileName = fileNames.shift() if (!fileName) return if (!/\.js$/i.test(fileName)) return runScripts(fileNames) if (fileName.toLowerCase() === 'index.js') return runScripts(fileNames) var fullPath = path.join(__dirname, fileName) console.log('> %s %s', exe, path.relative(cwd, fullPath)) var proc = spawn(exe, [fullPath], { stdio: 'inherit' }) proc.on('exit', function () { runScripts(fileNames) }) } jshttp-proxy-addr-a11ad82/benchmark/kind.js000066400000000000000000000021641525215777600207610ustar00rootroot00000000000000 /** * Globals for benchmark.js */ global.proxyaddr = require('..') global.createReq = createReq /** * Module dependencies. */ var benchmark = require('benchmark') var benchmarks = require('beautify-benchmark') var suite = new benchmark.Suite() suite.add({ name: 'ipv4', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1")' }) suite.add({ name: 'ipv4-mapped', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("::ffff:7f00:1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1")' }) suite.add({ name: 'ipv6', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("::1", "10.0.0.1"); trust = proxyaddr.compile("::1")' }) suite.on('cycle', function onCycle (event) { benchmarks.add(event.target) }) suite.on('complete', function onComplete () { benchmarks.log() }) suite.run({ async: false }) function createReq (socketAddr, forwardedFor) { return { connection: { remoteAddress: socketAddr }, headers: { 'x-forwarded-for': (forwardedFor || '') } } } jshttp-proxy-addr-a11ad82/benchmark/matching.js000066400000000000000000000032511525215777600216240ustar00rootroot00000000000000 /** * Globals for benchmark.js */ global.proxyaddr = require('..') global.createReq = createReq /** * Module dependencies. */ var benchmark = require('benchmark') var benchmarks = require('beautify-benchmark') var suite = new benchmark.Suite() suite.add({ name: 'trust none', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile([])' }) suite.add({ name: 'trust all', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = function() {return true}' }) suite.add({ name: 'trust single', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1")' }) suite.add({ name: 'trust first', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = function(a, i) {return i<1}' }) suite.add({ name: 'trust subnet', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1/8")' }) suite.add({ name: 'trust multiple', minSamples: 100, fn: 'proxyaddr(req, trust)', setup: 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile(["127.0.0.1", "10.0.0.1"])' }) suite.on('cycle', function onCycle (event) { benchmarks.add(event.target) }) suite.on('complete', function onComplete () { benchmarks.log() }) suite.run({ async: false }) function createReq (socketAddr, forwardedFor) { return { connection: { remoteAddress: socketAddr }, headers: { 'x-forwarded-for': (forwardedFor || '') } } } jshttp-proxy-addr-a11ad82/index.js000066400000000000000000000162541525215777600172160ustar00rootroot00000000000000/*! * proxy-addr * Copyright(c) 2014-2016 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module exports. * @public */ module.exports = proxyaddr module.exports.all = alladdrs module.exports.compile = compile /** * Module dependencies. * @private */ var forwarded = require('forwarded') var ipaddr = require('ipaddr.js') /** * Variables. * @private */ var DIGIT_REGEXP = /^[0-9]+$/ var isip = ipaddr.isValid var parseip = ipaddr.parse /** * Pre-defined IP ranges. * @private */ var IP_RANGES = { linklocal: ['169.254.0.0/16', 'fe80::/10'], loopback: ['127.0.0.1/8', '::1/128'], uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7'] } /** * Get all addresses in the request, optionally stopping * at the first untrusted. * * @param {Object} request * @param {Function|Array|String} [trust] * @public */ function alladdrs (req, trust) { // get addresses var addrs = forwarded(req) if (!trust) { // Return all addresses return addrs } if (typeof trust !== 'function') { trust = compile(trust) } for (var i = 0; i < addrs.length - 1; i++) { if (trust(addrs[i], i)) continue addrs.length = i + 1 } return addrs } /** * Compile argument into trust function. * * @param {Array|String} val * @private */ function compile (val) { if (!val) { throw new TypeError('argument is required') } var trust if (typeof val === 'string') { trust = [val] } else if (Array.isArray(val)) { trust = val.slice() } else { throw new TypeError('unsupported trust argument') } for (var i = 0; i < trust.length; i++) { val = trust[i] if (!Object.prototype.hasOwnProperty.call(IP_RANGES, val)) { continue } // Splice in pre-defined range val = IP_RANGES[val] trust.splice.apply(trust, [i, 1].concat(val)) i += val.length - 1 } return compileTrust(compileRangeSubnets(trust)) } /** * Compile `arr` elements into range subnets. * * @param {Array} arr * @private */ function compileRangeSubnets (arr) { var rangeSubnets = new Array(arr.length) for (var i = 0; i < arr.length; i++) { rangeSubnets[i] = parseipNotation(arr[i]) } return rangeSubnets } /** * Compile range subnet array into trust function. * * @param {Array} rangeSubnets * @private */ function compileTrust (rangeSubnets) { // Return optimized function based on length var len = rangeSubnets.length return len === 0 ? trustNone : len === 1 ? trustSingle(rangeSubnets[0]) : trustMulti(rangeSubnets) } /** * Parse IP notation string into range subnet. * * @param {String} note * @private */ function parseipNotation (note) { var pos = note.lastIndexOf('/') var str = pos !== -1 ? note.substring(0, pos) : note if (!isip(str)) { throw new TypeError('invalid IP address: ' + str) } var ip = parseip(str) if (pos === -1 && ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) { // Store as IPv4 ip = ip.toIPv4Address() } var max = ip.kind() === 'ipv6' ? 128 : 32 var range = pos !== -1 ? note.substring(pos + 1, note.length) : null if (range === null) { range = max } else if (DIGIT_REGEXP.test(range)) { range = parseInt(range, 10) } else if (ip.kind() === 'ipv4' && isip(range)) { range = parseNetmask(range) } else { range = null } if (range <= 0 || range > max) { throw new TypeError('invalid range on address: ' + note) } return [ip, range] } /** * Parse netmask string into CIDR range. * * @param {String} netmask * @private */ function parseNetmask (netmask) { var ip = parseip(netmask) var kind = ip.kind() return kind === 'ipv4' ? ip.prefixLengthFromSubnetMask() : null } /** * Determine address of proxied request. * * @param {Object} request * @param {Function|Array|String} trust * @public */ function proxyaddr (req, trust) { if (!req) { throw new TypeError('req argument is required') } if (!trust) { throw new TypeError('trust argument is required') } var addrs = alladdrs(req, trust) var addr = addrs[addrs.length - 1] return addr } /** * Static trust function to trust nothing. * * @private */ function trustNone () { return false } /** * Compile trust function for multiple subnets. * * @param {Array} subnets * @private */ function trustMulti (subnets) { return function trust (addr) { if (!isip(addr)) return false var ip = parseip(addr) if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) { // Canonicalize IPv4-mapped candidates to IPv4 so a mapped address // cannot bypass the cross-family guard via the same-family path ip = ip.toIPv4Address() } var ipconv var kind = ip.kind() for (var i = 0; i < subnets.length; i++) { var subnet = subnets[i] var subnetip = subnet[0] var subnetkind = subnetip.kind() var subnetrange = subnet[1] var trusted = ip if (kind !== subnetkind) { if (subnetkind === 'ipv4' && !ip.isIPv4MappedAddress()) { // Incompatible IP addresses continue } if (subnetkind !== 'ipv4' && !(subnetrange >= 96 && subnetip.isIPv4MappedAddress())) { // IPv6 subnet only spans IPv4 when it is a mapped subnet whose // prefix covers the ::ffff: marker; otherwise it must not match IPv4 continue } if (!ipconv) { // Convert IP to match subnet IP kind ipconv = subnetkind === 'ipv4' ? ip.toIPv4Address() : ip.toIPv4MappedAddress() } trusted = ipconv } else if (kind === 'ipv6' && subnetip.isIPv4MappedAddress()) { // A native IPv6 candidate cannot match an IPv4-mapped subnet continue } if (trusted.match(subnetip, subnetrange)) { return true } } return false } } /** * Compile trust function for single subnet. * * @param {Object} subnet * @private */ function trustSingle (subnet) { var subnetip = subnet[0] var subnetkind = subnetip.kind() var subnetisipv4 = subnetkind === 'ipv4' var subnetrange = subnet[1] return function trust (addr) { if (!isip(addr)) return false var ip = parseip(addr) if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) { // Canonicalize IPv4-mapped candidates to IPv4 so a mapped address // cannot bypass the cross-family guard via the same-family path ip = ip.toIPv4Address() } var kind = ip.kind() if (kind !== subnetkind) { if (subnetisipv4 && !ip.isIPv4MappedAddress()) { // Incompatible IP addresses return false } if (!subnetisipv4 && !(subnetrange >= 96 && subnetip.isIPv4MappedAddress())) { // IPv6 subnet only spans IPv4 when it is a mapped subnet whose // prefix covers the ::ffff: marker; otherwise it must not match IPv4 return false } // Convert IP to match subnet IP kind ip = subnetisipv4 ? ip.toIPv4Address() : ip.toIPv4MappedAddress() } else if (kind === 'ipv6' && subnetip.isIPv4MappedAddress()) { // A native IPv6 candidate cannot match an IPv4-mapped subnet return false } return ip.match(subnetip, subnetrange) } } jshttp-proxy-addr-a11ad82/package.json000066400000000000000000000024011525215777600200240ustar00rootroot00000000000000{ "name": "proxy-addr", "description": "Determine address of proxied request", "version": "2.0.8", "author": "Douglas Christopher Wilson ", "license": "MIT", "keywords": [ "ip", "proxy", "x-forwarded-for" ], "repository": "jshttp/proxy-addr", "funding": { "type": "opencollective", "url": "https://opencollective.com/express" }, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "devDependencies": { "benchmark": "2.1.4", "beautify-benchmark": "0.2.4", "deep-equal": "1.1.2", "eslint": "7.26.0", "eslint-config-standard": "14.1.1", "eslint-plugin-import": "2.32.0", "eslint-plugin-markdown": "2.2.1", "eslint-plugin-node": "11.1.0", "eslint-plugin-promise": "4.3.1", "eslint-plugin-standard": "4.1.0", "mocha": "8.4.0", "nyc": "15.1.0" }, "files": [ "LICENSE", "HISTORY.md", "README.md", "index.js" ], "engines": { "node": ">= 0.10" }, "scripts": { "bench": "node benchmark/index.js", "lint": "eslint .", "test": "mocha --reporter spec --bail --check-leaks test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" } } jshttp-proxy-addr-a11ad82/test/000077500000000000000000000000001525215777600165205ustar00rootroot00000000000000jshttp-proxy-addr-a11ad82/test/.eslintrc.yml000066400000000000000000000000231525215777600211370ustar00rootroot00000000000000env: mocha: true jshttp-proxy-addr-a11ad82/test/test.js000066400000000000000000000546241525215777600200500ustar00rootroot00000000000000 var assert = require('assert') var deepEqual = require('deep-equal') var proxyaddr = require('..') describe('proxyaddr(req, trust)', function () { describe('arguments', function () { describe('req', function () { it('should be required', function () { assert.throws(proxyaddr, /req.*required/) }) }) describe('trust', function () { it('should be required', function () { var req = createReq('127.0.0.1') assert.throws(proxyaddr.bind(null, req), /trust.*required/) }) it('should accept a function', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, all)) }) it('should accept an array', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, [])) }) it('should accept a string', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, '127.0.0.1')) }) it('should reject a number', function () { var req = createReq('127.0.0.1') assert.throws(proxyaddr.bind(null, req, 42), /unsupported trust argument/) }) it('should accept IPv4', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, '127.0.0.1')) }) it('should accept IPv6', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, '::1')) }) it('should accept IPv4-style IPv6', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, '::ffff:127.0.0.1')) }) it('should accept pre-defined names', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, 'loopback')) }) it('should accept pre-defined names in array', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, ['loopback', '10.0.0.1'])) }) it('should not alter input array', function () { var arr = ['loopback', '10.0.0.1'] var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.bind(null, req, arr)) strictDeepEqual(arr, ['loopback', '10.0.0.1']) }) it('should reject non-IP', function () { var req = createReq('127.0.0.1') assert.throws(proxyaddr.bind(null, req, 'blargh'), /invalid IP address/) assert.throws(proxyaddr.bind(null, req, '10.0.300.1'), /invalid IP address/) assert.throws(proxyaddr.bind(null, req, '::ffff:30.168.1.9000'), /invalid IP address/) assert.throws(proxyaddr.bind(null, req, '-1'), /invalid IP address/) }) it('should reject bad CIDR', function () { var req = createReq('127.0.0.1') assert.throws(proxyaddr.bind(null, req, '10.0.0.1/internet'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, '10.0.0.1/6000'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, '::1/6000'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, '::ffff:a00:2/136'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, '::ffff:a00:2/-1'), /invalid range on address/) }) it('should reject bad netmask', function () { var req = createReq('127.0.0.1') assert.throws(proxyaddr.bind(null, req, '10.0.0.1/255.0.255.0'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, '10.0.0.1/ffc0::'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, 'fe80::/ffc0::'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, 'fe80::/255.255.255.0'), /invalid range on address/) assert.throws(proxyaddr.bind(null, req, '::ffff:a00:2/255.255.255.0'), /invalid range on address/) }) it('should be invoked as trust(addr, i)', function () { var log = [] var req = createReq('127.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.1' }) proxyaddr(req, function (addr, i) { return log.push(Array.prototype.slice.call(arguments)) }) strictDeepEqual(log, [ ['127.0.0.1', 0], ['10.0.0.1', 1] ]) }) }) }) describe('with all trusted', function () { it('should return socket address with no headers', function () { var req = createReq('127.0.0.1') assert.strictEqual(proxyaddr(req, all), '127.0.0.1') }) it('should return header value', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1' }) assert.strictEqual(proxyaddr(req, all), '10.0.0.1') }) it('should return furthest header value', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, all), '10.0.0.1') }) }) describe('with none trusted', function () { it('should return socket address with no headers', function () { var req = createReq('127.0.0.1') assert.strictEqual(proxyaddr(req, none), '127.0.0.1') }) it('should return socket address with headers', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, none), '127.0.0.1') }) }) describe('with some trusted', function () { it('should return socket address with no headers', function () { var req = createReq('127.0.0.1') assert.strictEqual(proxyaddr(req, trust10x), '127.0.0.1') }) it('should return socket address when not trusted', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, trust10x), '127.0.0.1') }) it('should return header when socket trusted', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1' }) assert.strictEqual(proxyaddr(req, trust10x), '192.168.0.1') }) it('should return first untrusted after trusted', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, trust10x), '192.168.0.1') }) it('should not skip untrusted', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '10.0.0.3, 192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, trust10x), '192.168.0.1') }) }) describe('when given array', function () { it('should accept literal IP addresses', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, ['10.0.0.1', '10.0.0.2']), '192.168.0.1') }) it('should not trust non-IP addresses', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2, localhost' }) assert.strictEqual(proxyaddr(req, ['10.0.0.1', '10.0.0.2']), 'localhost') }) it('should return socket address if none match', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, ['127.0.0.1', '192.168.0.100']), '10.0.0.1') }) describe('when array empty', function () { it('should return socket address ', function () { var req = createReq('127.0.0.1') assert.strictEqual(proxyaddr(req, []), '127.0.0.1') }) it('should return socket address with headers', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, []), '127.0.0.1') }) }) }) describe('when given IPv4 addresses', function () { it('should accept literal IP addresses', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, ['10.0.0.1', '10.0.0.2']), '192.168.0.1') }) it('should accept CIDR notation', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.200' }) assert.strictEqual(proxyaddr(req, '10.0.0.2/26'), '10.0.0.200') }) it('should accept netmask notation', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.200' }) assert.strictEqual(proxyaddr(req, '10.0.0.2/255.255.255.192'), '10.0.0.200') }) }) describe('when given IPv6 addresses', function () { it('should accept literal IP addresses', function () { var req = createReq('fe80::1', { 'x-forwarded-for': '2002:c000:203::1, fe80::2' }) assert.strictEqual(proxyaddr(req, ['fe80::1', 'fe80::2']), '2002:c000:203::1') }) it('should accept CIDR notation', function () { var req = createReq('fe80::1', { 'x-forwarded-for': '2002:c000:203::1, fe80::ff00' }) assert.strictEqual(proxyaddr(req, 'fe80::/125'), 'fe80::ff00') }) }) describe('when IP versions mixed', function () { it('should match respective versions', function () { var req = createReq('::1', { 'x-forwarded-for': '2002:c000:203::1' }) assert.strictEqual(proxyaddr(req, ['127.0.0.1', '::1']), '2002:c000:203::1') }) it('should not match IPv4 to IPv6', function () { var req = createReq('::1', { 'x-forwarded-for': '2002:c000:203::1' }) assert.strictEqual(proxyaddr(req, '127.0.0.1'), '::1') }) }) describe('when IPv4-mapped IPv6 addresses', function () { it('should match IPv4 trust to IPv6 request', function () { var req = createReq('::ffff:a00:1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, ['10.0.0.1', '10.0.0.2']), '192.168.0.1') }) it('should match IPv4 netmask trust to IPv6 request', function () { var req = createReq('::ffff:a00:1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, ['10.0.0.1/16']), '192.168.0.1') }) it('should match IPv6 trust to IPv4 request', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.2' }) assert.strictEqual(proxyaddr(req, ['::ffff:a00:1', '::ffff:a00:2']), '192.168.0.1') }) it('should match CIDR notation for IPv4-mapped address', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.200' }) assert.strictEqual(proxyaddr(req, '::ffff:a00:2/122'), '10.0.0.200') }) it('should match CIDR notation for IPv4-mapped address mixed with IPv6 CIDR', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.200' }) assert.strictEqual(proxyaddr(req, ['::ffff:a00:2/122', 'fe80::/125']), '10.0.0.200') }) it('should match CIDR notation for IPv4-mapped address mixed with IPv4 addresses', function () { var req = createReq('10.0.0.1', { 'x-forwarded-for': '192.168.0.1, 10.0.0.200' }) assert.strictEqual(proxyaddr(req, ['::ffff:a00:2/122', '127.0.0.1']), '10.0.0.200') }) }) describe('when given pre-defined names', function () { it('should accept single pre-defined name', function () { var req = createReq('fe80::1', { 'x-forwarded-for': '2002:c000:203::1, fe80::2' }) assert.strictEqual(proxyaddr(req, 'linklocal'), '2002:c000:203::1') }) it('should accept multiple pre-defined names', function () { var req = createReq('::1', { 'x-forwarded-for': '2002:c000:203::1, fe80::2' }) assert.strictEqual(proxyaddr(req, ['loopback', 'linklocal']), '2002:c000:203::1') }) }) describe('when header contains non-ip addresses', function () { it('should stop at first non-ip after trusted', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': 'myrouter, 127.0.0.1, proxy' }) assert.strictEqual(proxyaddr(req, '127.0.0.1'), 'proxy') }) it('should stop at first malformed ip after trusted', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': 'myrouter, 127.0.0.1, ::8:8:8:8:8:8:8:8:8' }) assert.strictEqual(proxyaddr(req, '127.0.0.1'), '::8:8:8:8:8:8:8:8:8') }) it('should provide all values to function', function () { var log = [] var req = createReq('127.0.0.1', { 'x-forwarded-for': 'myrouter, 127.0.0.1, proxy' }) proxyaddr(req, function (addr, i) { return log.push(Array.prototype.slice.call(arguments)) }) strictDeepEqual(log, [ ['127.0.0.1', 0], ['proxy', 1], ['127.0.0.1', 2] ]) }) }) describe('when socket address undefined', function () { it('should return undefined as address', function () { var req = createReq(undefined) assert.strictEqual(proxyaddr(req, '127.0.0.1'), undefined) }) it('should return undefined even with trusted headers', function () { var req = createReq(undefined, { 'x-forwarded-for': '127.0.0.1, 10.0.0.1' }) assert.strictEqual(proxyaddr(req, '127.0.0.1'), undefined) }) }) }) describe('proxyaddr.all(req, [trust])', function () { describe('arguments', function () { describe('req', function () { it('should be required', function () { assert.throws(proxyaddr.all, /req.*required/) }) }) describe('trust', function () { it('should be optional', function () { var req = createReq('127.0.0.1') assert.doesNotThrow(proxyaddr.all.bind(null, req)) }) }) }) describe('with no headers', function () { it('should return socket address', function () { var req = createReq('127.0.0.1') strictDeepEqual(proxyaddr.all(req), ['127.0.0.1']) }) }) describe('with x-forwarded-for header', function () { it('should include x-forwarded-for', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1' }) strictDeepEqual(proxyaddr.all(req), ['127.0.0.1', '10.0.0.1']) }) it('should include x-forwarded-for in correct order', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) strictDeepEqual(proxyaddr.all(req), ['127.0.0.1', '10.0.0.2', '10.0.0.1']) }) }) describe('with trust argument', function () { it('should stop at first untrusted', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) strictDeepEqual(proxyaddr.all(req, '127.0.0.1'), ['127.0.0.1', '10.0.0.2']) }) it('should be only socket address for no trust', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' }) strictDeepEqual(proxyaddr.all(req, []), ['127.0.0.1']) }) }) }) describe('proxyaddr.compile(trust)', function () { describe('arguments', function () { describe('trust', function () { it('should be required', function () { assert.throws(proxyaddr.compile, /argument.*required/) }) it('should accept an array', function () { assert.strictEqual(typeof proxyaddr.compile([]), 'function') }) it('should accept a string', function () { assert.strictEqual(typeof proxyaddr.compile('127.0.0.1'), 'function') }) it('should reject a number', function () { assert.throws(proxyaddr.compile.bind(null, 42), /unsupported trust argument/) }) it('should accept IPv4', function () { assert.strictEqual(typeof proxyaddr.compile('127.0.0.1'), 'function') }) it('should accept IPv6', function () { assert.strictEqual(typeof proxyaddr.compile('::1'), 'function') }) it('should accept IPv4-style IPv6', function () { assert.strictEqual(typeof proxyaddr.compile('::ffff:127.0.0.1'), 'function') }) it('should accept pre-defined names', function () { assert.strictEqual(typeof proxyaddr.compile('loopback'), 'function') }) it('should accept pre-defined names in array', function () { assert.strictEqual(typeof proxyaddr.compile(['loopback', '10.0.0.1']), 'function') }) it('should reject non-IP', function () { assert.throws(proxyaddr.compile.bind(null, 'blargh'), /invalid IP address/) assert.throws(proxyaddr.compile.bind(null, '-1'), /invalid IP address/) }) it('should reject bad CIDR', function () { assert.throws(proxyaddr.compile.bind(null, '10.0.0.1/6000'), /invalid range on address/) assert.throws(proxyaddr.compile.bind(null, '::1/6000'), /invalid range on address/) assert.throws(proxyaddr.compile.bind(null, '::ffff:a00:2/136'), /invalid range on address/) assert.throws(proxyaddr.compile.bind(null, '::ffff:a00:2/-46'), /invalid range on address/) }) it('should not alter input array', function () { var arr = ['loopback', '10.0.0.1'] assert.strictEqual(typeof proxyaddr.compile(arr), 'function') strictDeepEqual(arr, ['loopback', '10.0.0.1']) }) }) }) }) describe('proxyaddr.compile(trust) IPv4-mapped IPv6 subnets', function () { // @see https://github.com/jshttp/proxy-addr/security/advisories/GHSA-jqcg-44mw-7w3h it('should not trust arbitrary IPv4 for a short-prefix mapped subnet', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/8']) assert.strictEqual(trust('8.8.8.8'), false) assert.strictEqual(trust('203.0.113.9'), false) assert.strictEqual(trust('0.0.0.0'), false) assert.strictEqual(trust('10.0.0.1'), false) }) it('should trust the intended block when written as a /104 mapped subnet', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/104']) assert.strictEqual(trust('10.0.0.1'), true) assert.strictEqual(trust('8.8.8.8'), false) }) it('should behave like the plain IPv4 subnet', function () { var trust = proxyaddr.compile(['10.0.0.0/8']) assert.strictEqual(trust('10.0.0.1'), true) assert.strictEqual(trust('8.8.8.8'), false) }) it('should treat a /96 mapped subnet as all IPv4', function () { var trust = proxyaddr.compile(['::ffff:0.0.0.0/96']) assert.strictEqual(trust('10.0.0.1'), true) assert.strictEqual(trust('8.8.8.8'), true) }) it('should reject a mapped subnet whose prefix is below 96', function () { var trust = proxyaddr.compile(['::ffff:0.0.0.0/95']) assert.strictEqual(trust('8.8.8.8'), false) }) it('should honor a /97 mapped subnet as a proper subset of IPv4', function () { var trust = proxyaddr.compile(['::ffff:0.0.0.0/97']) assert.strictEqual(trust('8.8.8.8'), true) assert.strictEqual(trust('120.0.0.1'), true) assert.strictEqual(trust('200.0.0.1'), false) }) it('should not trust IPv4 for an IPv6 subnet with zero leading bits', function () { var trust = proxyaddr.compile(['::/1']) assert.strictEqual(trust('8.8.8.8'), false) assert.strictEqual(trust('::ffff:8.8.8.8'), false) assert.strictEqual(trust('::ffff:10.0.0.1'), false) }) it('should leave ordinary IPv6 subnets unaffected', function () { var trust = proxyaddr.compile(['fc00::/7']) assert.strictEqual(trust('8.8.8.8'), false) assert.strictEqual(trust('fc00::1'), true) }) it('should leave the built-in uniquelocal name unaffected', function () { var trust = proxyaddr.compile('uniquelocal') assert.strictEqual(trust('10.0.0.1'), true) assert.strictEqual(trust('8.8.8.8'), false) }) it('should neutralize a short-prefix mapped subnet in a multi-subnet list', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/8', '10.0.0.0/8']) assert.strictEqual(trust('10.0.0.1'), true) assert.strictEqual(trust('8.8.8.8'), false) }) it('should not believe X-Forwarded-For from an untrusted socket peer', function () { var req = createReq('127.0.0.1', { 'x-forwarded-for': '9.9.9.9' }) assert.strictEqual(proxyaddr(req, ['::ffff:10.0.0.0/8']), '127.0.0.1') }) it('should not trust an IPv4-mapped candidate for a short-prefix mapped subnet', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/8']) assert.strictEqual(trust('::ffff:8.8.8.8'), false) assert.strictEqual(trust('::ffff:1.2.3.4'), false) assert.strictEqual(trust('::ffff:10.0.0.1'), false) }) it('should trust an IPv4-mapped candidate for a /104 mapped subnet', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/104']) assert.strictEqual(trust('::ffff:10.0.0.1'), true) assert.strictEqual(trust('::ffff:8.8.8.8'), false) }) it('should match a mapped candidate identically to its IPv4 form', function () { var trust = proxyaddr.compile(['10.0.0.0/8']) assert.strictEqual(trust('::ffff:10.0.0.1'), true) assert.strictEqual(trust('::ffff:8.8.8.8'), false) }) it('should not believe X-Forwarded-For from an untrusted IPv4-mapped socket peer', function () { var req = createReq('::ffff:8.8.8.8', { 'x-forwarded-for': '9.9.9.9' }) assert.strictEqual(proxyaddr(req, ['::ffff:10.0.0.0/8']), '::ffff:8.8.8.8') }) it('should not trust native IPv6 for a short-prefix mapped subnet', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/8']) assert.strictEqual(trust('::1'), false) assert.strictEqual(trust('::abcd'), false) var trust95 = proxyaddr.compile(['::ffff:0.0.0.0/95']) assert.strictEqual(trust95('::fffe:0:0'), false) }) it('should not trust native IPv6 for a valid mapped subnet', function () { var trust = proxyaddr.compile(['::ffff:10.0.0.0/104']) assert.strictEqual(trust('::1'), false) assert.strictEqual(trust('10.0.0.1'), true) }) it('should still trust native IPv6 within an ordinary IPv6 subnet', function () { var trust = proxyaddr.compile(['2001:db8::/32']) assert.strictEqual(trust('2001:db8::1'), true) assert.strictEqual(trust('::1'), false) }) }) function createReq (socketAddr, headers) { return { connection: { remoteAddress: socketAddr }, headers: headers || {} } } function strictDeepEqual (actual, expected, message) { if (assert.deepStrictEqual) { assert.deepStrictEqual(actual, expected, message) } else { assert.ok(deepEqual(actual, expected, { strict: true })) } } function all () { return true } function none () { return false } function trust10x (addr) { return /^10\./.test(addr) }