Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.0-rc.1 #1

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d500075
v1.0.0-rc.1
May 30, 2015
7416d5a
resolving node <=0.8 issue ("debug" target not found)
May 30, 2015
0b8bbc3
add node 0.12 to travis matrix
May 30, 2015
e9badf7
use node 0.6 safe versions of mocha + istanbul
May 30, 2015
5f36bf9
remove dependency on util._extend (node 0.6 support)
May 30, 2015
3e90938
fix gh markdown rendering bug
May 30, 2015
0962a47
restore copyright notice
May 30, 2015
560dfa6
throw error on invalid schema name
May 30, 2015
34ad6cc
lowecase schema names + test
May 30, 2015
4e7e8f4
debug should be under dependencies, rather than devDependencies
May 30, 2015
2d937df
On a disconnected socket, req.connection.remotePort is undefined
May 30, 2015
1520eae
fix: address ordering in rfc2739 processor, fix: ensure processed 'fo…
May 30, 2015
8694d1c
default to rfc7239 schema
May 30, 2015
5e77858
fix: cloudflare https detection
May 30, 2015
cbab687
fix: fastly ssl header existance counts as HTTPS
May 30, 2015
1761f9d
Merge remote-tracking branch 'upstream/master'
May 30, 2015
7a3cb41
add author to LICENSE
May 31, 2015
f39a24f
update author info
May 31, 2015
3363328
added test with real http server and request
May 31, 2015
553fb6f
node 0.6 lacks a callback function on server.close
May 31, 2015
e8f6794
chore(dependencies): update dependencies + replace debug for lighter …
Mar 26, 2016
6ce460a
chore(rename): better use of header prefixes to reflect schema naming
Mar 26, 2016
3459974
refactor(proto): simplified logic for protocol detection
Mar 26, 2016
e2c1292
refactor(rfc7239): use the excellent forwarded-parse by @lpinca
Mar 26, 2016
9e54f7a
feat(defaults): default schemas should be rfc7239 and x-forwarded
Mar 26, 2016
7aa4ffe
feat(schemas): add weblogic proxy support
Mar 26, 2016
8cf1117
docs(readme): update docs with details on each schema
Mar 26, 2016
0dc53a6
chore(package): update mocha to version 2.5.1
greenkeeperio-bot May 23, 2016
482dcec
Merge pull request #3 from ahmadnassri/greenkeeper-mocha-2.5.1
May 23, 2016
104f0a5
chore(package): update mocha to version 2.5.3
greenkeeperio-bot May 25, 2016
18e0181
Merge pull request #5 from ahmadnassri/greenkeeper-mocha-2.5.3
Jun 9, 2016
9712f4d
chore(package): update istanbul to version 0.4.3
greenkeeperio-bot Jun 9, 2016
462d7e5
Merge pull request #1 from ahmadnassri/greenkeeper-istanbul-0.4.3
Jun 9, 2016
4b97e71
chore(package): update mocha to version 3.0.1
greenkeeperio-bot Aug 4, 2016
cb066f8
Merge pull request #8 from ahmadnassri/greenkeeper-mocha-3.0.1
Aug 10, 2016
8980fbf
chore: drop support for Node.js 0.10
greenkeeperio-bot Oct 31, 2016
cad75c3
Merge pull request #15 from ahmadnassri/greenkeeper/remove-node-0.10
Nov 1, 2016
bbe1a0d
chore(package): update dependencies
greenkeeper[bot] Nov 7, 2016
43b9fae
Merge pull request #16 from ahmadnassri/greenkeeper/update-all
Nov 7, 2016
7f03ddf
chore(package): update mocha to version 3.2.0
greenkeeper[bot] Nov 25, 2016
f7e83c8
Merge pull request #17 from ahmadnassri/greenkeeper/mocha-3.2.0
Dec 4, 2016
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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ node_js:
- "0.8"
- "0.10"
- "0.11"
- "0.12"
matrix:
allow_failures:
- node_js: "0.11"
Expand Down
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
[![Build Status][travis-image]][travis-url]
[![Test Coverage][coveralls-image]][coveralls-url]

Parse HTTP X-Forwarded-For header
Parse *Forwarded* HTTP headers, using the standard: [RFC 7239](https://tools.ietf.org/html/rfc7239) *(Forwarded HTTP Extension)*, as well as commonly used none-standard headers (e.g. `X-Forwarded-*`, `X-Real-*`, etc ...)

review [`schemas` folder](lib/schemas) for a full list of supported headers schemas.

## Installation

Expand All @@ -20,23 +22,48 @@ $ npm install forwarded
var forwarded = require('forwarded')
```

### forwarded(req)
### forwarded(req[, options])

returns an object who's properties represent [RFC 7239 Parameters (Section 5)](http://tools.ietf.org/html/rfc7239#section-5)

```js
var addresses = forwarded(req)
var result = forwarded(req)
```

Parse the `X-Forwarded-For` header from the request. Returns an array
of the addresses, including the socket address for the `req`. In reverse
order (i.e. index `0` is the socket address and the last index is the
furthest address, typically the end-user).
#### options

| name | type | description | required | default |
| --------- | ------- | ----------------------------------------- | -------- | -------------------- |
| `schemas` | `array` | ordered list of header schemas to process | no | `['xff', 'rfc7239']` |

Parse appropriate headers from the request matching the selected [schemas](#options).

### returned object

| name | type | description | default |
| --------- | --------- | ---------------------------------------------------------------------------------------- | -------------------------------------- |
| `for` | `array` | alias of `addrs` |
| `by` | `string` | [RFC 7239 Section 5.1](http://tools.ietf.org/html/rfc7239#section-5.1) compatible result | `null` |
| `addrs` | `array` | [RFC 7239 Section 5.2](http://tools.ietf.org/html/rfc7239#section-5.2) compatible result | `[request.connection.remoteAddress]` |
| `host` | `string` | [RFC 7239 Section 5.3](http://tools.ietf.org/html/rfc7239#section-5.3) compatible result | `request.headers.host` |
| `proto` | `string` | [RFC 7239 Section 5.4](http://tools.ietf.org/html/rfc7239#section-5.4) compatible result | `request.connection.encrypted` |
| `port` | `string` | the last known port used by the client/proxy in chain of proxies | `request.connection.remotePort` |
| `ports` | `array` | ordered list of known ports in the chain of proxies | `[request.connection.remotePort]` |

###### Notes

- `forwarded().addrs` & `forwarded().ports`: return arrays of the addresses & ports respectively, including the socket address/port for the request. In reverse order (i.e. index `0` is the socket address/port and the last index is the furthest address/port, typically the end-user).

## Testing

```sh
$ npm test
```

## TODO
- [ ] process [`Via`](http://tools.ietf.org/html/rfc7230#section-5.7.1) header
- [ ] extract ports from [`Forwarded`](http://tools.ietf.org/html/rfc7239#section-5.2) header: `Forwarded: for=x.x.x.x:yyyy`

## License

[MIT](LICENSE)
Expand Down
72 changes: 51 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,65 @@
/*!
* forwarded
* Copyright(c) 2014 Douglas Christopher Wilson
* MIT Licensed
*/

/**
* Module exports.
*/
'use strict'

module.exports = forwarded
var Processor = require('./lib/processor')
var schemas = require('./lib/schemas')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to keep these headers so we know where in the source the copyrights lives (and so the note is retained when people copy the file but not the license).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, forgot to re-add those! (mind tends to zone those out!) :)


/**
* Get all addresses in the request, using the `X-Forwarded-For` header.
*
* @param {Object} req
* @param {http.IncomingMessage} req
* @api public
*/

function forwarded(req) {
module.exports = function forwarded (req, options) {
options = options || {}

var opts = {
// default to only common + standard
// array order matters here
schemas: options.schemas || [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo it should only default to a single schema, not multiple. The reason is people are mainly going to be set up for one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that typically in libraries such as this, it should be the standard only (i.e. RFC 7239).

however due to the long history of X-Forwarded-* and the relatively new age of RFC 7239 it would make sense to consider both a standard.

in that light, both are the defacto default here, other scenarios would be:

  • RFC 7239 only: would essentially break every dependant's assumptions
  • X-Forwarded-* only: would not be true to the standard and might even be problematic in the future.

this is precisely why the logic at index.js#L52 overwrites properties in the sorted order of this schemas array, existing implementations of X-Forwarded-* will still operate identical to the past, and when Forwarded header is present, it counts as the authoritative source of information as it is the standard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it counts as the authoritative source of information as it is the standard.

Yes, but this is the biggest issue I have with this assumption: people literally just are not using that header, nor are they blocking it. Because of this, it is extremely easy to fall in a trap where people rely on this module because customers are paying for IP whitelisting. An attacker can then just include a Forwarded header and even those the company's proxies are making sure X-Forwarded-For is correct, they will not have a breach of contract with the customer, as an attacker will include a Forwarded header and suddenly it becomes the authoritative source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically I think the standard should be the default and people can always change it (and we can simply add messaging to the README). I just don't like the default being multiple types, because I can just see the confusion/issues coming (I have a lot of experience with the things people report ;) ). I like the idea of the module letting you use multiple at once, though, if the user chooses.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify are you suggesting to keep RFC 7239 as the default option?

also, another possible option is to not offer a default, leaving it to the implementer to decide what to support.

e.g.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify are you suggesting to keep RFC 7239 as the default option?

Sure that works. Specifically, I'm suggesting that the default option should be only one schema; which one it is really doesn't matter as much, because people will change it.

another possible option is to not offer a default

Well, that is a possibility, yes, but I'm undecided on that (though if you did it, you'd need to move the schemas out of the options argument, since it's no longer optional).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for what proxy-addr or express may do, that's undecided for next majors, though of course for the existing major, they would stick to 'xff'.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved in 8694d1c

'xff',
'rfc7239'
]
}

// consistent case
opts.schemas.map(Function.prototype.call, String.prototype.toLowerCase)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result of map is not being used. Either a mistake or should be forEach.


if (!req) {
throw new TypeError('argument req is required')
}

// simple header parsing
var proxyAddrs = (req.headers['x-forwarded-for'] || '')
.split(/ *, */)
.filter(Boolean)
.reverse()
var socketAddr = req.connection.remoteAddress
var addrs = [socketAddr].concat(proxyAddrs)
// start with default values from socket connection
var forwarded = {
addrs: [req.connection.remoteAddress],
by: null,
host: req.headers && req.headers.host ? req.headers.host : undefined,
port: req.connection.remotePort.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a disconnected socket, req.connection.remotePort is undefined and this will throw. This needs to be handled.

ports: [req.connection.remotePort.toString()],
proto: req.connection.encrypted ? 'https' : 'http'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this actually works in Node.js 0.6 and the others? I only ask because the test is just a mock, so we have to verify manually if we cannot add a real test.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was relying on the travis test to be honest, I managed to manually test all the way down to 0.8, then gave up on getting 0.6 installed on my machine. I'll get that working today and confirm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are only as honest as your tests are--since you are using mocks, then you are not actually testing anything (you have to test a real req for Travis CI to be of any help to you).

}

// alias "for" to keep with RFC7239 naming
forwarded.for = forwarded.addrs

return opts.schemas
// check if schemas exist
.filter(function (name) {
return schemas[name]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown schemas need to throw an error, not be silently dropped. A user making a typo in a schema name may not realize for a while.

})

// process schemas
.reduce(function (forwarded, name) {
var result = new Processor(req, schemas[name])

// return all addresses
return addrs
// update forwarded object
return {
addrs: forwarded.addrs.concat(result.addrs).filter(Boolean),
by: result.by ? result.by : forwarded.by,
host: result.host ? result.host : forwarded.host,
port: result.port ? result.port : forwarded.port,
ports: forwarded.ports.concat([result.port]).filter(Boolean),
proto: result.proto ? result.proto : forwarded.proto
}
}, forwarded)
}
82 changes: 82 additions & 0 deletions lib/processor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use strict'

var debug = require('debug')('forwarded')

function processor (req, schema) {
if (typeof schema === 'function') {
return schema(req)
}

this.req = req
this.schema = schema

return {
addrs: this.addrs(),
host: this.host(),
port: this.port(),
proto: this.protocol()
}
}

processor.prototype.host = function () {
if (this.schema.host && this.req.headers[this.schema.host]) {
var value = this.req.headers[this.schema.host]

debug('found header [%s = %s]', this.schema.host, value)

return this.req.headers[this.schema.host]
}
}

processor.prototype.port = function () {
if (this.schema.port && this.req.headers[this.schema.port]) {
var value = this.req.headers[this.schema.port]

debug('found header [%s = %s]', this.schema.port, value)

return value
}
}

processor.prototype.addrs = function () {
if (this.schema.addrs && this.req.headers[this.schema.addrs]) {
var value = this.req.headers[this.schema.addrs]

debug('found header [%s = %s]', this.schema.addrs, value)

return value
.split(/ *, */)
.filter(Boolean)
.reverse()
}
}

processor.prototype.protocol = function () {
// utility
function runner (obj) {
// multiple possible values
if (Array.isArray(obj)) {
// get the last succesful item
return obj.map(runner.bind(this)).reduce(function (prev, curr) {
return curr ? curr : prev
})
}

if (typeof obj === 'function') {
return obj.call(this, this.req)
}

if (this.req.headers[obj]) {
debug('found header [%s = %s]', obj, this.req.headers[obj])

return this.req.headers[obj]
}
}

// actually run
if (this.schema.proto) {
return runner.call(this, this.schema.proto)
}
}

module.exports = processor
19 changes: 19 additions & 0 deletions lib/schemas/cloudflare.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict'

var debug = require('debug')('forwarded')

function isSecure (req) {
try {
var cf = JSON.parse(req.headers['cf-visitor'])
return cf.scheme !== undefined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is broken; according to https://support.cloudflare.com/hc/en-us/articles/200170536-How-do-I-redirect-HTTPS-traffic-with-Flexible-SSL-and-Apache- the value {"scheme":"http"} is valid and this routine would incorrectly mark the request as secure.

} catch (e) {
debug('could not parse "cf-visitor" header: %s', req.headers['cf-visitor'])
}

return false
}

module.exports = {
addrs: 'cf-connecting-ip',
proto: isSecure
}
11 changes: 11 additions & 0 deletions lib/schemas/fastly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict'

function isSecure (req) {
return ~['1', 'true'].indexOf(req.headers['fastly-ssl'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems according to the docs at https://docs.fastly.com/guides/backend-servers/maintaining-separate-http-and-https-requests-to-backend-servers just the existence of the header with any value is enough to signal SSL. Is there a reason to check the value here?

}

module.exports = {
addrs: 'fastly-client-ip',
port: 'fastly-client-port',
proto: isSecure
}
12 changes: 12 additions & 0 deletions lib/schemas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict'

module.exports = {
cloudflare: require('./cloudflare'),
fastly: require('./fastly'),
microsoft: require('./microsoft'),
nginx: require('./nginx'),
rackspace: require('./rackspace'),
rfc7239: require('./rfc7239'),
xff: require('./xff'),
zscaler: require('./zscaler')
}
7 changes: 7 additions & 0 deletions lib/schemas/microsoft.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = {
proto: function isSecure (req) {
return req.headers['front-end-https'] === 'on'
}
}
7 changes: 7 additions & 0 deletions lib/schemas/nginx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

module.exports = {
addrs: 'x-real-ip',
port: 'x-real-port',
proto: ['x-real-proto', 'x-url-scheme']
}
5 changes: 5 additions & 0 deletions lib/schemas/rackspace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict'

module.exports = {
addrs: 'x-cluster-client-ip'
}
54 changes: 54 additions & 0 deletions lib/schemas/rfc7239.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict'

function splitMap (string, separator, cb) {
// split into elements
return string.split(separator)
.filter(Boolean)
.forEach(cb)
}

function parsePart (part) {
var pair = part.split(/ *= */)

var name = pair[0].toLowerCase()
var value = pair[1]

if (value) {
switch (typeof this[name]) {
case 'undefined':
this[name] = value
break

// convert to array
case 'string':
this[name] = [this[name], value]
break

// append to array
case 'object':
this[name].push(value)
break
}
}
}

var ELEMENT_SEPARATOR = / *; */
var PART_SEPARATOR = / *, */

module.exports = function (req) {
var forwarded = {}
var header = req.headers.forwarded

if (!header) {
return forwarded
}

splitMap(header, ELEMENT_SEPARATOR, function parseElement (el) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this will fail to parse the header Forwarded: for="foo,bar" properly, opening a vector for security issues when relying on the header for access control.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from my reading of RFC 7239 the spec states that each for parameter represents a single address. unless I'm reading this wrong. can you link to or highlight the section that says otherwise?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is irrelevant; an incoming header can contain any values, valid or not. The current implementation will do the wrong thing and this is not good, as the only reason you are parsing these headers is for logging, auditing, or access controls.

Just think of a standard proxy that does not modify anything existing in the Forwarded header and simply adds it's own data to it; it will pass-through the bogus value that will create false parses.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, what I'm saying is that the current parse is actually not RFC 7239 compliant, as you are not correctly following the ABNF, which allows for , and ; to appear within a quoted-string and the defined ABNF for the header is as follows:

Forwarded   = 1#forwarded-element
forwarded-element = [ forwarded-pair ] *( ";" [ forwarded-pair ] )
forwarded-pair = token "=" value
value          = token / quoted-string
token          = 1*tchar
tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
               / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
               / DIGIT / ALPHA
quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qdtext         = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
obs-text       = %x80-FF

The given header, Forwarded: for="foo,bar" is syntactically valid, yet this module will definitely parse it incorrectly.

Because the RFC allows extensions, perhaps your proxy decides it's going to include a custom field of the authenticated user's name. Uh, oh!

# this is in bash, so the "" is an escaped double quote (i.e. it's only a single double quote in the program)
$ node -pe "require('forwarded')({connection:{remotePort:0},headers:{forwarded:'user=""bad,for=_whitelisted_ip"";for=_blacklisted_ip'}})"
{ addrs: [ 'whitelisted_ip', 'blacklisted_ip' ],
  by: null,
  host: undefined,
  port: '0',
  ports: [ '0' ],
  proto: 'http' }

Even though the header is 100% syntactically valid, with a single forwarded entry of {for: '_blacklisted_ip', user: 'bad,for=_whitelisted_ip'}, we end up thinking that the proxy got the request from _whitelisted_ip incorrectly due to the invalid parsing this module did :(

And I'm not even a security expert; I can tell you that if we don't fix the parsing, it will most likely end up as a critical security bug.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation will do the wrong thing

can you clarify what you mean by that?

the token syntax does not actually allow a comma to be present:

     token          = 1*tchar

     tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
                    / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
                    / DIGIT / ALPHA
                    ; any VCHAR, except delimiters
Delimiters are chosen from the set of US-ASCII visual characters not allowed in a token
(DQUOTE and "(),/:;<=>?@[\]{}").

so, if anything, we should discard for="foo,bar" since its not a valid token

verification vs. validation

further, imo, the purpose of this library is to parse the headers, _not to verify values_, the resulting values should be filtered through a secondary layer if the implementer chooses to verify values, such as network addresses, host names, etc ...

which leaves us with the question: should we add validation here?

specifically: to validate addresses, hostnames, ports.


as a side, RFC 7239 node identifier syntax could also include unknown and obfuscated identifiers:

   MUST have a leading underscore "_".  Furthermore, it MUST also
   consist of only "ALPHA", "DIGIT", and the characters ".", "_", and
   "-".

which we can easily validate as well.

return splitMap(el, PART_SEPARATOR, parsePart.bind(forwarded))
})

// re-mapping
forwarded.addrs = forwarded.for

return forwarded
}
13 changes: 13 additions & 0 deletions lib/schemas/xff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict'

function isSecure (req) {
return req.headers['x-forwarded-ssl'] === 'on'
}

module.exports = {
addrs: 'x-forwarded-for',
host: 'x-forwarded-host',
port: 'x-forwarded-port',
proto: ['x-forwarded-proto', 'x-forwarded-protocol', isSecure],
protoFn: isSecure
}
8 changes: 8 additions & 0 deletions lib/schemas/zscaler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

module.exports = {
addrs: 'z-forwarded-for',
host: 'z-forwarded-host',
port: 'z-forwarded-port',
proto: ['z-forwarded-proto', 'z-forwarded-protocol']
}
Loading