├── .editorconfig ├── .gitignore ├── .travis.yml ├── .verb.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Always-ignore dirs # 2 | # #################### 3 | _gh_pages 4 | node_modules 5 | jspm_packages 6 | bower_components 7 | components 8 | vendor 9 | build 10 | dest 11 | src 12 | lib-cov 13 | coverage 14 | nbproject 15 | cache 16 | temp 17 | tmp 18 | express-ip-filter 19 | 20 | # Packages # 21 | # ########## 22 | *.7z 23 | *.dmg 24 | *.gz 25 | *.iso 26 | *.jar 27 | *.rar 28 | *.tar 29 | *.zip 30 | 31 | # OS, Logs and databases # 32 | # ######################### 33 | logs 34 | *.pid 35 | *.dat 36 | *.log 37 | *.sql 38 | *.sqlite 39 | *~ 40 | ~* 41 | 42 | # Another files # 43 | # ############### 44 | Icon? 45 | .DS_Store* 46 | Thumbs.db 47 | ehthumbs.db 48 | Desktop.ini 49 | npm-debug.log 50 | .directory 51 | ._* 52 | lcov.info 53 | 54 | # Runtime data 55 | pids 56 | *.pid 57 | *.seed 58 | *.pid.lock 59 | 60 | 61 | # nyc test coverage 62 | .nyc_output 63 | 64 | # Grunt intermediate storage 65 | # see here: http://gruntjs.com/creating-plugins#storing-task-files 66 | .grunt 67 | 68 | # Optional npm cache directory 69 | .npm 70 | 71 | # Optional REPL history 72 | .node_repl_history 73 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | 4 | node_js: 5 | - "node" 6 | - "6" 7 | - "5" 8 | - "4" 9 | - "0.12" 10 | - "0.10" 11 | 12 | matrix: 13 | fast_finish: true 14 | allow_failures: 15 | - node_js: "4" 16 | - node_js: "0.10" 17 | - node_js: "0.12" 18 | 19 | notifications: 20 | email: false 21 | 22 | after_success: npm run report-coverage 23 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | # [{%= name %}][author-www-url] [![npmjs.com][npmjs-img]][npmjs-url] [![The MIT License][license-img]][license-url] [![npm downloads][downloads-img]][downloads-url] 2 | 3 | > {%= description %} 4 | 5 | [![code climate][codeclimate-img]][codeclimate-url] [![standard code style][standard-img]][standard-url] [![travis build status][travis-img]][travis-url] [![coverage status][coveralls-img]][coveralls-url] [![dependency status][david-img]][david-url] 6 | 7 | ## Install 8 | 9 | ``` 10 | npm i {%= name %} --save 11 | ``` 12 | 13 | ## Features 14 | - custom message when `403 Forbidden` response, through `opts.forbidden` 15 | - custom identifier different than default `this.ip`, through `opts.id` 16 | + you may want to add `opts.strict: false` if it's not IP 17 | - filter IP using glob patterns, regexp, string, array or function 18 | - blacklist with negative glob patterns, whitelist with positive 19 | - would restrict all to `403 Forbidden` that not match to filter 20 | 21 | > **Notice:** In the next middleware you will have `this.filter` method which is [ip-filter][] 22 | > and `this.identifier` - the IP/ID that passed the given filter 23 | 24 | ## Usage 25 | > For more use-cases see the [tests](./test.js) 26 | 27 | ```js 28 | const {%= varname %} = require('{%= name %}') 29 | ``` 30 | 31 | {%= apidocs('index.js') %} 32 | 33 | 34 | ### One more example 35 | > If you want to allow all IPs, but want to restrict only some range 36 | 37 | ```js 38 | 'use strict' 39 | 40 | var express = require('express') 41 | var ipFilter = require('express-ip-filter') 42 | var helloWorld = require('express-hello-world') 43 | 44 | var app = express() 45 | 46 | app 47 | .use(ipFilter({ 48 | forbidden: '403: Get out of here!', 49 | filter: ['*', '!213.15.*'] 50 | })) 51 | .use(helloWorld()) 52 | 53 | app.listen(1234) 54 | console.log('express server start listening on http://localhost:1234') 55 | 56 | // only user with IP starting with `213.15.*` 57 | // will see the message `403: Get out of here!` 58 | ``` 59 | 60 | {% if (verb.related && verb.related.list && verb.related.list.length) { %} 61 | ## Related 62 | {%= related(verb.related.list, {words: 12}) %} 63 | {% } %} 64 | 65 | ## Contributing 66 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/{%= repository %}/issues/new). 67 | But before doing anything, please read the [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines. 68 | 69 | ## [Charlike Make Reagent](http://j.mp/1stW47C) [![new message to charlike][new-message-img]][new-message-url] [![freenode #charlike][freenode-img]][freenode-url] 70 | 71 | [![{%= author.username %}.tk][author-www-img]][author-www-url] [![keybase {%= author.username %}][keybase-img]][keybase-url] [![{%= author.username %} npm][author-npm-img]][author-npm-url] [![{%= author.username %} twitter][author-twitter-img]][author-twitter-url] [![{%= author.username %} github][author-github-img]][author-github-url] 72 | 73 | {%= reflinks(verb.reflinks) %} 74 | 75 | [npmjs-url]: https://www.npmjs.com/package/{%= name %} 76 | [npmjs-img]: https://img.shields.io/npm/v/{%= name %}.svg?label={%= name %} 77 | 78 | [license-url]: https://github.com/{%= repository %}/blob/master/LICENSE 79 | [license-img]: https://img.shields.io/npm/l/{%= name %}.svg 80 | 81 | [downloads-url]: https://www.npmjs.com/package/{%= name %} 82 | [downloads-img]: https://img.shields.io/npm/dm/{%= name %}.svg 83 | 84 | 85 | [codeclimate-url]: https://codeclimate.com/github/{%= repository %} 86 | [codeclimate-img]: https://img.shields.io/codeclimate/github/{%= repository %}.svg 87 | 88 | [travis-url]: https://travis-ci.org/{%= repository %} 89 | [travis-img]: https://img.shields.io/travis/{%= repository %}/master.svg 90 | 91 | [coveralls-url]: https://coveralls.io/r/{%= repository %} 92 | [coveralls-img]: https://img.shields.io/coveralls/{%= repository %}.svg 93 | 94 | [david-url]: https://david-dm.org/{%= repository %} 95 | [david-img]: https://img.shields.io/david/{%= repository %}.svg 96 | 97 | [standard-url]: https://github.com/feross/standard 98 | [standard-img]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg 99 | 100 | 101 | [author-www-url]: http://www.{%= author.username.toLowerCase() %}.tk 102 | [author-www-img]: https://img.shields.io/badge/www-{%= author.username.toLowerCase() %}.tk-fe7d37.svg 103 | 104 | [keybase-url]: https://keybase.io/{%= author.username.toLowerCase() %} 105 | [keybase-img]: https://img.shields.io/badge/keybase-{%= author.username.toLowerCase() %}-8a7967.svg 106 | 107 | [author-npm-url]: https://www.npmjs.com/~{%= author.username.toLowerCase() %} 108 | [author-npm-img]: https://img.shields.io/badge/npm-~{%= author.username.toLowerCase() %}-cb3837.svg 109 | 110 | [author-twitter-url]: https://twitter.com/{%= author.username %} 111 | [author-twitter-img]: https://img.shields.io/badge/twitter-@{%= author.username %}-55acee.svg 112 | 113 | [author-github-url]: https://github.com/{%= author.username %} 114 | [author-github-img]: https://img.shields.io/badge/github-@{%= author.username %}-4183c4.svg 115 | 116 | [freenode-url]: http://webchat.freenode.net/?channels=charlike 117 | [freenode-img]: https://img.shields.io/badge/freenode-%23charlike-5654a4.svg 118 | 119 | [new-message-url]: https://github.com/{%= author.username %}/ama 120 | [new-message-img]: https://img.shields.io/badge/ask%20me-anything-green.svg -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 2.0.0 - 2016-10-07 4 | 5 | **POSSIBLY BREAKING CHANGES** 6 | 7 | Mainly bump to `ip-filter@2` which fixes some wrong behaving. Also you should notice 8 | that signle `*` would not work in some cases, so you should `**`, because basically `ip-filter` 9 | converts IPs to filepaths to be able to use `micromatch` as matching library behind the scenes. 10 | 11 | So one way is to pass such `filter: ['*.*.*.*', '!111.??.244.*']`, another is 12 | the `filter: ['**', '!111.??.244.*']` to match any IPs except `111.22.244.31` for example. 13 | 14 | **MISC STUFF** 15 | 16 | Boilerplate stuff. Update contributing guide, dotfiles, license year, npm scripts, release/publish flow and etc. Replace `assertit` with `mukla` which is drop-in replacement. Using `verb` for generating the README.md and API docs. Travis builds, and flow et all. 17 | 18 | ## 1.0.0 - 2015-06-21 19 | - Release v1.0.0 / npm@v1.0.0 20 | - update tests titles 21 | - add docs 22 | - update readme 23 | - add keywords 24 | - add tests 25 | - add `ip-filter` 26 | - lets get started 27 | - boilerplate 28 | 29 | ## 0.0.0 - 2015-06-20 30 | - first commits -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to express-ip-filter 2 | 3 | :sparkles: Thanks for your contribution in advance! :tada: 4 | 5 | First and foremost, thank you! We appreciate that you want to contribute to `express-ip-filter`, your time is valuable, and your contributions mean a lot to us. 6 | 7 | ## What does "contributing" mean? 8 | 9 | There are many ways to contribute to an open source project, including: 10 | 11 | - Updating or correcting documentation 12 | - Feature requests 13 | - Submitting bug reports 14 | 15 | But you aren't limited to these things. Use your imagination. If you like a project, and you see something that can or should be improved, then you have an opportunity (but not an obligation) to contribute. 16 | 17 | ### Improve documentation 18 | 19 | As a user of `express-ip-filter` you're the perfect candidate to help us improve our documentation. Typo corrections, error fixes, better explanations, more examples, etc. Open issues for things that could be improved. Anything. Even improvements to this document. 20 | 21 | Use the [`docs` label](https://github.com/tunnckoCore/express-ip-filter/labels/docs) to find suggestions for what we'd love to see more documentation on. 22 | 23 | ### Improve issues 24 | 25 | Some issues are created with missing information, not reproducible, or plain invalid. Help make them easier to resolve. Handling issues takes a lot of time that we could rather spend on fixing bugs and adding features. 26 | 27 | ### Give feedback on issues 28 | 29 | We're always looking for more opinions on discussions in the issue tracker. It's a good opportunity to influence the future direction of AVA. 30 | 31 | The [`question` label](https://github.com/tunnckoCore/express-ip-filter/labels/question) is a good place to find ongoing discussions. 32 | 33 | 34 | ## Why should I contribute? 35 | 36 | Regardless of the details, being an effective contributor means that you're adding _adding value_ to a project. 37 | 38 | Here are just a few of the advantages of adding value to a project: 39 | 40 | - you gain the appreciation and respect of the project's maintainers and community 41 | - you gain valuable experience 42 | - you get noticed by job recruiters 43 | - you become more attrative to potential employers. 44 | 45 | ## Getting familiarized with a project 46 | 47 | Before you attempt to contribute to a project, take a moment to get familiarized with it. In most cases you can learn all you need to know within a couple of minutes. 48 | 49 | ### Required 50 | 51 | The following items are a pre-requisite for contributing to any project. Avoid creating issues or doing pull requests until you've done all of these things: 52 | 53 | - **Review the readme**: Oftentimes a project readme has links to documentation, advice on creating issues or bug reports, and so on. 54 | - **Read contributing guidelines**: look for a `CONTRIBUTING.md` file and, if one exists, read it in its entirety before creating issues or doing a pull request. Typically this is in the root of the project, but it might be in `.github/CONTRIBUTING.md`. 55 | - **Search issues**: Before creating bug reports, feature requests, or submitting issues of any kind, you should always search for existing issues (closed or open) that address the same thing. 56 | 57 | ### Recommended 58 | 59 | - **Review unit tests** - one of the best ways to get familiarized with a project is through its unit tests. Of course, this depends on the type of project, complexity, test coverage, and so on. But when applicable, test are often a good source of insight. 60 | - **Get familiarized with the code** - If the codebase is small, and you're familiar with the language, take a moment to review the code to see if you find anything that can be improved. If the codebase is large, you might be able to provide domain expertise or fixes for specific areas. 61 | - **Ask questions** - Depending the project type and size, it might be good to start by searching google to find anwers to your questions. Then, check to see if the project uses [gitter](https://gitter.im) or has a [slack](https://slack.com) channel, or something similar. Also visit [stackoverflow](https://stackoverflow.com) and do a search to see if others have already asked the same question. As a last resort, create an issue on the project's GitHub repository. 62 | 63 | 64 | ## Details of Highly Effective Bug Reports 65 | 66 | ### Rationale 67 | 68 | The easier you make it for a maintainter or members of the community to react, the more likely it is for them to react quickly. 69 | 70 | Like you, maintainers have to make decisions about where to spend their time. Not only within a given project, but oftentimes across multiple projects. If you're experiencing a bug and you want to make a report, bug reports that are clearly described and organized are much more likely to get addressed by the maintainers or member of the community. 71 | 72 | Providing these details up front will make everyone happy. If you don't provide these details, maintainers will have to ask you for them, which can be annoying for experienced maintainers who have had to ask for these crucial details many times. 73 | 74 | ### The details 75 | 76 | Always include the following essential details in every bug report: 77 | 78 | 1. **version**: what version of `express-ip-filter` were you using when you experienced the bug? 79 | 2. **description**: clear description of the bug, and minimum steps to reproduce it. 80 | 3. **error messages**: paste any error messages into the issue or a [github gist](https://gist.github.com/), use [gfm code blocks][gfm]. 81 | 4. **code**: paste any code necessary for reproducing the bug and use [gfm code blocks][gfm] to wrap the code. 82 | 5. **title**: use a clear and descriptive title. 83 | 84 | See GitHub's guide to [Creating and highlighting code blocks][gfm] for more details. 85 | 86 | ## Submitting a pull requests 87 | 88 | **Working on your first Pull Request?** 89 | 90 | You can learn how from this *free* video series ["How to Contribute to an Open Source Project on GitHub"][howto-oss-github] 91 | 92 | **Details** 93 | 94 | - Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work. 95 | - For ambitious tasks, you should try to get your work in front of the community for feedback as soon as possible. Open a pull request as soon as you have done the minimum needed to demonstrate your idea. At this early stage, don't worry about making things perfect, or 100% complete. Add a [WIP] prefix to the title, and describe what you still need to do. This lets reviewers know not to nit-pick small details or point out improvements you already know you need to make. 96 | - New features should be accompanied with tests and documentation. 97 | - Don't include unrelated changes. 98 | - Lint and test immediately after you fork by running `$ npm test`. 99 | - Lint and test before submitting the pull request by running `$ npm test`. 100 | - Make the pull request from a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches), not master. 101 | - Use a clear and descriptive title for the pull request and commits. 102 | - Write a convincing description of why we should land your pull request. It's your job to convince us. Answer "why" it's needed and provide use-cases. 103 | - You might be asked to do changes to your pull request. There's never a need to open another pull request. [Just update the existing one.][amending] 104 | 105 | ## Other ways to contribute 106 | 107 | ### Show your support 108 | 109 | Sometimes we find a project we like but just don't have time to contribute. That's okay, there are other ways to show support: 110 | 111 | - Star the project 112 | - Tweet about it 113 | - Tell your friends 114 | 115 | ### Show your appreciation 116 | 117 | Maintainers are people too. You can make someone's day by letting them know you appreciate their work. If you use a library in one of your own projects, let the author know you care: 118 | 119 | - Add a link to the project on your project's readme 120 | - Say "thanks" on twitter 121 | 122 | ## Attribution 123 | 124 | This document is adapted from a few Contributing Guides. It is more general and can apply in most cases. Everyone is free to re-use it or re-adapt it. 125 | 126 | ### Good to read 127 | 128 | - [Awesome Contributing][awesomelist] 129 | - [Idiomatic Contributing][idiomatic] 130 | - [AVA's Contributing Guide][avajs] 131 | - [Amending a commit Guide][amending] 132 | - [Creating and highlighting code blocks][gfm] 133 | - [Contributing to Open Source (GitHub)][os-on-github] 134 | - [How to contribute to Open Source Project (Egghead.io videos)][howto-oss-github] 135 | 136 | ### Authors 137 | 138 | **Charlike Mike Reagent** 139 | 140 | * [github/tunnckoCore](https://github.com/tunnckoCore) 141 | * [twitter/tunnckoCore](http://twitter.com/tunnckoCore) 142 | 143 | ## License 144 | 145 | Released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/). 146 | Copyright © 2016, [Charlike Mike Reagent](http://www.tunnckocore.tk). 147 | 148 | [gfm]: https://help.github.com/articles/creating-and-highlighting-code-blocks/ 149 | [avajs]: https://github.com/avajs/ava/blob/master/contributing.md 150 | [idiomatic]: https://github.com/jonschlinkert/idiomatic-contributing 151 | [awesomelist]: https://github.com/jonschlinkert/awesome-contributing 152 | [amending]: https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md 153 | [os-on-github]: https://guides.github.com/activities/contributing-to-open-source/ 154 | [howto-oss-github]: http://j.mp/how-to-contrib-on-github -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Charlike Mike Reagent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [express-ip-filter][author-www-url] [![npmjs.com][npmjs-img]][npmjs-url] [![The MIT License][license-img]][license-url] [![npm downloads][downloads-img]][downloads-url] 2 | 3 | > Middleware for [express][] that filters IPs against glob patterns, RegExp, string or array of globs. Support custom `403 Forbidden` message, blacklists, whitelists and custom ID. 4 | 5 | [![code climate][codeclimate-img]][codeclimate-url] [![standard code style][standard-img]][standard-url] [![travis build status][travis-img]][travis-url] [![coverage status][coveralls-img]][coveralls-url] [![dependency status][david-img]][david-url] 6 | 7 | ## Install 8 | 9 | ``` 10 | npm i express-ip-filter --save 11 | ``` 12 | 13 | ## Features 14 | - custom message when `403 Forbidden` response, through `opts.forbidden` 15 | - custom identifier different than default `this.ip`, through `opts.id` 16 | + you may want to add `opts.strict: false` if it's not IP 17 | - filter IP using glob patterns, regexp, string, array or function 18 | - blacklist with negative glob patterns, whitelist with positive 19 | - would restrict all to `403 Forbidden` that not match to filter 20 | 21 | > **Notice:** In the next middleware you will have `this.filter` method which is [ip-filter][] 22 | > and `this.identifier` - the IP/ID that passed the given filter 23 | 24 | ## Usage 25 | > For more use-cases see the [tests](./test.js) 26 | 27 | ```js 28 | const expressIpFilter = require('express-ip-filter') 29 | ``` 30 | 31 | ### [expressIpFilter](index.js#L51) 32 | > Filtering incoming request with glob patterns array, regexp, string or matcher function 33 | 34 | **Params** 35 | 36 | * `options` **{Object}** 37 | - `id` **{Function}**: custom identifier, defaults to `this.ip` 38 | - `strict` **{Boolean}**: to throw when not valid IPv4/IPv6? default `true` 39 | - `filter` **{Array|String|RegExp|Function}**: black/white list filter 40 | - `forbidden` **{String|Function}**: custom message when `403 Forbidden` response 41 | * `returns` **{Function}** 42 | 43 | **Example** 44 | 45 | ```js 46 | 'use strict' 47 | 48 | var express = require('express') 49 | var ipFilter = require('express-ip-filter') 50 | var helloWorld = require('express-hello-world') 51 | 52 | var app = express() 53 | 54 | app 55 | .use(ipFilter({ 56 | forbidden: '403: Get out of here!', 57 | filter: ['127.??.6*.12', '!1.2.*.4'] 58 | })) 59 | .use(helloWorld()) 60 | 61 | app.listen(1234) 62 | console.log('express server start listening on http://localhost:1234') 63 | 64 | // if your IP is `127.43.65.12` you will see `Hello World` 65 | // otherwise you will see `403: Get out of here!` 66 | ``` 67 | 68 | ### One more example 69 | > If you want to allow all IPs, but want to restrict only some range 70 | 71 | ```js 72 | 'use strict' 73 | 74 | var express = require('express') 75 | var ipFilter = require('express-ip-filter') 76 | var helloWorld = require('express-hello-world') 77 | 78 | var app = express() 79 | 80 | app 81 | .use(ipFilter({ 82 | forbidden: '403: Get out of here!', 83 | filter: ['*', '!213.15.*'] 84 | })) 85 | .use(helloWorld()) 86 | 87 | app.listen(1234) 88 | console.log('express server start listening on http://localhost:1234') 89 | 90 | // only user with IP starting with `213.15.*` 91 | // will see the message `403: Get out of here!` 92 | ``` 93 | 94 | ## Related 95 | - [ip-filter](https://www.npmjs.com/package/ip-filter): Validates valid IPs (IPv4 and IPv6) using [micromatch][] - glob patterns, RegExp… [more](https://github.com/tunnckocore/ip-filter#readme) | [homepage](https://github.com/tunnckocore/ip-filter#readme "Validates valid IPs (IPv4 and IPv6) using [micromatch][] - glob patterns, RegExp, string or array of globs. If match returns the IP, otherwise null.") 96 | - [is-match-ip](https://www.npmjs.com/package/is-match-ip): Matching IPs using [micromatch][] and [ip-filter][] - glob patterns, RegExp, string or… [more](https://github.com/tunnckocore/is-match-ip#readme) | [homepage](https://github.com/tunnckocore/is-match-ip#readme "Matching IPs using [micromatch][] and [ip-filter][] - glob patterns, RegExp, string or array of globs. Returns matcher function.") 97 | - [is-match](https://www.npmjs.com/package/is-match): Create a matching function from a glob pattern, regex, string, array, object… [more](https://github.com/jonschlinkert/is-match) | [homepage](https://github.com/jonschlinkert/is-match "Create a matching function from a glob pattern, regex, string, array, object or function.") 98 | - [koa-better-body](https://www.npmjs.com/package/koa-better-body): Full-featured [koa][] body parser! Support parsing text, buffer, json, json patch, json… [more](https://github.com/tunnckocore/koa-better-body#readme) | [homepage](https://github.com/tunnckocore/koa-better-body#readme "Full-featured [koa][] body parser! Support parsing text, buffer, json, json patch, json api, csp-report, multipart, form and urlencoded bodies. Works for koa@1, koa@2 and will work for koa@3.") 99 | - [koa-ip-filter](https://www.npmjs.com/package/koa-ip-filter): Middleware for [koa][] that filters IPs against glob patterns, RegExp, string or… [more](https://github.com/tunnckocore/koa-ip-filter#readme) | [homepage](https://github.com/tunnckocore/koa-ip-filter#readme "Middleware for [koa][] that filters IPs against glob patterns, RegExp, string or array of globs. Support custom `403 Forbidden` message and custom ID.") 100 | 101 | ## Contributing 102 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/tunnckoCore/express-ip-filter/issues/new). 103 | But before doing anything, please read the [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines. 104 | 105 | ## [Charlike Make Reagent](http://j.mp/1stW47C) [![new message to charlike][new-message-img]][new-message-url] [![freenode #charlike][freenode-img]][freenode-url] 106 | 107 | [![tunnckoCore.tk][author-www-img]][author-www-url] [![keybase tunnckoCore][keybase-img]][keybase-url] [![tunnckoCore npm][author-npm-img]][author-npm-url] [![tunnckoCore twitter][author-twitter-img]][author-twitter-url] [![tunnckoCore github][author-github-img]][author-github-url] 108 | 109 | [express]: http://expressjs.com/ 110 | [ip-filter]: https://github.com/tunnckocore/ip-filter 111 | [koa]: https://github.com/koajs/koa 112 | [micromatch]: https://github.com/jonschlinkert/micromatch 113 | 114 | [npmjs-url]: https://www.npmjs.com/package/express-ip-filter 115 | [npmjs-img]: https://img.shields.io/npm/v/express-ip-filter.svg?label=express-ip-filter 116 | 117 | [license-url]: https://github.com/tunnckoCore/express-ip-filter/blob/master/LICENSE 118 | [license-img]: https://img.shields.io/npm/l/express-ip-filter.svg 119 | 120 | [downloads-url]: https://www.npmjs.com/package/express-ip-filter 121 | [downloads-img]: https://img.shields.io/npm/dm/express-ip-filter.svg 122 | 123 | [codeclimate-url]: https://codeclimate.com/github/tunnckoCore/express-ip-filter 124 | [codeclimate-img]: https://img.shields.io/codeclimate/github/tunnckoCore/express-ip-filter.svg 125 | 126 | [travis-url]: https://travis-ci.org/tunnckoCore/express-ip-filter 127 | [travis-img]: https://img.shields.io/travis/tunnckoCore/express-ip-filter/master.svg 128 | 129 | [coveralls-url]: https://coveralls.io/r/tunnckoCore/express-ip-filter 130 | [coveralls-img]: https://img.shields.io/coveralls/tunnckoCore/express-ip-filter.svg 131 | 132 | [david-url]: https://david-dm.org/tunnckoCore/express-ip-filter 133 | [david-img]: https://img.shields.io/david/tunnckoCore/express-ip-filter.svg 134 | 135 | [standard-url]: https://github.com/feross/standard 136 | [standard-img]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg 137 | 138 | [author-www-url]: http://www.tunnckocore.tk 139 | [author-www-img]: https://img.shields.io/badge/www-tunnckocore.tk-fe7d37.svg 140 | 141 | [keybase-url]: https://keybase.io/tunnckocore 142 | [keybase-img]: https://img.shields.io/badge/keybase-tunnckocore-8a7967.svg 143 | 144 | [author-npm-url]: https://www.npmjs.com/~tunnckocore 145 | [author-npm-img]: https://img.shields.io/badge/npm-~tunnckocore-cb3837.svg 146 | 147 | [author-twitter-url]: https://twitter.com/tunnckoCore 148 | [author-twitter-img]: https://img.shields.io/badge/twitter-@tunnckoCore-55acee.svg 149 | 150 | [author-github-url]: https://github.com/tunnckoCore 151 | [author-github-img]: https://img.shields.io/badge/github-@tunnckoCore-4183c4.svg 152 | 153 | [freenode-url]: http://webchat.freenode.net/?channels=charlike 154 | [freenode-img]: https://img.shields.io/badge/freenode-%23charlike-5654a4.svg 155 | 156 | [new-message-url]: https://github.com/tunnckoCore/ama 157 | [new-message-img]: https://img.shields.io/badge/ask%20me-anything-green.svg 158 | 159 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express-ip-filter 3 | * 4 | * Copyright (c) 2015-2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk) 5 | * Released under the MIT license. 6 | */ 7 | 8 | 'use strict' 9 | 10 | var ipFilter = require('ip-filter') 11 | 12 | /** 13 | * > Filtering incoming request with glob patterns 14 | * array, regexp, string or matcher function 15 | * 16 | * **Example** 17 | * 18 | * ```js 19 | * 'use strict' 20 | * 21 | * var express = require('express') 22 | * var ipFilter = require('express-ip-filter') 23 | * var helloWorld = require('express-hello-world') 24 | * 25 | * var app = express() 26 | * 27 | * app 28 | * .use(ipFilter({ 29 | * forbidden: '403: Get out of here!', 30 | * filter: ['127.??.6*.12', '!1.2.*.4'] 31 | * })) 32 | * .use(helloWorld()) 33 | * 34 | * app.listen(1234) 35 | * console.log('express server start listening on http://localhost:1234') 36 | * 37 | * // if your IP is `127.43.65.12` you will see `Hello World` 38 | * // otherwise you will see `403: Get out of here!` 39 | * ``` 40 | * 41 | * @name expressIpFilter 42 | * @param {Object} `options` 43 | * @option {Function} [options] `id` custom identifier, defaults to `this.ip` 44 | * @option {Boolean} [options] `strict` to throw when not valid IPv4/IPv6? default `true` 45 | * @option {Array|String|RegExp|Function} [options] `filter` black/white list filter 46 | * @option {String|Function} [options] `forbidden` custom message when `403 Forbidden` response 47 | * @return {Function} 48 | * @api public 49 | */ 50 | 51 | module.exports = function expressIpFilter (options) { 52 | options = typeof options === 'object' ? options : {} 53 | 54 | return function (req, res, next) { 55 | var id = typeof options.id === 'function' ? options.id.call(this, req, res) : req.ip 56 | 57 | if (!id || !options.filter) { 58 | next() 59 | return 60 | } 61 | 62 | var forbidden = options.forbidden || '403 Forbidden' 63 | 64 | var identifier = ipFilter(id, options.filter, options) 65 | if (identifier === null) { 66 | var body = typeof forbidden === 'function' ? forbidden.call(this, req, res) : forbidden 67 | res.status(403).send(body) 68 | return 69 | } 70 | 71 | res.filter = ipFilter 72 | res.identifier = identifier 73 | 74 | next() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-ip-filter", 3 | "version": "2.0.0", 4 | "description": "Middleware for [express][] that filters IPs against glob patterns, RegExp, string or array of globs. Support custom `403 Forbidden` message, blacklists, whitelists and custom ID.", 5 | "repository": "tunnckoCore/express-ip-filter", 6 | "author": "Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)", 7 | "precommit.silent": true, 8 | "main": "index.js", 9 | "license": "MIT", 10 | "scripts": { 11 | "lint": "standard --verbose", 12 | "pretest": "npm run lint", 13 | "test": "npm run coverage", 14 | "posttest": "npm run lint:coverage", 15 | "coverage": "nyc node test.js", 16 | "lint:coverage": "nyc check-coverage --lines 100 --branches 100 --statements 100 --functions 100", 17 | "report-coverage": "nyc report --reporter=text-lcov | coveralls", 18 | "prerelease": "npm test", 19 | "release": "standard-version --sign --no-verify", 20 | "precommit": "git add --all", 21 | "commit": "git-cz" 22 | }, 23 | "dependencies": { 24 | "ip-filter": "^2.0.0" 25 | }, 26 | "devDependencies": { 27 | "commitizen": "^2.8.6", 28 | "coveralls": "^2.11.12", 29 | "cz-conventional-changelog": "^1.2.0", 30 | "express": "^4.14.0", 31 | "express-hello-world": "^1.0.0", 32 | "mukla": "^0.4.1", 33 | "nyc": "^8.1.0", 34 | "pre-commit": "^1.1.3", 35 | "standard": "^8.0.0", 36 | "standard-version": "^2.4.0", 37 | "supertest": "^2.0.0" 38 | }, 39 | "files": [ 40 | "index.js" 41 | ], 42 | "keywords": [ 43 | "array", 44 | "blacklist", 45 | "custom", 46 | "express", 47 | "expressjs", 48 | "filter", 49 | "function", 50 | "glob", 51 | "id", 52 | "ip", 53 | "ip-filter", 54 | "ips", 55 | "limit", 56 | "limiter", 57 | "match", 58 | "matcher", 59 | "micromatch", 60 | "middleware", 61 | "minimatch", 62 | "options", 63 | "pattern", 64 | "regex", 65 | "regexp", 66 | "string", 67 | "support", 68 | "whitelist" 69 | ], 70 | "config": { 71 | "commitizen": { 72 | "path": "./node_modules/cz-conventional-changelog" 73 | } 74 | }, 75 | "verb": { 76 | "run": true, 77 | "toc": false, 78 | "layout": "empty", 79 | "tasks": [ 80 | "readme" 81 | ], 82 | "related": { 83 | "list": [ 84 | "is-match", 85 | "is-match-ip", 86 | "ip-filter", 87 | "koa-ip-filter", 88 | "koa-better-body" 89 | ] 90 | }, 91 | "lint": { 92 | "reflinks": true 93 | }, 94 | "reflinks": [ 95 | "express", 96 | "ip-filter", 97 | "koa", 98 | "micromatch" 99 | ] 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express-ip-filter 3 | * 4 | * Copyright (c) 2015-2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk) 5 | * Released under the MIT license. 6 | */ 7 | 8 | /* jshint asi:true */ 9 | 10 | 'use strict' 11 | 12 | var test = require('mukla') 13 | var ipFilter = require('./index') 14 | var request = require('supertest') 15 | var helloWorld = require('express-hello-world') 16 | var express = require('express') 17 | 18 | function middleware (fn) { 19 | return express().use(fn).use(helloWorld()) 20 | } 21 | 22 | test('should yield next middleware if no `opts.filter` given', function (done) { 23 | var server = middleware(ipFilter()) 24 | 25 | request(server) 26 | .get('/') 27 | .expect(200, 'Hello World') 28 | .end(done) 29 | }) 30 | test('should have `opts.id` and it should be binded to koa this', function (done) { 31 | var server = middleware(ipFilter({ 32 | id: function _id_ (req, res) { 33 | res.set('x-github-username', 'tunnckoCore') 34 | return req.get('x-expressip') 35 | } 36 | })) 37 | 38 | request(server) 39 | .get('/') 40 | .expect('x-github-username', 'tunnckoCore') 41 | .expect(200) 42 | .expect('Hello World') 43 | .end(done) 44 | }) 45 | test('should `403 Forbidden` if not match to `opts.filter`', function (done) { 46 | var server = middleware(ipFilter({ 47 | id: function (req) { 48 | return req.get('x-expressip') 49 | }, 50 | filter: '1.2.3.*' 51 | })) 52 | 53 | request(server) 54 | .get('/').set('x-expressip', '4.4.8.8') 55 | .expect('403 Forbidden') 56 | .expect(403) 57 | .end(done) 58 | }) 59 | test('should `403 Forbidden` if IP is in blacklist', function (done) { 60 | var server = middleware(ipFilter({ 61 | id: function (req) { 62 | return req.get('x-expressip') 63 | }, 64 | filter: ['*', '!89.???.30.*'] 65 | })) 66 | 67 | request(server) 68 | .get('/') 69 | .set('x-expressip', '89.111.30.8') 70 | .expect('403 Forbidden') 71 | .expect(403, done) 72 | }) 73 | test('should `200 OK` if not in blacklist range', function (done) { 74 | request(middleware(ipFilter({ 75 | id: function (req) { 76 | return req.get('x-expressip') 77 | }, 78 | filter: ['**', '!89.???.30.*'] 79 | }))) 80 | .get('/').set('x-expressip', '4.4.8.8') 81 | .expect(200, 'Hello World') 82 | .end(done) 83 | }) 84 | test('should support custom message for 403 Forbidden', function (done) { 85 | var server = middleware(ipFilter({ 86 | id: function (req) { 87 | return req.get('x-envip') !== 'bar' || req.get('x-fooip') 88 | }, 89 | filter: ['*.*.*.*', '!89.???.30.*'], 90 | forbidden: '403, Get out of here!' 91 | })) 92 | 93 | request(server) 94 | .get('/foobar') 95 | .set('x-envip', 'bar') 96 | .set('x-fooip', '89.111.30.8') 97 | .expect(403) 98 | .expect('403, Get out of here!') 99 | .end(done) 100 | }) 101 | test('should be able `opts.forbidden` to be function', function (done) { 102 | var server = middleware(ipFilter({ 103 | id: function (req) { 104 | return req.get('x-expressip') 105 | }, 106 | filter: '123.225.23.120', 107 | forbidden: function (req, res) { 108 | res.set('X-Forbidden', 'Can be function') 109 | res.set('X-Seriously', 'yes') 110 | return 'opts.forbidden can be function' 111 | } 112 | })) 113 | 114 | request(server) 115 | .get('/') 116 | .set('x-expressip', '55.55.55.55') 117 | .expect('X-Forbidden', 'Can be function') 118 | .expect('X-Seriously', 'yes') 119 | .expect(403, 'opts.forbidden can be function') 120 | .end(done) 121 | }) 122 | test('should have `res.filter` and `res.identifier` in next middleware', function (done) { 123 | var ok = false 124 | var server = express() 125 | .use(ipFilter({ 126 | filter: ['**', '!213.15.*'] 127 | })) 128 | .use(helloWorld()) 129 | .use(function (req, res, next) { 130 | test.ok(res.filter, 'should have `res.filter` in next') 131 | test.ok(res.identifier, 'should have `res.identifier` in next') 132 | test.equal(typeof res.filter, 'function', 'should have `res.filter` method') 133 | test.equal(typeof res.identifier, 'string', 'should have `res.identifier`') 134 | ok = true 135 | next() 136 | }) 137 | 138 | request(server) 139 | .get('/') 140 | .set('x-expressip', '7.7.7.7') 141 | .expect(200, 'Hello World') 142 | .end(function (err) { 143 | test.ifError(err) 144 | test.equal(ok, true) 145 | done() 146 | }) 147 | }) 148 | test('should not have `res.filter` if no `opts.filter` given', function (done) { 149 | var ok = false 150 | var server = express().use(ipFilter()).use(helloWorld()).use(function (req, res, next) { 151 | test.ok(!res.filter, 'should not have `res.filter` in next') 152 | test.ok(!res.identifier, 'should not have `res.identifier` in next') 153 | ok = true 154 | next() 155 | }) 156 | 157 | request(server) 158 | .get('/') 159 | .expect(200, 'Hello World') 160 | .end(function (err) { 161 | test.ifError(err) 162 | test.equal(ok, true) 163 | done() 164 | }) 165 | }) 166 | --------------------------------------------------------------------------------