1+ import { EOL } from "os" ;
12import { spawn } from "child-process-promise" ;
23import * as child_process from "child_process" ;
34import { readFile } from "fs-extra" ;
@@ -26,6 +27,7 @@ import { Logger, ProgressReporter } from "./common";
2627import { CompilationMessage } from "./pure/legacy-messages" ;
2728import { sarifParser } from "./sarif-parser" ;
2829import { dbSchemeToLanguage , walkDirectory } from "./helpers" ;
30+ import { Credentials } from "./authentication" ;
2931
3032/**
3133 * The version of the SARIF format that we are using.
@@ -156,6 +158,10 @@ interface BqrsDecodeOptions {
156158 entities ?: string [ ] ;
157159}
158160
161+ export type OnLineCallback = (
162+ line : string ,
163+ ) => Promise < string | undefined > | string | undefined ;
164+
159165/**
160166 * This class manages a cli server started by `codeql execute cli-server` to
161167 * run commands without the overhead of starting a new java
@@ -304,6 +310,7 @@ export class CodeQLCliServer implements Disposable {
304310 command : string [ ] ,
305311 commandArgs : string [ ] ,
306312 description : string ,
313+ onLine ?: OnLineCallback ,
307314 ) : Promise < string > {
308315 const stderrBuffers : Buffer [ ] = [ ] ;
309316 if ( this . commandInProcess ) {
@@ -328,6 +335,22 @@ export class CodeQLCliServer implements Disposable {
328335 await new Promise < void > ( ( resolve , reject ) => {
329336 // Start listening to stdout
330337 process . stdout . addListener ( "data" , ( newData : Buffer ) => {
338+ if ( onLine ) {
339+ void ( async ( ) => {
340+ const response = await onLine ( newData . toString ( "utf-8" ) ) ;
341+
342+ if ( ! response ) {
343+ return ;
344+ }
345+
346+ process . stdin . write ( `${ response } ${ EOL } ` ) ;
347+
348+ // Remove newData from stdoutBuffers because the data has been consumed
349+ // by the onLine callback.
350+ stdoutBuffers . splice ( stdoutBuffers . indexOf ( newData ) , 1 ) ;
351+ } ) ( ) ;
352+ }
353+
331354 stdoutBuffers . push ( newData ) ;
332355 // If the buffer ends in '0' then exit.
333356 // We don't have to check the middle as no output will be written after the null until
@@ -487,13 +510,15 @@ export class CodeQLCliServer implements Disposable {
487510 * @param commandArgs The arguments to pass to the `codeql` command.
488511 * @param description Description of the action being run, to be shown in log and error messages.
489512 * @param progressReporter Used to output progress messages, e.g. to the status bar.
513+ * @param onLine Used for responding to interactive output on stdout/stdin.
490514 * @returns The contents of the command's stdout, if the command succeeded.
491515 */
492516 runCodeQlCliCommand (
493517 command : string [ ] ,
494518 commandArgs : string [ ] ,
495519 description : string ,
496520 progressReporter ?: ProgressReporter ,
521+ onLine ?: OnLineCallback ,
497522 ) : Promise < string > {
498523 if ( progressReporter ) {
499524 progressReporter . report ( { message : description } ) ;
@@ -503,10 +528,12 @@ export class CodeQLCliServer implements Disposable {
503528 // Construct the command that actually does the work
504529 const callback = ( ) : void => {
505530 try {
506- this . runCodeQlCliInternal ( command , commandArgs , description ) . then (
507- resolve ,
508- reject ,
509- ) ;
531+ this . runCodeQlCliInternal (
532+ command ,
533+ commandArgs ,
534+ description ,
535+ onLine ,
536+ ) . then ( resolve , reject ) ;
510537 } catch ( err ) {
511538 reject ( err ) ;
512539 }
@@ -528,6 +555,7 @@ export class CodeQLCliServer implements Disposable {
528555 * @param description Description of the action being run, to be shown in log and error messages.
529556 * @param addFormat Whether or not to add commandline arguments to specify the format as JSON.
530557 * @param progressReporter Used to output progress messages, e.g. to the status bar.
558+ * @param onLine Used for responding to interactive output on stdout/stdin.
531559 * @returns The contents of the command's stdout, if the command succeeded.
532560 */
533561 async runJsonCodeQlCliCommand < OutputType > (
@@ -536,6 +564,7 @@ export class CodeQLCliServer implements Disposable {
536564 description : string ,
537565 addFormat = true ,
538566 progressReporter ?: ProgressReporter ,
567+ onLine ?: OnLineCallback ,
539568 ) : Promise < OutputType > {
540569 let args : string [ ] = [ ] ;
541570 if ( addFormat )
@@ -547,6 +576,7 @@ export class CodeQLCliServer implements Disposable {
547576 args ,
548577 description ,
549578 progressReporter ,
579+ onLine ,
550580 ) ;
551581 try {
552582 return JSON . parse ( result ) as OutputType ;
@@ -559,6 +589,44 @@ export class CodeQLCliServer implements Disposable {
559589 }
560590 }
561591
592+ /**
593+ * Runs a CodeQL CLI command, returning the output as JSON.
594+ * @param command The `codeql` command to be run, provided as an array of command/subcommand names.
595+ * @param commandArgs The arguments to pass to the `codeql` command.
596+ * @param description Description of the action being run, to be shown in log and error messages.
597+ * @param addFormat Whether or not to add commandline arguments to specify the format as JSON.
598+ * @param progressReporter Used to output progress messages, e.g. to the status bar.
599+ * @returns The contents of the command's stdout, if the command succeeded.
600+ */
601+ async runJsonCodeQlCliCommandWithAuthentication < OutputType > (
602+ command : string [ ] ,
603+ commandArgs : string [ ] ,
604+ description : string ,
605+ addFormat = true ,
606+ progressReporter ?: ProgressReporter ,
607+ ) : Promise < OutputType > {
608+ const credentials = await Credentials . initialize ( ) ;
609+
610+ const accessToken = await credentials . getExistingAccessToken ( ) ;
611+
612+ const extraArgs = accessToken ? [ "--github-auth-stdin" ] : [ ] ;
613+
614+ return this . runJsonCodeQlCliCommand (
615+ command ,
616+ [ ...extraArgs , ...commandArgs ] ,
617+ description ,
618+ addFormat ,
619+ progressReporter ,
620+ async ( line ) => {
621+ if ( line . startsWith ( "Enter value for --github-auth-stdin" ) ) {
622+ return credentials . getAccessToken ( ) ;
623+ }
624+
625+ return undefined ;
626+ } ,
627+ ) ;
628+ }
629+
562630 /**
563631 * Resolve the library path and dbscheme for a query.
564632 * @param workspaces The current open workspaces
@@ -1136,7 +1204,7 @@ export class CodeQLCliServer implements Disposable {
11361204 * @param packs The `<package-scope/name[@version]>` of the packs to download.
11371205 */
11381206 async packDownload ( packs : string [ ] ) {
1139- return this . runJsonCodeQlCliCommand (
1207+ return this . runJsonCodeQlCliCommandWithAuthentication (
11401208 [ "pack" , "download" ] ,
11411209 packs ,
11421210 "Downloading packs" ,
@@ -1148,7 +1216,7 @@ export class CodeQLCliServer implements Disposable {
11481216 if ( forceUpdate ) {
11491217 args . push ( "--mode" , "update" ) ;
11501218 }
1151- return this . runJsonCodeQlCliCommand (
1219+ return this . runJsonCodeQlCliCommandWithAuthentication (
11521220 [ "pack" , "install" ] ,
11531221 args ,
11541222 "Installing pack dependencies" ,
@@ -1169,7 +1237,7 @@ export class CodeQLCliServer implements Disposable {
11691237 ...this . getAdditionalPacksArg ( workspaceFolders ) ,
11701238 ] ;
11711239
1172- return this . runJsonCodeQlCliCommand (
1240+ return this . runJsonCodeQlCliCommandWithAuthentication (
11731241 [ "pack" , "bundle" ] ,
11741242 args ,
11751243 "Bundling pack" ,
@@ -1200,7 +1268,7 @@ export class CodeQLCliServer implements Disposable {
12001268 ) : Promise < { [ pack : string ] : string } > {
12011269 // Uses the default `--mode use-lock`, which creates the lock file if it doesn't exist.
12021270 const results : { [ pack : string ] : string } =
1203- await this . runJsonCodeQlCliCommand (
1271+ await this . runJsonCodeQlCliCommandWithAuthentication (
12041272 [ "pack" , "resolve-dependencies" ] ,
12051273 [ dir ] ,
12061274 "Resolving pack dependencies" ,
0 commit comments