Welcome to the Linux Foundation Forum!

LFW211 lab 15.1 AssertionError [ERR_ASSERTION]: child process should have only one env var

this problem was touched on in the past, title: Lab 15.1 problem. Path to child.js, but was originally related to a different problem.

windows 10 with cmd.exe

when providing the spawn options object with the env property set to a new key/value pair, the result is a minimal set of environment variables (11) with the addition of the new key/value set in the options object. the minimal set contains:

HOMEDRIVE
HOMEPATH
LOGONSERVER
PATH
SYSTEMDRIVE
SYSTEMROOT
TEMP
USERDOMAIN
USERNAME
USERPROFILE
WINDIR

with the following code in index.js

'use strict'

function exercise (myEnvVar) {
  return require('child_process').spawnSync(process.argv[0], ['child.js'],{
    env: {MY_ENV_VAR: myEnvVar}
  })
}

module.exports = exercise

an assertion error is thrown which notes the actual value received was 12 but the expected value was 1. after perusing the internet, it appears the functionality may have changed at some point in the past with how windows or node handles new processes. i found a few sources (including the lesson) that stated the above should overwrite all environment variables in the child process. whether it's node or windows, there doesn't seem to be an obvious way to create a child process with an empty or custom set of environment variables without the addition of the minimal set described above.

the values of the environment variables in the minimal set can be overwritten but there doesn't seem to be a way to remove any one or all of them.

it doesn't seem to be the point of the lab to learn how to remove environment variables from a child process so it may be prudent to remove the assertion from child.js which tests for the number of keys in the child process.env. if this test has some further use in linux environments, maybe an if block could be added to child.js to exclude the test on windows platforms:

if (process.platform !== 'win32') {
  assert.strictEqual(
    Object.keys(env).length,
    1,
    'child process should have only one env var'
  )
}

Comments

  • cscharr
    cscharr Posts: 4

    +1

    Just stumbled upon this :neutral:

  • xdxmxc
    xdxmxc Posts: 157

    working on a fix for this - still need a way to establish a blank env was passed on windows but you are right, hard coding the minimal set doesn't make sense given the churn on Windows. I have a windows laptop coming in a few days and will implement a fix as soon as I have it set up

  • I also encountered this issue.

  • xdxmxc
    xdxmxc Posts: 157

    this will be fixed in the next update

  • I came across this issue as well. Running node 18 LTS on a Windows 10 machine.

  • ailequal
    ailequal Posts: 3

    I am having a similar problem with macOS. Specifically with this one liner solution:

    return exec(`${process.execPath} ${path.join(__dirname, 'child.js')}`, { env: { MY_ENV_VAR: myEnvVar } } )
    

    I still have in my child process some extra injected environment variables (I double checked this directly in the test.js). Specifically: PWD, SHLVL and _.

  • xdxmxc
    xdxmxc Posts: 157

    so this problem of OS's injecting env vars into fresh envs isn't going to go away, what we're trying to check for is fresh env, which is no longer the same as blank env. What we need to ensure is the MY_ENV_VAR is not set on the parent env and then inherited, but rather passed as an env option. So I'm updating the test.js of this exercise so that it adds a control env var to the parent and then checks for that control in child env - if it's there then the child inherited the env var instead of setting the env var as a fresh env.

  • xdxmxc
    xdxmxc Posts: 157

    so the new test.js will be

    'use strict'
    const { deserialize: d } = require('v8')
    const { unlinkSync } = require('fs')
    const assert = require('assert')
    const { equal, fail } = assert.strict
    const exercise = require('.')
    const env = freshEnv()
    process.env.UNFRESH = 1
    let sp = null
    const value = 'is set [' + Date.now() + ']'
    try {
      sp = exercise(value)
      assert(sp, 'exercise function should return the result of a child process method')
      if (Buffer.isBuffer(sp)) {
        checkEnv()
        return
      }
    } catch (err) { 
      const { status} = err
      if (status == null) throw err
      equal(status, 0, 'exit code should be 0')
      return
    }
    
    if (!sp.on) {
      equal(sp.status, 0, 'exit code should be 0')
      checkEnv()
      return
    }
    
    const timeout = setTimeout(checkEnv, 5000)
    sp.once('exit', (status) => {
      equal(status, 0, 'exit code should be 0')
      clearTimeout(timeout)
      checkEnv()
    })
    
    function checkEnv () {
      let childEnv = null  
      try { 
        childEnv = loadEnv('./child-env.json')
      } catch {
        fail('child process misconfigured (cannot access child-env.json)')
      }
      for (let prop in env) if (Object.hasOwn(childEnv, prop)) delete childEnv[prop]
      equal(childEnv.MY_ENV_VAR, value)
      equal(childEnv.UNFRESH, undefined, 'child process should have fresh env')
      console.log('passed!')
    }
    
    function freshEnv () {
      require('child_process').spawnSync(process.execPath, [require.resolve('./child'), 'fresh'], d(Buffer.from('/w9vIgNlbnZvewB7AQ==', 'base64')))
      return loadEnv('./fresh-env.json')
    }
    
    function loadEnv (str, retry = 0) {
      try {
        return require(str)
      } catch (err) {
        if (retry > 5) throw err
        Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 500)
        return loadEnv(str, ++retry)
      } finally { 
        try { unlinkSync(require.resolve(str)) } catch { /*ignore*/ }
      }
    }
    

Categories

Upcoming Training