Skip to content

Commit

Permalink
Fix: prevent Git from showing a password prompt (#3633)
Browse files Browse the repository at this point in the history
**Summary**

Fixes #764.

Turns out when fetching packages from remote sources via Git,
it may ask for a username or password and get stuck there since
we run it in the background. This patch passes the following env
variables to disable any prompts:

1. `$GIT_ASKPASS=""`
   This is the command Git runs to get username and pass. Setting
   it to an empty string disables it and git exits with an error.
2. `$GIT_TERMINAL_PROMPT=0`
    This is new in Git 2.3 and it prevents git from even trying to
    ask for a password. Surpasses `$GIT_ASKPASS` above.
3. `$GIT_SSH_COMMAND="ssh -oBatchMode=yes"`
    This is also new in Git 2.3 and it makes git make the call to
    ssh via this command. `BatchMode=yes` option tells ssh to not
    do anything interactive (because batch/script mode) like
    password prompts in case publickey auth fails etc.

**Test plan**

Do `yarn add git+https://github.com/Napsty/privrepo.git#1.0.2`. `yarn`
hangs before this patch and fails with a "Refusing to download" error
after the patch.
  • Loading branch information
BYK authored Jun 14, 2017
1 parent 0361edf commit 27e9922
Showing 1 changed file with 18 additions and 11 deletions.
29 changes: 18 additions & 11 deletions src/util/git.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const supportsArchiveCache: {[key: string]: boolean} = map({
'github.com': false, // not support, doubt they will ever support it
});

// Suppress any password prompts since we run these in the background
const env = {GIT_ASKPASS: '', GIT_TERMINAL_PROMPT: 0, GIT_SSH_COMMAND: 'ssh -oBatchMode=yes'};

// This regex is designed to match output from git of the style:
// ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/v0.21.0
// and extract the hash and tag name as capture groups
Expand Down Expand Up @@ -87,6 +90,10 @@ export default class Git {
};
}

static spawn(args: Array<string>, opts?: child_process$spawnOpts = {}): Promise<string> {
return child.spawn('git', args, {...opts, env});
}

/**
* Check if the host specified in the input `gitUrl` has archive capability.
*/
Expand All @@ -102,7 +109,7 @@ export default class Git {
}

try {
await child.spawn('git', ['archive', `--remote=${ref.repository}`, 'HEAD', Date.now() + '']);
await Git.spawn(['archive', `--remote=${ref.repository}`, 'HEAD', Date.now() + '']);
throw new Error();
} catch (err) {
const supports = err.message.indexOf('did not match any files') >= 0;
Expand All @@ -120,7 +127,7 @@ export default class Git {

static async repoExists(ref: GitUrl): Promise<boolean> {
try {
await child.spawn('git', ['ls-remote', '-t', ref.repository]);
await Git.spawn(['ls-remote', '-t', ref.repository]);
return true;
} catch (err) {
return false;
Expand Down Expand Up @@ -191,7 +198,7 @@ export default class Git {

async _archiveViaRemoteArchive(dest: string): Promise<string> {
const hashStream = new crypto.HashStream();
await child.spawn('git', ['archive', `--remote=${this.gitUrl.repository}`, this.ref], {
await Git.spawn(['archive', `--remote=${this.gitUrl.repository}`, this.ref], {
process(proc, resolve, reject, done) {
const writeStream = createWriteStream(dest);
proc.on('error', reject);
Expand All @@ -208,7 +215,7 @@ export default class Git {

async _archiveViaLocalFetched(dest: string): Promise<string> {
const hashStream = new crypto.HashStream();
await child.spawn('git', ['archive', this.hash], {
await Git.spawn(['archive', this.hash], {
cwd: this.cwd,
process(proc, resolve, reject, done) {
const writeStream = createWriteStream(dest);
Expand Down Expand Up @@ -237,7 +244,7 @@ export default class Git {
}

async _cloneViaRemoteArchive(dest: string): Promise<void> {
await child.spawn('git', ['archive', `--remote=${this.gitUrl.repository}`, this.ref], {
await Git.spawn(['archive', `--remote=${this.gitUrl.repository}`, this.ref], {
process(proc, update, reject, done) {
const extractor = tarFs.extract(dest, {
dmode: 0o555, // all dirs should be readable
Expand All @@ -253,7 +260,7 @@ export default class Git {
}

async _cloneViaLocalFetched(dest: string): Promise<void> {
await child.spawn('git', ['archive', this.hash], {
await Git.spawn(['archive', this.hash], {
cwd: this.cwd,
process(proc, resolve, reject, done) {
const extractor = tarFs.extract(dest, {
Expand All @@ -278,9 +285,9 @@ export default class Git {

return fs.lockQueue.push(gitUrl.repository, async () => {
if (await fs.exists(cwd)) {
await child.spawn('git', ['pull'], {cwd});
await Git.spawn(['pull'], {cwd});
} else {
await child.spawn('git', ['clone', gitUrl.repository, cwd]);
await Git.spawn(['clone', gitUrl.repository, cwd]);
}

this.fetched = true;
Expand Down Expand Up @@ -320,7 +327,7 @@ export default class Git {

async _getFileFromArchive(filename: string): Promise<string | false> {
try {
return await child.spawn('git', ['archive', `--remote=${this.gitUrl.repository}`, this.ref, filename], {
return await Git.spawn(['archive', `--remote=${this.gitUrl.repository}`, this.ref, filename], {
process(proc, update, reject, done) {
const parser = tarStream.extract();

Expand Down Expand Up @@ -359,7 +366,7 @@ export default class Git {
invariant(this.fetched, 'Repo not fetched');

try {
return await child.spawn('git', ['show', `${this.hash}:${filename}`], {
return await Git.spawn(['show', `${this.hash}:${filename}`], {
cwd: this.cwd,
});
} catch (err) {
Expand All @@ -385,7 +392,7 @@ export default class Git {
}

async setRefRemote(): Promise<string> {
const stdout = await child.spawn('git', ['ls-remote', '--tags', '--heads', this.gitUrl.repository]);
const stdout = await Git.spawn(['ls-remote', '--tags', '--heads', this.gitUrl.repository]);
const refs = Git.parseRefs(stdout);
return this.setRef(refs);
}
Expand Down

0 comments on commit 27e9922

Please sign in to comment.