├── .github └── workflows │ └── release.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── browser.js ├── index.js ├── package-lock.json ├── package.json ├── renovate.json └── test └── modules ├── ava-test.js ├── bullet-catcher-test.js ├── dependencies-test.js └── dev-dependencies-test.js /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build, Test, and Publish on release 2 | on: 3 | release: 4 | types: [published] 5 | branches: [master] 6 | jobs: 7 | build: 8 | name: Build, test and publish 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: 12 15 | registry-url: 'https://registry.npmjs.org' 16 | - run: npm install 17 | - run: npm test 18 | - run: npm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea 3 | .vs 4 | .vscode 5 | 6 | # Mac OS 7 | .DS_Store 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (http://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # Typescript v1 declaration files 48 | typings/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | 68 | # Local 69 | ossl 70 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | after_success: 5 | - npm run coveralls 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Geir Gåsodden 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 | [![Build Status](https://travis-ci.org/zrrrzzt/bullet-catcher.svg?branch=master)](https://travis-ci.org/zrrrzzt/bullet-catcher) 2 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 3 | 4 | # bullet-catcher 5 | 6 | Restrict put on a [gun](https://github.com/amark/gun) server 7 | 8 | This module will listen to ```in``` and run your supplied validation function against all `put` messages. 9 | 10 | ## Usage 11 | 12 | Install `bullet-catcher` from npm 13 | 14 | ```bash 15 | $ npm i bullet-catcher 16 | ``` 17 | 18 | Require `bullet-catcher` in your gun server 19 | 20 | ```JavaScript 21 | const Gun = require('gun') 22 | // MUST be required after Gun to work 23 | require('bullet-catcher') 24 | 25 | // This is an example validation function 26 | function hasValidToken (msg) { 27 | return msg && msg && msg.headers && msg.headers.token && msg.headers.token === 'thisIsTheTokenForReals' 28 | } 29 | 30 | const server = require('http').createServer(Gun.serve(__dirname)); 31 | 32 | // Pass the validation function as isValid 33 | const gun = Gun({ 34 | file: 'data.json', 35 | web: server, 36 | isValid: hasValidToken 37 | }) 38 | 39 | // Sync everything 40 | gun.on('out', {get: {'#': {'*': ''}}}) 41 | 42 | server.listen(8000) 43 | ``` 44 | 45 | On your client add a listener for outgoing messages and enrich them with whatever you want to validate for 46 | 47 | ```JavaScript 48 | Gun.on('opt', function (ctx) { 49 | if (ctx.once) { 50 | return 51 | } 52 | ctx.on('out', function (msg) { 53 | var to = this.to 54 | // Adds headers for put 55 | msg.headers = { 56 | token: 'thisIsTheTokenForReals' 57 | } 58 | to.next(msg) // pass to next middleware 59 | }) 60 | }) 61 | ``` 62 | 63 | ## Related 64 | 65 | - [gun-restrict-examples](https://github.com/zrrrzzt/gun-restrict-examples) 66 | 67 | ## License 68 | 69 | [MIT](LICENSE) 70 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | const Gun = require('gun') 2 | const isFn = require('is-fn') 3 | 4 | // Add listener 5 | Gun.on('opt', function (context) { 6 | if (context.once) { 7 | return 8 | } 9 | // Pass to subsequent opt handlers 10 | this.to.next(context) 11 | 12 | const { isValid } = context.opt 13 | 14 | if (!isValid) { 15 | throw new Error('you must pass in an isValid function') 16 | } 17 | 18 | if (!isFn(isValid)) { 19 | throw new Error('isValid must be a function') 20 | } 21 | 22 | // Check all incoming traffic 23 | context.on('in', function (msg) { 24 | var to = this.to 25 | // restrict put 26 | if (msg.put) { 27 | if (isValid(msg)) { 28 | to.next(msg) 29 | } 30 | } else { 31 | to.next(msg) 32 | } 33 | }) 34 | }) 35 | 36 | module.exports = Gun 37 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Gun = require('gun/gun') 2 | const isFn = require('is-fn') 3 | 4 | // Add listener 5 | Gun.on('opt', function (context) { 6 | if (context.once) { 7 | return 8 | } 9 | // Pass to subsequent opt handlers 10 | this.to.next(context) 11 | 12 | const { isValid } = context.opt 13 | 14 | if (!isValid) { 15 | throw new Error('you must pass in an isValid function') 16 | } 17 | 18 | if (!isFn(isValid)) { 19 | throw new Error('isValid must be a function') 20 | } 21 | 22 | // Check all incoming traffic 23 | context.on('in', function (msg) { 24 | var to = this.to 25 | // restrict put 26 | if (msg.put) { 27 | if (isValid(msg)) { 28 | to.next(msg) 29 | } 30 | } else { 31 | to.next(msg) 32 | } 33 | }) 34 | }) 35 | 36 | module.exports = Gun 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bullet-catcher", 3 | "version": "3.0.5", 4 | "description": "Restrict put on gun server", 5 | "main": "index.js", 6 | "browser": "browser.js", 7 | "scripts": { 8 | "test": "standard && ava", 9 | "coverage": "nyc ava", 10 | "coveralls": "nyc ava && nyc report --reporter=lcov && cat coverage/lcov.info | coveralls", 11 | "standard-fix": "standard --fix", 12 | "refresh": "rm -rf node_modules && rm package-lock.json && npm install" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/zrrrzzt/bullet-catcher.git" 17 | }, 18 | "keywords": [], 19 | "author": "Geir Gåsodden (https://github.com/zrrrzzt)", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/zrrrzzt/bullet-catcher/issues" 23 | }, 24 | "homepage": "https://github.com/zrrrzzt/bullet-catcher#readme", 25 | "engines": { 26 | "node": ">=12.16.3" 27 | }, 28 | "devDependencies": { 29 | "ava": "3.15.0", 30 | "coveralls": "3.1.1", 31 | "gun": "0.2020.1235", 32 | "nyc": "15.1.0", 33 | "standard": "16.0.4" 34 | }, 35 | "peerDependencies": { 36 | "gun": "0.2020.1235" 37 | }, 38 | "dependencies": { 39 | "is-fn": "3.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>zrrrzzt/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/modules/ava-test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | 3 | test('basic check', t => { 4 | t.true(true, 'ava works ok') 5 | }) 6 | -------------------------------------------------------------------------------- /test/modules/bullet-catcher-test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const Gun = require('gun/gun') 3 | require('../../index') 4 | 5 | test('It requires isValid to be supplied', t => { 6 | const error = t.throws(() => { 7 | const gun = Gun() // eslint-disable-line 8 | }) 9 | t.is(error.message, 'you must pass in an isValid function') 10 | }) 11 | 12 | test('It requires isValid to be a function and not a string', t => { 13 | const error = t.throws(() => { 14 | const options = { 15 | isValid: 'nope' 16 | } 17 | const gun = Gun(options) // eslint-disable-line 18 | }) 19 | t.is(error.message, 'isValid must be a function') 20 | }) 21 | 22 | test('It requires isValid to be a function', t => { 23 | const gun = Gun({ 24 | isValid: () => { 25 | return true 26 | } 27 | }) 28 | t.truthy(gun, 'Seems legit') 29 | }) 30 | -------------------------------------------------------------------------------- /test/modules/dependencies-test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const pkg = require('../../package.json') 3 | const dependencies = pkg.dependencies || {} 4 | const dropModules = [] 5 | const isDropped = (module) => !dropModules.includes(module) 6 | 7 | if (Object.keys(dependencies).length > 0) { 8 | Object.keys(dependencies).filter(isDropped).forEach((dependency) => { 9 | test(`${dependency} loads ok`, t => { 10 | const module = require(dependency) 11 | t.truthy(module) 12 | }) 13 | }) 14 | } else { 15 | test('no dependecies to test', t => { 16 | t.truthy(true) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /test/modules/dev-dependencies-test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const pkg = require('../../package.json') 3 | const dependencies = pkg.devDependencies || {} 4 | const dropModules = ['nsp'] 5 | const isDropped = (module) => !dropModules.includes(module) 6 | 7 | if (Object.keys(dependencies).length > 0) { 8 | Object.keys(dependencies).filter(isDropped).forEach((dependency) => { 9 | test(`${dependency} loads ok`, t => { 10 | const module = require(dependency) 11 | t.truthy(module) 12 | }) 13 | }) 14 | } else { 15 | test('no dev-dependecies to test', t => { 16 | t.truthy(true) 17 | }) 18 | } 19 | --------------------------------------------------------------------------------