Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] sudo prompt is not shown in scripts, causing install and tests to hang #2887

Closed
aral opened this issue Mar 17, 2021 · 10 comments
Closed
Labels
Release 7.x work is associated with a specific npm 7 release

Comments

@aral
Copy link

aral commented Mar 17, 2021

Current Behavior:

If a command in an npm script (e.g., postinstall or test) causes a sudo prompt to be shown, the prompt is not presented to the person and the script hangs.

For example, if I have a postinstall script and I run npm install and some command in the script (e.g., an execFileSync()) requires sudo permissions, I will never the sudo prompt and the installation will hang.

Expected Behavior:

The sudo prompt should be shown to the person.

Steps To Reproduce:

  1. Create a folder called my-module
  2. Run npm init -y to create your package.json file
  3. Add the following scripts section to your package file:
  "scripts": {
    "postinstall": "node ./postinstall.js"
  }
  1. Create the postinstall.js file with the following contents:
import childProcess from 'child_process'
childProcess.execFileSync('sudo', ['ls', '/root'])
  1. Run npm i (and note that you get prompted for the sudo password as expected and that installation succeeds once you enter it)
  2. Create a different directory in the same parent directory as my-module called the-bug
  3. Run npm init -y from the the-bug directory
  4. In the the-bug directory, type npm i ../my-module to install your module

What should happen

You should get prompted for your sudo password and be able to enter it and installation should succeed when you do.

What actually happens

You don’t see the sudo prompt and the installation hangs.

Workaround

Until this bug is fixed, you can use the sudo-prompt module to display a graphical sudo prompt in your lifecycle scripts.

The following snippet from the real-world use case where I encountered this bug (in my Auto Encrypt Localhost library that uses mkcert to provision local TLS certificates) demonstrates the workaround:

import sudoPrompt from 'sudo-prompt'
import { binaryPath as mkcertBinary } from '../lib/mkcert.js'
//…
await (() => {
  return new Promise((resolve, reject) => {
    sudoPrompt.exec(`${mkcertBinary} -install`, {name: 'Auto Encrypt Localhost'}, function(error, stdout, stderr) {
      if (error) reject(error)
      console.log(stdout)
      resolve()
    })
  })
})()

(Above is not a true workaround as it does not behave exactly like sudo on Linux, causing file permission errors).

Other notes

  • Note that if you have passwordless sudo set up on your system, you will not encounter this bug.

Update

Also just ran into this issue also when running tests, which actually makes it a much more important bug. So if your Node app at any point requires sudo privileges, this means that your test will fail also as they do not show the sudo password prompt.

As a workaround for that, do something that requires the sudo prompt before your test. e.g., in your package file:

"scripts": {
  "test": "sudo echo 'Got sudo privileges.\n' && esm-tape-runner 'test/**/*.js' | tap-monkey"
}

That will unlock sudo and you should be able to run your tests before the timeout.

(Or, of course, use passwordless sudo on your dev machine but you cannot rely on everyone doing this. Most folks probably won’t and they’ll just think that your tests are hanging. So until this bug is fixed, your best bet is to use a workaround similar to the one above.)

Environment:

  • OS: elementary OS 5.1.7 Hera x86_64 (based on Ubuntu 18.04)
  • Node: 14.16.0
  • npm: 7.6.0
@aral aral added Bug thing that needs fixing Needs Triage needs review for next steps Release 7.x work is associated with a specific npm 7 release labels Mar 17, 2021
@ljharb
Copy link
Contributor

ljharb commented Mar 17, 2021

This seems like a bug in postinstall.js - you're initiating a sudo call without first checking if you're in a TTY, which causes a prompt.

@aral
Copy link
Author

aral commented Mar 17, 2021

@ljharb But my sudo call is a crucial part of the process. Unless I’m missing something, you’re not a TTY while being run as a lifecycle script and so this translates to “you cannot do anything that requires sudo from a lifecycle script.” I’m not bothered either way because I’m happy with the sudo-prompt workaround so it’s up to the npm folks if they consider that a bug or a feature. I feel it’s the former but this is not the hill I choose to die on :)

@ljharb
Copy link
Contributor

ljharb commented Mar 17, 2021

so this translates to “you cannot do anything that requires sudo from a lifecycle script.

Yes, but that's how shells work - you can't rely on a sudo prompt in a non-TTY. It seems like the confusion here is that npm suppressing the postinstall script's output meant you didn't realize the prompt was the problem, but I'd be surprised if the user was actually able to respond to the prompt directly on the shell anyways.

@aral
Copy link
Author

aral commented Mar 17, 2021

If you run npm install on the project itself, you get the sudo prompt. It’s only when installing it from a separate project that you don’t (see Step 3 versus Step 6 in the reproduction details).

I’ve since deployed an update to Auto Encrypt Localhost and documented the workaround in the readme in case the real-world use case helps: https://github.com/small-tech/auto-encrypt-localhost

Regardless, the workaround addresses my issue and the issue and workaround are adequately documented in this issue for anyone else encountering it so please feel free to close this if you don’t consider it a bug :)

@ljharb
Copy link
Contributor

ljharb commented Mar 17, 2021

That still seems problematic, since it would make the project untestable in CI. I'd assume the sudo prompt would appear when trying to do something, not when merely trying to install dependencies.

I'll leave it to the npm people to determine if it's a bug or not; it just seems a bit of a mismatch to me to use sudo in a lifecycle script at all.

@aral
Copy link
Author

aral commented Mar 17, 2021

Well, I do have a valid real-world use case for it (and I don’t see CI being used for small web stuff anytime soon so that’s not an issue for us) :) But like I said, the workaround works for me so I’m not bothered either way :)

@aral
Copy link
Author

aral commented Mar 23, 2021

Actually, scratch that, sudo-prompt uses pkexec on Linux and that doesn’t behave exactly like sudo so it means that when executing through it, you get different file permissions and, at least in my case, mkcert -install is failing. It would be really nice sudo worked properly in lifecycle scripts. It’s causing me a world of pain right now.

@aral
Copy link
Author

aral commented Mar 24, 2021

Just ran into this issue also when running tests. So if your Node app at any point requires sudo privileges, this means that your test will fail also as they do not show the sudo password prompt.

As a workaround for that, do something that requires the sudo prompt before your test. e.g., in your package file:

"scripts": {
  "test": "sudo echo 'Got sudo privileges.\n' && esm-tape-runner 'test/**/*.js' | tap-monkey"
}

That will unlock sudo and you should be able to run your tests before the timeout.

(Or, of course, use passwordless sudo on your dev machine but you cannot rely on everyone doing this. Most folks probably won’t and they’ll just think that your tests are hanging. So until this bug is fixed, your best bet is to use a workaround similar to the one above.)

@aral aral changed the title [BUG] sudo prompt is not shown in lifecycle scripts, causing install to hang [BUG] sudo prompt is not shown in scripts, causing install and tests to hang Mar 24, 2021
@darcyclarke
Copy link
Contributor

Seems like what you want is to run the command w/ --foreground-scripts; This changed in npm v7 so that scripts/output are run in the background.

@darcyclarke darcyclarke removed Needs Triage needs review for next steps Bug thing that needs fixing labels May 14, 2021
@Mustafatarakci
Copy link

Mevcut Davranış:

Bir npm betiğindeki bir komut (örneğin, kurulum sonrası veya test) bir sudo isteminin gösterilmesine neden olursa, istem kişiye sunulmaz ve betik askıda kalır.

Örneğin, bir betiğim varsa postinstallve çalıştırırsam npm installve betikteki bazı komutlar (örneğin, an execFileSync()) sudo izinleri gerektiriyorsa, asla sudo istemini almayacağım ve kurulum askıda kalacak.

Beklenen davranış:

Sudo istemi kişiye gösterilmelidir.

Yeniden Oluşturma Adımları:

  1. adlı bir klasör oluşturun.my-module
  2. Dosyanızı npm init -yoluşturmak için çalıştırınpackage.json
  3. scriptsPaket dosyanıza aşağıdaki bölümü ekleyin:
  "scripts": {
    "postinstall": "node ./postinstall.js"
  }
  1. postinstall.jsAşağıdaki içeriklerle dosyayı oluşturun :
import childProcess from 'child_process'
childProcess.execFileSync('sudo', ['ls', '/root'])
  1. Çalıştır npm i(ve beklendiği gibi sizden sudo parolası istendiğini ve bu parolayı girdikten sonra yüklemenin başarılı olduğunu unutmayın)
  2. my-moduleÇağrıldığı gibi aynı üst dizinde farklı bir dizin oluşturunthe-bug
  3. dizinden çalıştırın npm init -y_the-bug
  4. the-bugDizine npm i ../my-modulemodülünüzü kurmak için yazın

Ne olmalı

Sizden sudo şifreniz istenmeli ve girebilmelisiniz ve bunu yaptığınızda kurulum başarılı olmalıdır.

aslında ne olur

Sudo istemini görmüyorsunuz ve kurulum askıda kalıyor.

geçici çözüm

Bu hata düzeltilene kadar, yaşam döngüsü komut dosyalarınızda bir grafik sudo istemi görüntülemek için sudo istemi modülünü kullanabilirsiniz.

Bu hatayla karşılaştığım gerçek dünyadaki kullanım örneğinden aşağıdaki pasaj ( yerel TLS sertifikalarını sağlamak için mkcert kullanan Auto Encrypt Localhost kitaplığımda) geçici çözümü gösterir:

import sudoPrompt from 'sudo-prompt'
import { binaryPath as mkcertBinary } from '../lib/mkcert.js'
//…
await (() => {
  return new Promise((resolve, reject) => {
    sudoPrompt.exec(`${mkcertBinary} -install`, {name: 'Auto Encrypt Localhost'}, function(error, stdout, stderr) {
      if (error) reject(error)
      console.log(stdout)
      resolve()
    })
  })
})()

(Linux'ta tam olarak sudo gibi davranmadığı ve dosya izin hatalarına neden olmadığı için yukarıdaki gerçek bir geçici çözüm değildir).

Diğer notlar

  • Sisteminizde şifresiz sudo ayarladıysanız, bu hatayla karşılaşmayacağınızı unutmayın.

Güncelleme

Ayrıca testler yaparken de bu sorunla karşılaştım, bu da onu çok daha önemli bir hata haline getiriyor. Dolayısıyla, Düğüm uygulamanız herhangi bir noktada sudo ayrıcalıkları gerektiriyorsa, bu, sudo parola istemini göstermedikleri için testinizin de başarısız olacağı anlamına gelir.

Bunun için bir geçici çözüm olarak, testinizden önce sudo istemini gerektiren bir şey yapın. örneğin, paket dosyanızda:

"scripts": {
  "test": "sudo echo 'Got sudo privileges.\n' && esm-tape-runner 'test/**/*.js' | tap-monkey"
}

Bu, sudo'nun kilidini açar ve testlerinizi zaman aşımından önce çalıştırabilmeniz gerekir.

(Veya, elbette, geliştirici makinenizde parolasız sudo kullanın, ancak bunu yapan herkese güvenemezsiniz. Çoğu insan muhtemelen güvenmeyecek ve testlerinizin beklemede olduğunu düşünecekler. Bu hata düzeltilene kadar en iyi seçeneğiniz yukarıdakine benzer bir geçici çözüm kullanmaktır.)

Çevre:

  • OS: elementary OS 5.1.7 Hera x86_64 (based on Ubuntu 18.04)
  • Node: 14.16.0
  • npm: 7.6.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release 7.x work is associated with a specific npm 7 release
Projects
None yet
Development

No branches or pull requests

4 participants