current commit
261015.9 · stream: create stream/iter iterator results with a constructor
// Flags: --experimental-stream-iter
'use strict';

// Iterator results created by the stream/iter iterators themselves do not
// inherit from Object.prototype, so prototype pollution cannot affect them.
// (Iterators implemented as async generators, such as the one returned by
// pull(), return ordinary iterator results created by the engine.)

const common = require('../common');
const assert = require('assert');
const { inspect } = require('util');
const {
  broadcast,
  from,
  push,
  share,
  shareSync,
} = require('stream/iter');

function assertResult(result, done) {
  assert.strictEqual(result instanceof Object, false);
  assert.deepStrictEqual(Object.keys(result), ['done', 'value']);
  assert.strictEqual(result.done, done);
  assert.match(inspect(result), /^IterResult \{ done: (true|false), value: /);
}

async function testAsyncIterators() {
  const sources = {
    'push()': () => {
      const { writer, readable } = push();
      writer.writeSync('a');
      writer.endSync();
      return readable;
    },
    'share()': () => share(from('a')).pull(),
    'broadcast()': () => {
      const { writer, broadcast: bc } = broadcast();
      const consumer = bc.push();
      writer.writeSync('a');
      writer.endSync();
      return consumer;
    },
  };
  for (const create of Object.values(sources)) {
    const iterator = create()[Symbol.asyncIterator]();
    assertResult(await iterator.next(), false);
    assertResult(await iterator.next(), true);
  }
}

function testShareSync() {
  const iterator = shareSync(['a']).pull()[Symbol.iterator]();
  assertResult(iterator.next(), false);
  assertResult(iterator.next(), true);
}

async function testPollutedThen() {
  // Resolving an async next() with an object looks up `then`. Results must
  // not pick it up from a polluted Object.prototype.
  const { writer, readable } = push();
  writer.writeSync('ab');
  writer.endSync();
  const iterator = readable[Symbol.asyncIterator]();
  Object.prototype.then = common.mustNotCall('Object.prototype.then');
  try {
    const first = await iterator.next();
    assert.strictEqual(first.done, false);
    assert.strictEqual(new TextDecoder().decode(first.value[0]), 'ab');
    assert.strictEqual((await iterator.next()).done, true);
  } finally {
    delete Object.prototype.then;
  }
}

(async () => {
  await testAsyncIterators();
  testShareSync();
  await testPollutedThen();
})().then(common.mustCall());