GithubHelp home page GithubHelp logo

safer-buffer's Introduction

safer-buffer travis npm javascript style guide Security Responsible Disclosure

Modern Buffer API polyfill without footguns, working on Node.js from 0.8 to current.

How to use?

First, port all Buffer() and new Buffer() calls to Buffer.alloc() and Buffer.from() API.

Then, to achieve compatibility with outdated Node.js versions (<4.5.0 and 5.x <5.10.0), use const Buffer = require('safer-buffer').Buffer in all files where you make calls to the new Buffer API. Use var instead of const if you need that for your Node.js version range support.

Also, see the porting Buffer guide.

Do I need it?

Hopefully, not — dropping support for outdated Node.js versions should be fine nowdays, and that is the recommended path forward. You do need to port to the Buffer.alloc() and Buffer.from() though.

See the porting guide for a better description.

Why not safe-buffer?

In short: while safe-buffer serves as a polyfill for the new API, it allows old API usage and itself contains footguns.

safe-buffer could be used safely to get the new API while still keeping support for older Node.js versions (like this module), but while analyzing ecosystem usage of the old Buffer API I found out that safe-buffer is itself causing problems in some cases.

For example, consider the following snippet:

$ cat example.unsafe.js
console.log(Buffer(20))
$ ./node-v6.13.0-linux-x64/bin/node example.unsafe.js
<Buffer 0a 00 00 00 00 00 00 00 28 13 de 02 00 00 00 00 05 00 00 00>
$ standard example.unsafe.js
standard: Use JavaScript Standard Style (https://standardjs.com)
  /home/chalker/repo/safer-buffer/example.unsafe.js:2:13: 'Buffer()' was deprecated since v6. Use 'Buffer.alloc()' or 'Buffer.from()' (use 'https://www.npmjs.com/package/safe-buffer' for '<4.5.0') instead.

This is allocates and writes to console an uninitialized chunk of memory. standard linter (among others) catch that and warn people to avoid using unsafe API.

Let's now throw in safe-buffer!

$ cat example.safe-buffer.js
const Buffer = require('safe-buffer').Buffer
console.log(Buffer(20))
$ standard example.safe-buffer.js
$ ./node-v6.13.0-linux-x64/bin/node example.safe-buffer.js
<Buffer 08 00 00 00 00 00 00 00 28 58 01 82 fe 7f 00 00 00 00 00 00>

See the problem? Adding in safe-buffer magically removes the lint warning, but the behavior remains identiсal to what we had before, and when launched on Node.js 6.x LTS — this dumps out chunks of uninitialized memory. And this code will still emit runtime warnings on Node.js 10.x and above.

That was done by design. I first considered changing safe-buffer, prohibiting old API usage or emitting warnings on it, but that significantly diverges from safe-buffer design. After some discussion, it was decided to move my approach into a separate package, and this is that separate package.

This footgun is not imaginary — I observed top-downloaded packages doing that kind of thing, «fixing» the lint warning by blindly including safe-buffer without any actual changes.

Also in some cases, even if the API was migrated to use of safe Buffer API — a random pull request can bring unsafe Buffer API usage back to the codebase by adding new calls — and that could go unnoticed even if you have a linter prohibiting that (because of the reason stated above), and even pass CI. I also observed that being done in popular packages.

Some examples:

I filed a PR at mysticatea/eslint-plugin-node#110 to partially fix that (for cases when that lint rule is used), but it is a semver-major change for linter rules and presets, so it would take significant time for that to reach actual setups. It also hasn't been released yet (2018-03-20).

Also, safer-buffer discourages the usage of .allocUnsafe(), which is often done by a mistake. It still supports it with an explicit concern barier, by placing it under require('safer-buffer/dangereous').

But isn't throwing bad?

Not really. It's an error that could be noticed and fixed early, instead of causing havoc later like unguarded new Buffer() calls that end up receiving user input can do.

This package affects only the files where var Buffer = require('safer-buffer').Buffer was done, so it is really simple to keep track of things and make sure that you don't mix old API usage with that. Also, CI should hint anything that you might have missed.

New commits, if tested, won't land new usage of unsafe Buffer API this way. Node.js 10.x also deals with that by printing a runtime depecation warning.

Would it affect third-party modules?

No, unless you explicitly do an awful thing like monkey-patching or overriding the built-in Buffer. Don't do that.

But I don't want throwing…

That is also fine!

Also, it could be better in some cases when you don't comprehensive enough test coverage.

In that case — just don't override Buffer and use var SaferBuffer = require('safer-buffer').Buffer instead.

That way, everything using Buffer natively would still work, but there would be two drawbacks:

  • Buffer.from/Buffer.alloc won't be polyfilled — use SaferBuffer.from and SaferBuffer.alloc instead.
  • You are still open to accidentally using the insecure deprecated API — use a linter to catch that.

Note that using a linter to catch accidential Buffer constructor usage in this case is strongly recommended. Buffer is not overriden in this usecase, so linters won't get confused.

«Without footguns»?

Well, it is still possible to do some things with Buffer API, e.g. accessing .buffer property on older versions and duping things from there. You shouldn't do that in your code, probabably.

The intention is to remove the most significant footguns that affect lots of packages in the ecosystem, and to do it in the proper way.

Also, this package doesn't protect against security issues affecting some Node.js versions, so for usage in your own production code, it is still recommended to update to a Node.js version supported by upstream.

safer-buffer's People

Contributors

addaleax avatar ashtuchkin avatar chalker avatar jon-simpkins avatar joyeecheung avatar trott avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

safer-buffer's Issues

Identifier 'Buffer' has already been declared - when multiple sdks are declaring/adding Buffer

Uncaught SyntaxError: Identifier 'Buffer' has already been declared (at intuit-oauth-ts.js?v=ad72efd1:238:1)

My environment:
vite: 4.4.0

Vite Config

import {defineConfig} from "vite";
import nodePolyfills from "rollup-plugin-polyfill-node";
import {NodeGlobalsPolyfillPlugin} from "@esbuild-plugins/node-globals-polyfill";
import {NodeModulesPolyfillPlugin} from "@esbuild-plugins/node-modules-polyfill";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";
import {esbuildCommonjs} from "@originjs/vite-plugin-commonjs";

import svgrPlugin from "vite-plugin-svgr";

// https://vitejs.dev/config/
export default defineConfig(({command}) => {
  return {
    base: command == "serve" ? "/cockpit/" : "/",
    plugins: [
      react(),
      tsconfigPaths(),
      svgrPlugin({svgrOptions: {icon: true}}),
      {
        name: "fix-node-globals-polyfill",
        setup(build) {
          build.onResolve({filter: /util\.js/}, ({path}) => ({path}));
        },
      },
    ],
    server: {
      port: 3000,
    },
    resolve: {
      alias: {
        // This Rollup aliases are extracted from @esbuild-plugins/node-modules-polyfill,
        // see https://github.com/remorses/esbuild-plugins/blob/master/node-modules-polyfill/src/polyfills.ts
        // process and buffer are excluded because already managed
        // by node-globals-polyfill
        util: "util",
        sys: "util",
        events: "rollup-plugin-node-polyfills/polyfills/events",
        stream: "rollup-plugin-node-polyfills/polyfills/stream",
        path: "rollup-plugin-node-polyfills/polyfills/path",
        querystring: "rollup-plugin-node-polyfills/polyfills/qs",
        punycode: "rollup-plugin-node-polyfills/polyfills/punycode",
        url: "rollup-plugin-node-polyfills/polyfills/url",
        http: "rollup-plugin-node-polyfills/polyfills/http",
        https: "rollup-plugin-node-polyfills/polyfills/http",
        os: "rollup-plugin-node-polyfills/polyfills/os",
        assert: "rollup-plugin-node-polyfills/polyfills/assert",
        constants: "rollup-plugin-node-polyfills/polyfills/constants",
        _stream_duplex:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/duplex",
        _stream_passthrough:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/passthrough",
        _stream_readable:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/readable",
        _stream_writable:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/writable",
        _stream_transform:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/transform",
        timers: "rollup-plugin-node-polyfills/polyfills/timers",
        console: "rollup-plugin-node-polyfills/polyfills/console",
        vm: "rollup-plugin-node-polyfills/polyfills/vm",
        zlib: "rollup-plugin-node-polyfills/polyfills/zlib",
        tty: "rollup-plugin-node-polyfills/polyfills/tty",
        domain: "rollup-plugin-node-polyfills/polyfills/domain",
        "react/jsx-dev-runtime": require.resolve("react/jsx-dev-runtime"),
        "react/jsx-runtime": require.resolve("react/jsx-runtime"),
      },
    },
    optimizeDeps: {
      include: ["react/jsx-runtime"],
      esbuildOptions: {
        // Node.js global to browser globalThis
        define: {
          global: "globalThis",
        },
        // Enable esbuild polyfill plugins
        plugins: [
          esbuildCommonjs(["react-calendar", "react-date-picker", "date-fns"]),
          NodeGlobalsPolyfillPlugin({
            process: true,
            buffer: false,
          }),
          NodeModulesPolyfillPlugin(),
        ],
      },
    },
    build: {
      commonjsOptions: {
        include: [/linked-dep/, /node_modules/],
      },
      rollupOptions: {
        plugins: [nodePolyfills()],
      },
    },
  };
});

We are using "intuit-oauth-ts": "^0.0.4" which has a dependency which adds Buffer polyfill. And with this intuit-oauth-ts our repo is working perfectly with above config.

Now we are trying to add new sdk - statsig-react, which also has a dependency -safer-buffer which is also adding Buffer polyfill and it's creating a conflict - Identifier 'Buffer' has already been declared.

I am providing one minimal reproduction repo here.

Can somebody help here? We have already tried below option it's not working since this issue is at dependency level. We don't won't to switch back to react-scripts.

  NodeGlobalsPolyfillPlugin({
            process: true,
            buffer: false,
          }),

Node.js version support policy

Hi @ChALkeR . I'm looking to move some modules to use this module for the whole Buffer thing and was curious on your Node.js version support policy. I looked around but didn't see anything (including looking for engines.node in the package.json). I do see the .travis.yml though has down to 0.8.

Just has a couple questions:

  1. Does this mean the minimum supported Node.js version for this module in 0.8?
  2. If that does change in the future, would you consider that a major version bump of this module or no?

Thanks for all your hard work @ChALkeR

deprecate npm package

The time have come where we no longer need this

Think ppl should stop using this package

Issue when bundling with rollup

Hi,

when using rollup to bundle, it converts modules to something like this:

var bufferEs6 = /*#__PURE__*/Object.freeze({
  __proto__: null,
  Buffer: Buffer,
  INSPECT_MAX_BYTES: INSPECT_MAX_BYTES,
  SlowBuffer: SlowBuffer,
  isBuffer: isBuffer,
  kMaxLength: _kMaxLength
});

Because the Object is explicitly set to have no prototype, this check in safer-buffer will fail (no prototype -- no hasOwnProperty()):
if (!buffer.hasOwnProperty(key)) continue

If you use the following instead, it'll work fine:
if (!Object.hasOwnProperty.call(buffer, key)) continue

Do you think this solution would be safe enough to rewrite?

Identifier 'Buffer' has already been declared - when multiple sdks are declaring/adding Buffer

Uncaught SyntaxError: Identifier 'Buffer' has already been declared (at intuit-oauth-ts.js?v=ad72efd1:238:1)

My environment:
vite: 4.4.0

Vite Config

import {defineConfig} from "vite";
import nodePolyfills from "rollup-plugin-polyfill-node";
import {NodeGlobalsPolyfillPlugin} from "@esbuild-plugins/node-globals-polyfill";
import {NodeModulesPolyfillPlugin} from "@esbuild-plugins/node-modules-polyfill";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";
import {esbuildCommonjs} from "@originjs/vite-plugin-commonjs";

import svgrPlugin from "vite-plugin-svgr";

// https://vitejs.dev/config/
export default defineConfig(({command}) => {
  return {
    base: command == "serve" ? "/cockpit/" : "/",
    plugins: [
      react(),
      tsconfigPaths(),
      svgrPlugin({svgrOptions: {icon: true}}),
      {
        name: "fix-node-globals-polyfill",
        setup(build) {
          build.onResolve({filter: /util\.js/}, ({path}) => ({path}));
        },
      },
    ],
    server: {
      port: 3000,
    },
    resolve: {
      alias: {
        // This Rollup aliases are extracted from @esbuild-plugins/node-modules-polyfill,
        // see https://github.com/remorses/esbuild-plugins/blob/master/node-modules-polyfill/src/polyfills.ts
        // process and buffer are excluded because already managed
        // by node-globals-polyfill
        util: "util",
        sys: "util",
        events: "rollup-plugin-node-polyfills/polyfills/events",
        stream: "rollup-plugin-node-polyfills/polyfills/stream",
        path: "rollup-plugin-node-polyfills/polyfills/path",
        querystring: "rollup-plugin-node-polyfills/polyfills/qs",
        punycode: "rollup-plugin-node-polyfills/polyfills/punycode",
        url: "rollup-plugin-node-polyfills/polyfills/url",
        http: "rollup-plugin-node-polyfills/polyfills/http",
        https: "rollup-plugin-node-polyfills/polyfills/http",
        os: "rollup-plugin-node-polyfills/polyfills/os",
        assert: "rollup-plugin-node-polyfills/polyfills/assert",
        constants: "rollup-plugin-node-polyfills/polyfills/constants",
        _stream_duplex:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/duplex",
        _stream_passthrough:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/passthrough",
        _stream_readable:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/readable",
        _stream_writable:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/writable",
        _stream_transform:
          "rollup-plugin-node-polyfills/polyfills/readable-stream/transform",
        timers: "rollup-plugin-node-polyfills/polyfills/timers",
        console: "rollup-plugin-node-polyfills/polyfills/console",
        vm: "rollup-plugin-node-polyfills/polyfills/vm",
        zlib: "rollup-plugin-node-polyfills/polyfills/zlib",
        tty: "rollup-plugin-node-polyfills/polyfills/tty",
        domain: "rollup-plugin-node-polyfills/polyfills/domain",
        "react/jsx-dev-runtime": require.resolve("react/jsx-dev-runtime"),
        "react/jsx-runtime": require.resolve("react/jsx-runtime"),
      },
    },
    optimizeDeps: {
      include: ["react/jsx-runtime"],
      esbuildOptions: {
        // Node.js global to browser globalThis
        define: {
          global: "globalThis",
        },
        // Enable esbuild polyfill plugins
        plugins: [
          esbuildCommonjs(["react-calendar", "react-date-picker", "date-fns"]),
          NodeGlobalsPolyfillPlugin({
            process: true,
            buffer: false,
          }),
          NodeModulesPolyfillPlugin(),
        ],
      },
    },
    build: {
      commonjsOptions: {
        include: [/linked-dep/, /node_modules/],
      },
      rollupOptions: {
        plugins: [nodePolyfills()],
      },
    },
  };
});

We are using "intuit-oauth-ts": "^0.0.4" which has a dependency which adds Buffer polyfill. And with this intuit-oauth-ts our repo is working perfectly with above config.

Now we are trying to add new sdk - statsig-react, which also has a dependency -safer-buffer which is also adding Buffer polyfill and it's creating a conflict - Identifier 'Buffer' has already been declared.

I am providing one minimal reproduction repo here.

Can somebody help here? We have already tried below option it's not working since this issue is at dependency level. We don't won't to switch back to react-scripts.

  NodeGlobalsPolyfillPlugin({
            process: true,
            buffer: false,
          }),

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.