azure-pipelines-task-lib 5.280.0 → 5.280.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ import stream = require('stream');
5
+ /** Commands allowed by default once VSO commands are enabled for a path. */
6
+ export declare const defaultAllowedVsoCommands: readonly string[];
7
+ /**
8
+ * Where the external output originates. Used for documentation and telemetry only; it does
9
+ * NOT select an automatic allowlist. A task must explicitly enable VSO commands when needed.
10
+ */
11
+ export type ExternalOutputSource = 'remote' | 'childProcess' | 'repository';
12
+ export interface ExternalOutputOptions {
13
+ /** Origin of the output. Documentation/telemetry only. */
14
+ source: ExternalOutputSource;
15
+ /**
16
+ * When false or omitted, every "##vso[" marker is blocked. When true, markers whose
17
+ * command name is in the effective allowlist pass through unchanged.
18
+ */
19
+ enableVsoCommands?: boolean;
20
+ /**
21
+ * The allowlist of "area.event" command names to permit when enableVsoCommands is true.
22
+ * Replaces (does not extend) the default list. An explicit empty array allows nothing.
23
+ * Has no effect when enableVsoCommands is false.
24
+ */
25
+ allowedVsoCommands?: readonly string[];
26
+ /** Destination for filtered output. Defaults to process.stdout. */
27
+ destination?: NodeJS.WritableStream;
28
+ }
29
+ /**
30
+ * Stateful, byte-level marker filter. Feed bytes with push() and finish with flush().
31
+ * Retains only the minimal bytes needed to resolve a marker that straddles a chunk
32
+ * boundary, so memory stays bounded regardless of input size.
33
+ */
34
+ export declare class MarkerFilter {
35
+ private readonly enabled;
36
+ private readonly allowed;
37
+ private pending;
38
+ constructor(enabled: boolean, allowed: Set<string>);
39
+ push(chunk: Buffer): Buffer;
40
+ flush(): Buffer;
41
+ }
42
+ /**
43
+ * A Transform stream that neutralizes VSO command markers in external output. Pipe untrusted
44
+ * output into it; it writes filtered bytes to its readable side (and, via
45
+ * createExternalOutputStream, on to the destination).
46
+ */
47
+ export declare class ExternalOutputStream extends stream.Transform {
48
+ private readonly markerFilter;
49
+ constructor(options: ExternalOutputOptions);
50
+ _transform(chunk: any, _encoding: string, callback: (error?: Error | null) => void): void;
51
+ _flush(callback: (error?: Error | null) => void): void;
52
+ }
53
+ /**
54
+ * Creates a filtering stream for external output and pipes it to the destination
55
+ * (default process.stdout). Pipe untrusted output into the returned stream, e.g.
56
+ * `jenkinsResponse.pipe(tl.createExternalOutputStream({ source: 'remote' }))`.
57
+ */
58
+ export declare function createExternalOutputStream(options: ExternalOutputOptions): ExternalOutputStream;
59
+ /**
60
+ * Filters a complete piece of external output and writes it to the destination
61
+ * (default process.stdout). Each call is self-contained; for output that arrives in
62
+ * chunks that may split a marker, use createExternalOutputStream instead.
63
+ */
64
+ export declare function writeExternalOutput(data: string | Buffer, options: ExternalOutputOptions): void;
65
+ /** Filters one complete value and returns its bytes without writing them. */
66
+ export declare function filterExternalOutput(data: string | Buffer, options: ExternalOutputOptions): Buffer;
67
+ export interface FilteredWriter {
68
+ write(data: string | Buffer): void;
69
+ end(): void;
70
+ }
71
+ /** Creates a stateful writer that filters markers split across writes. */
72
+ export declare function createFilteredWriter(options: ExternalOutputOptions, destination: NodeJS.WritableStream): FilteredWriter;
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft. All rights reserved.
3
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4
+ var __extends = (this && this.__extends) || (function () {
5
+ var extendStatics = function (d, b) {
6
+ extendStatics = Object.setPrototypeOf ||
7
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
8
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
9
+ return extendStatics(d, b);
10
+ };
11
+ return function (d, b) {
12
+ if (typeof b !== "function" && b !== null)
13
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
14
+ extendStatics(d, b);
15
+ function __() { this.constructor = d; }
16
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
17
+ };
18
+ })();
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createFilteredWriter = exports.filterExternalOutput = exports.writeExternalOutput = exports.createExternalOutputStream = exports.ExternalOutputStream = exports.MarkerFilter = exports.defaultAllowedVsoCommands = void 0;
21
+ var stream = require("stream");
22
+ //
23
+ // External output filtering.
24
+ //
25
+ // Azure Pipelines treats a task's stdout both as log text and as a command channel:
26
+ // a line containing the marker "##vso[area.event ...]data" is parsed and executed by the
27
+ // agent. When a task echoes output that originates from an untrusted source (a remote
28
+ // server, a child process, a repository filename, compiler/test output, ...), that source
29
+ // can smuggle a "##vso[" marker and make the agent run a command on its behalf.
30
+ //
31
+ // This module neutralizes markers in such external output before it reaches the agent's
32
+ // command parser. By default every marker is blocked (rewritten "##vso[" -> "##_vso[").
33
+ // A task may opt a compatibility-sensitive path into a small, explicit allowlist of
34
+ // command names; unknown, malformed, or future commands stay blocked.
35
+ //
36
+ // The filter works on raw bytes (Buffer) so it never corrupts invalid UTF-8, binary
37
+ // output, or multibyte characters split across chunk boundaries. It matches the marker
38
+ // byte-for-byte, mirroring the agent's Ordinal comparison, so the two agree exactly on
39
+ // what a marker is.
40
+ //
41
+ /** The exact marker bytes the agent recognizes as the start of a command: "##vso[". */
42
+ var MARKER = Buffer.from('##vso[', 'ascii');
43
+ /** Neutralized form written in place of a blocked marker: "##_vso[". */
44
+ var NEUTRALIZED = Buffer.from('##_vso[', 'ascii');
45
+ var EMPTY = Buffer.alloc(0);
46
+ var SPACE = 0x20;
47
+ var RBRACKET = 0x5D; // ]
48
+ var CR = 0x0D;
49
+ var LF = 0x0A;
50
+ // Maximum command-name bytes retained while waiting for a terminator.
51
+ var MAX_HEADER = 256;
52
+ /** Commands allowed by default once VSO commands are enabled for a path. */
53
+ exports.defaultAllowedVsoCommands = Object.freeze(['task.debug', 'task.setprogress']);
54
+ function resolveAllowed(options) {
55
+ if (!options.enableVsoCommands) {
56
+ return new Set();
57
+ }
58
+ var list = options.allowedVsoCommands !== undefined
59
+ ? options.allowedVsoCommands
60
+ : exports.defaultAllowedVsoCommands;
61
+ // Command-name comparison is case-insensitive to match the agent's command lookup.
62
+ return new Set(list.map(function (c) { return c.toLowerCase(); }));
63
+ }
64
+ /**
65
+ * Normalizes a raw command-name token to the agent's canonical "area.event" form, or null
66
+ * when it is not a valid command name. Mirrors the agent's Split('.', RemoveEmptyEntries)
67
+ * with a required length of exactly two non-empty segments.
68
+ */
69
+ function canonicalCommandName(raw) {
70
+ var parts = raw.split('.').filter(function (p) { return p.length > 0; });
71
+ if (parts.length !== 2) {
72
+ return null;
73
+ }
74
+ return (parts[0] + '.' + parts[1]).toLowerCase();
75
+ }
76
+ /** Returns the longest suffix of buf that is a proper marker prefix. */
77
+ function partialMarkerSuffixLength(buf) {
78
+ var max = Math.min(MARKER.length - 1, buf.length);
79
+ for (var k = max; k >= 1; k--) {
80
+ if (buf.compare(MARKER, 0, k, buf.length - k, buf.length) === 0) {
81
+ return k;
82
+ }
83
+ }
84
+ return 0;
85
+ }
86
+ /**
87
+ * Locates the end of a command-name header that starts at `start` (the byte after a marker).
88
+ * The name ends at the first space or ']'. A CR or LF is also reported as a terminator with
89
+ * newline=true so the caller can fail closed. Scans at most MAX_HEADER bytes; term is -1 when
90
+ * no terminator is found within that bound (or before the buffer ends).
91
+ */
92
+ function scanHeader(buf, start) {
93
+ var limit = Math.min(buf.length, start + MAX_HEADER);
94
+ for (var k = start; k < limit; k++) {
95
+ var b = buf[k];
96
+ if (b === SPACE || b === RBRACKET) {
97
+ return { term: k, newline: false };
98
+ }
99
+ if (b === CR || b === LF) {
100
+ return { term: k, newline: true };
101
+ }
102
+ }
103
+ return { term: -1, newline: false };
104
+ }
105
+ /**
106
+ * Stateful, byte-level marker filter. Feed bytes with push() and finish with flush().
107
+ * Retains only the minimal bytes needed to resolve a marker that straddles a chunk
108
+ * boundary, so memory stays bounded regardless of input size.
109
+ */
110
+ var MarkerFilter = /** @class */ (function () {
111
+ function MarkerFilter(enabled, allowed) {
112
+ this.enabled = enabled;
113
+ this.allowed = allowed;
114
+ this.pending = EMPTY;
115
+ }
116
+ MarkerFilter.prototype.push = function (chunk) {
117
+ var buf = this.pending.length ? Buffer.concat([this.pending, chunk]) : chunk;
118
+ this.pending = EMPTY;
119
+ var out = [];
120
+ var pos = 0;
121
+ while (pos < buf.length) {
122
+ var idx = buf.indexOf(MARKER, pos);
123
+ if (idx === -1) {
124
+ // No complete marker remains. Retain a possible partial marker at the tail so
125
+ // it can be completed by the next chunk; emit everything before it.
126
+ var keep = Math.min(partialMarkerSuffixLength(buf), buf.length - pos);
127
+ var emitEnd = buf.length - keep;
128
+ if (emitEnd > pos) {
129
+ out.push(pos === 0 && emitEnd === buf.length ? buf : buf.subarray(pos, emitEnd));
130
+ }
131
+ this.pending = keep ? buf.subarray(buf.length - keep) : EMPTY;
132
+ break;
133
+ }
134
+ if (idx > pos) {
135
+ out.push(buf.subarray(pos, idx));
136
+ }
137
+ var afterMarker = idx + MARKER.length;
138
+ if (!this.enabled) {
139
+ out.push(NEUTRALIZED);
140
+ pos = afterMarker;
141
+ continue;
142
+ }
143
+ // Enabled: read the command name (up to the first space or ']') and allow the marker
144
+ // only when that name is allowlisted.
145
+ var _a = scanHeader(buf, afterMarker), term = _a.term, newline = _a.newline;
146
+ if (term === -1) {
147
+ if (buf.length - afterMarker >= MAX_HEADER) {
148
+ // No terminator within the bound: fail closed.
149
+ out.push(NEUTRALIZED);
150
+ pos = afterMarker;
151
+ continue;
152
+ }
153
+ // Header may complete in a later chunk; retain from the marker start.
154
+ this.pending = buf.subarray(idx);
155
+ break;
156
+ }
157
+ // A CR/LF before the terminator means the agent (which parses per line) would never
158
+ // treat this as a command, so we fail closed and neutralize.
159
+ if (newline) {
160
+ out.push(NEUTRALIZED);
161
+ pos = afterMarker;
162
+ continue;
163
+ }
164
+ var name_1 = canonicalCommandName(buf.toString('utf8', afterMarker, term));
165
+ // Allowlisted markers pass unchanged; the header/data after them flow through as
166
+ // ordinary bytes and any later markers are evaluated independently.
167
+ out.push(name_1 && this.allowed.has(name_1) ? MARKER : NEUTRALIZED);
168
+ pos = afterMarker;
169
+ }
170
+ if (out.length === 0) {
171
+ return EMPTY;
172
+ }
173
+ if (out.length === 1) {
174
+ var only = out[0];
175
+ // Never expose module-level buffers that callers could mutate globally.
176
+ return only === NEUTRALIZED || only === MARKER ? Buffer.from(only) : only;
177
+ }
178
+ return Buffer.concat(out);
179
+ };
180
+ MarkerFilter.prototype.flush = function () {
181
+ var p = this.pending;
182
+ this.pending = EMPTY;
183
+ if (p.length === 0) {
184
+ return EMPTY;
185
+ }
186
+ // An incomplete command candidate that begins with the full marker is neutralized.
187
+ if (this.enabled && p.length >= MARKER.length && p.subarray(0, MARKER.length).equals(MARKER)) {
188
+ return Buffer.concat([NEUTRALIZED, p.subarray(MARKER.length)]);
189
+ }
190
+ // A partial marker (fewer than the full bytes) cannot be executed by the agent, so it
191
+ // is safe to emit unchanged.
192
+ return p;
193
+ };
194
+ return MarkerFilter;
195
+ }());
196
+ exports.MarkerFilter = MarkerFilter;
197
+ /**
198
+ * A Transform stream that neutralizes VSO command markers in external output. Pipe untrusted
199
+ * output into it; it writes filtered bytes to its readable side (and, via
200
+ * createExternalOutputStream, on to the destination).
201
+ */
202
+ var ExternalOutputStream = /** @class */ (function (_super) {
203
+ __extends(ExternalOutputStream, _super);
204
+ function ExternalOutputStream(options) {
205
+ var _this = _super.call(this) || this;
206
+ _this.markerFilter = new MarkerFilter(!!options.enableVsoCommands, resolveAllowed(options));
207
+ return _this;
208
+ }
209
+ ExternalOutputStream.prototype._transform = function (chunk, _encoding, callback) {
210
+ try {
211
+ var buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), 'utf8');
212
+ var out = this.markerFilter.push(buf);
213
+ if (out.length) {
214
+ this.push(out);
215
+ }
216
+ callback();
217
+ }
218
+ catch (err) {
219
+ // Fail closed: never recover by emitting the original marker.
220
+ callback(err);
221
+ }
222
+ };
223
+ ExternalOutputStream.prototype._flush = function (callback) {
224
+ try {
225
+ var out = this.markerFilter.flush();
226
+ if (out.length) {
227
+ this.push(out);
228
+ }
229
+ callback();
230
+ }
231
+ catch (err) {
232
+ callback(err);
233
+ }
234
+ };
235
+ return ExternalOutputStream;
236
+ }(stream.Transform));
237
+ exports.ExternalOutputStream = ExternalOutputStream;
238
+ /**
239
+ * Creates a filtering stream for external output and pipes it to the destination
240
+ * (default process.stdout). Pipe untrusted output into the returned stream, e.g.
241
+ * `jenkinsResponse.pipe(tl.createExternalOutputStream({ source: 'remote' }))`.
242
+ */
243
+ function createExternalOutputStream(options) {
244
+ var filterStream = new ExternalOutputStream(options);
245
+ var destination = options.destination || process.stdout;
246
+ // Do not end the shared destination (e.g. process.stdout) when the source ends.
247
+ filterStream.pipe(destination, { end: false });
248
+ return filterStream;
249
+ }
250
+ exports.createExternalOutputStream = createExternalOutputStream;
251
+ /**
252
+ * Filters a complete piece of external output and writes it to the destination
253
+ * (default process.stdout). Each call is self-contained; for output that arrives in
254
+ * chunks that may split a marker, use createExternalOutputStream instead.
255
+ */
256
+ function writeExternalOutput(data, options) {
257
+ var destination = options.destination || process.stdout;
258
+ destination.write(filterExternalOutput(data, options));
259
+ }
260
+ exports.writeExternalOutput = writeExternalOutput;
261
+ /** Filters one complete value and returns its bytes without writing them. */
262
+ function filterExternalOutput(data, options) {
263
+ var filter = new MarkerFilter(!!options.enableVsoCommands, resolveAllowed(options));
264
+ var buf = Buffer.isBuffer(data) ? data : Buffer.from(String(data), 'utf8');
265
+ var filtered = filter.push(buf);
266
+ var pending = filter.flush();
267
+ return pending.length ? Buffer.concat([filtered, pending]) : filtered;
268
+ }
269
+ exports.filterExternalOutput = filterExternalOutput;
270
+ /** Creates a stateful writer that filters markers split across writes. */
271
+ function createFilteredWriter(options, destination) {
272
+ var filter = new MarkerFilter(!!options.enableVsoCommands, resolveAllowed(options));
273
+ var ended = false;
274
+ return {
275
+ write: function (data) {
276
+ if (ended) {
277
+ throw new Error('Cannot write after end');
278
+ }
279
+ var buf = Buffer.isBuffer(data) ? data : Buffer.from(String(data), 'utf8');
280
+ var filtered = filter.push(buf);
281
+ if (filtered.length) {
282
+ destination.write(filtered);
283
+ }
284
+ },
285
+ end: function () {
286
+ if (ended) {
287
+ return;
288
+ }
289
+ ended = true;
290
+ var pending = filter.flush();
291
+ if (pending.length) {
292
+ destination.write(pending);
293
+ }
294
+ }
295
+ };
296
+ }
297
+ exports.createFilteredWriter = createFilteredWriter;
package/mock-task.d.ts CHANGED
@@ -4,6 +4,7 @@ import Q = require('q');
4
4
  import fs = require('fs');
5
5
  import task = require('./task');
6
6
  import trm = require('./mock-toolrunner');
7
+ import eom = require('./externaloutput');
7
8
  import ma = require('./mock-answer');
8
9
  export declare function setAnswers(answers: ma.TaskLibAnswers): void;
9
10
  export declare function setResourcePath(path: string): void;
@@ -111,5 +112,10 @@ export declare class CodeCoverageEnabler {
111
112
  [key: string]: string;
112
113
  }): void;
113
114
  }
115
+ export type ExternalOutputOptions = eom.ExternalOutputOptions;
116
+ export declare function createExternalOutputStream(options: eom.ExternalOutputOptions): eom.ExternalOutputStream;
117
+ export declare function writeExternalOutput(data: string | Buffer, options: eom.ExternalOutputOptions): void;
118
+ export declare function filterExternalOutput(data: string | Buffer, options: eom.ExternalOutputOptions): Buffer;
119
+ export declare const defaultAllowedVsoCommands: readonly string[];
114
120
  export declare function getHttpProxyConfiguration(requestUrl?: string): task.ProxyConfiguration | null;
115
121
  export declare function getHttpCertConfiguration(): task.CertConfiguration | null;
package/mock-task.js CHANGED
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getHttpCertConfiguration = exports.getHttpProxyConfiguration = exports.CodeCoverageEnabler = exports.CodeCoveragePublisher = exports.TestPublisher = exports.legacyFindFiles = exports.findMatch = exports.tool = exports.execSync = exports.execAsync = exports.exec = exports.mv = exports.rmRF = exports.find = exports.retry = exports.cp = exports.ls = exports.which = exports.resolve = exports.mkdirP = exports.checkPath = exports.popd = exports.pushd = exports.cd = exports.cwd = exports.getAgentMode = exports.getNodeMajorVersion = exports.getPlatform = exports.osType = exports.writeFile = exports.exist = exports.stats = exports.FsStats = exports.loc = exports.setResourcePath = exports.setAnswers = void 0;
3
+ exports.getHttpCertConfiguration = exports.getHttpProxyConfiguration = exports.defaultAllowedVsoCommands = exports.filterExternalOutput = exports.writeExternalOutput = exports.createExternalOutputStream = exports.CodeCoverageEnabler = exports.CodeCoveragePublisher = exports.TestPublisher = exports.legacyFindFiles = exports.findMatch = exports.tool = exports.execSync = exports.execAsync = exports.exec = exports.mv = exports.rmRF = exports.find = exports.retry = exports.cp = exports.ls = exports.which = exports.resolve = exports.mkdirP = exports.checkPath = exports.popd = exports.pushd = exports.cd = exports.cwd = exports.getAgentMode = exports.getNodeMajorVersion = exports.getPlatform = exports.osType = exports.writeFile = exports.exist = exports.stats = exports.FsStats = exports.loc = exports.setResourcePath = exports.setAnswers = void 0;
4
4
  var path = require("path");
5
5
  var task = require("./task");
6
6
  var tcm = require("./taskcommand");
7
7
  var trm = require("./mock-toolrunner");
8
+ var eom = require("./externaloutput");
8
9
  var ma = require("./mock-answer");
9
10
  var mock = new ma.MockAnswers();
10
11
  function setAnswers(answers) {
@@ -455,6 +456,19 @@ exports.updateReleaseName = task.updateReleaseName;
455
456
  exports.TaskCommand = tcm.TaskCommand;
456
457
  exports.commandFromString = tcm.commandFromString;
457
458
  exports.ToolRunner = trm.ToolRunner;
459
+ function createExternalOutputStream(options) {
460
+ return eom.createExternalOutputStream(options);
461
+ }
462
+ exports.createExternalOutputStream = createExternalOutputStream;
463
+ function writeExternalOutput(data, options) {
464
+ eom.writeExternalOutput(data, options);
465
+ }
466
+ exports.writeExternalOutput = writeExternalOutput;
467
+ function filterExternalOutput(data, options) {
468
+ return eom.filterExternalOutput(data, options);
469
+ }
470
+ exports.filterExternalOutput = filterExternalOutput;
471
+ exports.defaultAllowedVsoCommands = eom.defaultAllowedVsoCommands;
458
472
  //-----------------------------------------------------
459
473
  // Http Proxy Helper
460
474
  //-----------------------------------------------------
@@ -3,6 +3,7 @@
3
3
  import Q = require('q');
4
4
  import events = require('events');
5
5
  import ma = require('./mock-answer');
6
+ import eom = require('./externaloutput');
6
7
  export declare function setAnswers(answers: ma.TaskLibAnswers): void;
7
8
  export interface IExecOptions extends IExecSyncOptions {
8
9
  failOnStdErr?: boolean;
@@ -14,9 +15,11 @@ export interface IExecSyncOptions {
14
15
  [key: string]: string | undefined;
15
16
  };
16
17
  silent?: boolean;
17
- outStream: NodeJS.WritableStream;
18
- errStream: NodeJS.WritableStream;
18
+ outStream?: NodeJS.WritableStream;
19
+ errStream?: NodeJS.WritableStream;
19
20
  windowsVerbatimArguments?: boolean;
21
+ shell?: boolean;
22
+ externalOutput?: eom.ExternalOutputOptions;
20
23
  }
21
24
  export interface IExecSyncResult {
22
25
  stdout: string;
@@ -20,6 +20,7 @@ var Q = require("q");
20
20
  var os = require("os");
21
21
  var events = require("events");
22
22
  var ma = require("./mock-answer");
23
+ var eom = require("./externaloutput");
23
24
  var mock = new ma.MockAnswers();
24
25
  function setAnswers(answers) {
25
26
  mock.initialize(answers);
@@ -149,7 +150,9 @@ var ToolRunner = /** @class */ (function (_super) {
149
150
  errStream: options.errStream || process.stderr,
150
151
  failOnStdErr: options.failOnStdErr || false,
151
152
  ignoreReturnCode: options.ignoreReturnCode || false,
152
- windowsVerbatimArguments: options.windowsVerbatimArguments
153
+ windowsVerbatimArguments: options.windowsVerbatimArguments,
154
+ shell: options.shell,
155
+ externalOutput: options.externalOutput
153
156
  };
154
157
  var argString = this.args.join(' ') || '';
155
158
  var cmdString = this.toolPath;
@@ -167,14 +170,16 @@ var ToolRunner = /** @class */ (function (_super) {
167
170
  }
168
171
  cmdString += ' | ' + pipeToolCmdString;
169
172
  }
170
- ops.outStream.write('[command]' + cmdString + os.EOL);
173
+ var commandLine = '[command]' + cmdString + os.EOL;
174
+ ops.outStream.write(ops.externalOutput ? eom.filterExternalOutput(commandLine, ops.externalOutput) : commandLine);
171
175
  }
172
176
  // TODO: filter process.env
173
177
  var res = mock.getResponse('exec', cmdString, debug);
174
178
  if (res.stdout) {
175
179
  this.emit('stdout', res.stdout);
176
180
  if (!ops.silent) {
177
- ops.outStream.write(res.stdout + os.EOL);
181
+ var stdout = res.stdout + os.EOL;
182
+ ops.outStream.write(ops.externalOutput ? eom.filterExternalOutput(stdout, ops.externalOutput) : stdout);
178
183
  }
179
184
  var stdLineArray = res.stdout.split(os.EOL);
180
185
  for (var _i = 0, _a = stdLineArray.slice(0, -1); _i < _a.length; _i++) {
@@ -190,7 +195,8 @@ var ToolRunner = /** @class */ (function (_super) {
190
195
  success = !ops.failOnStdErr;
191
196
  if (!ops.silent) {
192
197
  var s = ops.failOnStdErr ? ops.errStream : ops.outStream;
193
- s.write(res.stderr + os.EOL);
198
+ var stderr = res.stderr + os.EOL;
199
+ s.write(ops.externalOutput ? eom.filterExternalOutput(stderr, ops.externalOutput) : stderr);
194
200
  }
195
201
  var stdErrArray = res.stderr.split(os.EOL);
196
202
  for (var _b = 0, _c = stdErrArray.slice(0, -1); _b < _c.length; _b++) {
@@ -243,7 +249,9 @@ var ToolRunner = /** @class */ (function (_super) {
243
249
  errStream: options.errStream || process.stderr,
244
250
  failOnStdErr: options.failOnStdErr || false,
245
251
  ignoreReturnCode: options.ignoreReturnCode || false,
246
- windowsVerbatimArguments: options.windowsVerbatimArguments
252
+ windowsVerbatimArguments: options.windowsVerbatimArguments,
253
+ shell: options.shell,
254
+ externalOutput: options.externalOutput
247
255
  };
248
256
  var argString = this.args.join(' ') || '';
249
257
  var cmdString = this.toolPath;
@@ -261,14 +269,16 @@ var ToolRunner = /** @class */ (function (_super) {
261
269
  }
262
270
  cmdString += ' | ' + pipeToolCmdString;
263
271
  }
264
- ops.outStream.write('[command]' + cmdString + os.EOL);
272
+ var commandLine = '[command]' + cmdString + os.EOL;
273
+ ops.outStream.write(ops.externalOutput ? eom.filterExternalOutput(commandLine, ops.externalOutput) : commandLine);
265
274
  }
266
275
  // TODO: filter process.env
267
276
  var res = mock.getResponse('exec', cmdString, debug);
268
277
  if (res.stdout) {
269
278
  this.emit('stdout', res.stdout);
270
279
  if (!ops.silent) {
271
- ops.outStream.write(res.stdout + os.EOL);
280
+ var stdout = res.stdout + os.EOL;
281
+ ops.outStream.write(ops.externalOutput ? eom.filterExternalOutput(stdout, ops.externalOutput) : stdout);
272
282
  }
273
283
  var stdLineArray = res.stdout.split(os.EOL);
274
284
  for (var _i = 0, _a = stdLineArray.slice(0, -1); _i < _a.length; _i++) {
@@ -284,7 +294,8 @@ var ToolRunner = /** @class */ (function (_super) {
284
294
  success = !ops.failOnStdErr;
285
295
  if (!ops.silent) {
286
296
  var s = ops.failOnStdErr ? ops.errStream : ops.outStream;
287
- s.write(res.stderr + os.EOL);
297
+ var stderr = res.stderr + os.EOL;
298
+ s.write(ops.externalOutput ? eom.filterExternalOutput(stderr, ops.externalOutput) : stderr);
288
299
  }
289
300
  var stdErrArray = res.stderr.split(os.EOL);
290
301
  for (var _b = 0, _c = stdErrArray.slice(0, -1); _b < _c.length; _b++) {
@@ -333,6 +344,8 @@ var ToolRunner = /** @class */ (function (_super) {
333
344
  outStream: options.outStream || process.stdout,
334
345
  errStream: options.errStream || process.stderr,
335
346
  windowsVerbatimArguments: options.windowsVerbatimArguments,
347
+ shell: options.shell,
348
+ externalOutput: options.externalOutput
336
349
  };
337
350
  var argString = this.args.join(' ') || '';
338
351
  var cmdString = this.toolPath;
@@ -342,14 +355,15 @@ var ToolRunner = /** @class */ (function (_super) {
342
355
  cmdString += (' ' + argString);
343
356
  }
344
357
  if (!ops.silent) {
345
- ops.outStream.write('[command]' + cmdString + os.EOL);
358
+ var commandLine = '[command]' + cmdString + os.EOL;
359
+ ops.outStream.write(ops.externalOutput ? eom.filterExternalOutput(commandLine, ops.externalOutput) : commandLine);
346
360
  }
347
361
  var r = mock.getResponse('exec', cmdString, debug);
348
362
  if (!ops.silent && r.stdout && r.stdout.length > 0) {
349
- ops.outStream.write(r.stdout);
363
+ ops.outStream.write(ops.externalOutput ? eom.filterExternalOutput(r.stdout, ops.externalOutput) : r.stdout);
350
364
  }
351
365
  if (!ops.silent && r.stderr && r.stderr.length > 0) {
352
- ops.errStream.write(r.stderr);
366
+ ops.errStream.write(ops.externalOutput ? eom.filterExternalOutput(r.stderr, ops.externalOutput) : r.stderr);
353
367
  }
354
368
  return {
355
369
  code: r.code,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azure-pipelines-task-lib",
3
- "version": "5.280.0",
3
+ "version": "5.280.1",
4
4
  "description": "Azure Pipelines Task SDK",
5
5
  "main": "./task.js",
6
6
  "typings": "./task.d.ts",
package/task.d.ts CHANGED
@@ -4,6 +4,7 @@ import Q = require('q');
4
4
  import fs = require('fs');
5
5
  import im = require('./internal');
6
6
  import trm = require('./toolrunner');
7
+ import eom = require('./externaloutput');
7
8
  type OptionCases<T extends string> = `-${Uppercase<T> | Lowercase<T>}`;
8
9
  type OptionsPermutations<T extends string, U extends string = ''> = T extends `${infer First}${infer Rest}` ? OptionCases<`${U}${First}`> | OptionCases<`${First}${U}`> | OptionsPermutations<Rest, `${U}${First}`> | OptionCases<First> : OptionCases<U> | '';
9
10
  export declare enum TaskResult {
@@ -833,4 +834,43 @@ export declare function addBuildTag(value: string): void;
833
834
  * @returns void
834
835
  */
835
836
  export declare function updateReleaseName(name: string): void;
837
+ /**
838
+ * Options controlling how external (untrusted) output is filtered before it is written to
839
+ * the log. See {@link eom.ExternalOutputOptions}.
840
+ */
841
+ export type ExternalOutputOptions = eom.ExternalOutputOptions;
842
+ /**
843
+ * Creates a filtering stream for external output and pipes it to the destination
844
+ * (default process.stdout). Pipe untrusted output (a remote response, a child process's
845
+ * stdout, etc.) into the returned stream so that any "##vso[" command markers it contains
846
+ * are neutralized instead of executed by the agent.
847
+ *
848
+ * By default every marker is blocked. Set enableVsoCommands to true (optionally with an
849
+ * explicit allowedVsoCommands list) to permit a small set of command names on a
850
+ * compatibility-sensitive path.
851
+ *
852
+ * @param options External output options. See ExternalOutputOptions.
853
+ * @returns A writable/readable stream to pipe untrusted output into.
854
+ */
855
+ export declare function createExternalOutputStream(options: eom.ExternalOutputOptions): eom.ExternalOutputStream;
856
+ /**
857
+ * Filters a complete piece of external output and writes it to the destination
858
+ * (default process.stdout). Use this for output already held in a string or Buffer; for
859
+ * output that streams in chunks that may split a marker, use createExternalOutputStream.
860
+ *
861
+ * @param data The external output to write.
862
+ * @param options External output options. See ExternalOutputOptions.
863
+ * @returns void
864
+ */
865
+ export declare function writeExternalOutput(data: string | Buffer, options: eom.ExternalOutputOptions): void;
866
+ /**
867
+ * Filters a complete piece of external output and returns its bytes without writing them.
868
+ *
869
+ * @param data The external output to filter.
870
+ * @param options External output options. See ExternalOutputOptions.
871
+ * @returns The filtered output.
872
+ */
873
+ export declare function filterExternalOutput(data: string | Buffer, options: eom.ExternalOutputOptions): Buffer;
874
+ /** Commands allowed when VSO commands are enabled without an explicit allowlist. */
875
+ export declare const defaultAllowedVsoCommands: readonly string[];
836
876
  export {};
package/task.js CHANGED
@@ -10,7 +10,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.getPlatform = exports.osType = exports.writeFile = exports.exist = exports.stats = exports.debug = exports.error = exports.warning = exports.command = exports.setTaskVariable = exports.getTaskVariable = exports.getSecureFileTicket = exports.getSecureFileName = exports.getEndpointAuthorization = exports.getEndpointAuthorizationParameterRequired = exports.getEndpointAuthorizationParameter = exports.getEndpointAuthorizationSchemeRequired = exports.getEndpointAuthorizationScheme = exports.getEndpointDataParameterRequired = exports.getEndpointDataParameter = exports.getEndpointUrlRequired = exports.getEndpointUrl = exports.getPathInputRequired = exports.getPathInput = exports.filePathSupplied = exports.getDelimitedInput = exports.getPipelineFeature = exports.getBoolFeatureFlag = exports.getBoolInput = exports.getInputRequired = exports.getInput = exports.setSecret = exports.setVariable = exports.getVariables = exports.assertAgent = exports.getVariable = exports.loc = exports.setResourcePath = exports.setSanitizedResult = exports.setResult = exports.setErrStream = exports.setStdStream = exports.AgentHostedMode = exports.Platform = exports.IssueSource = exports.FieldType = exports.ArtifactType = exports.IssueType = exports.TaskState = exports.TaskResult = void 0;
13
- exports.updateReleaseName = exports.addBuildTag = exports.updateBuildNumber = exports.uploadBuildLog = exports.associateArtifact = exports.uploadArtifact = exports.logIssue = exports.logDetail = exports.setProgress = exports.setEndpoint = exports.addAttachment = exports.uploadSummary = exports.prependPath = exports.uploadFile = exports.CodeCoverageEnabler = exports.CodeCoveragePublisher = exports.TestPublisher = exports.getHttpCertConfiguration = exports.getHttpProxyConfiguration = exports.findMatch = exports.filter = exports.match = exports.tool = exports.execSync = exports.exec = exports.execAsync = exports.rmRF = exports.legacyFindFiles = exports.find = exports.retry = exports.mv = exports.cp = exports.ls = exports.which = exports.resolve = exports.mkdirP = exports.popd = exports.pushd = exports.cd = exports.checkPath = exports.cwd = exports.getSprint = exports.getAgentMode = exports.getNodeMajorVersion = void 0;
13
+ exports.defaultAllowedVsoCommands = exports.filterExternalOutput = exports.writeExternalOutput = exports.createExternalOutputStream = exports.updateReleaseName = exports.addBuildTag = exports.updateBuildNumber = exports.uploadBuildLog = exports.associateArtifact = exports.uploadArtifact = exports.logIssue = exports.logDetail = exports.setProgress = exports.setEndpoint = exports.addAttachment = exports.uploadSummary = exports.prependPath = exports.uploadFile = exports.CodeCoverageEnabler = exports.CodeCoveragePublisher = exports.TestPublisher = exports.getHttpCertConfiguration = exports.getHttpProxyConfiguration = exports.findMatch = exports.filter = exports.match = exports.tool = exports.execSync = exports.exec = exports.execAsync = exports.rmRF = exports.legacyFindFiles = exports.find = exports.retry = exports.mv = exports.cp = exports.ls = exports.which = exports.resolve = exports.mkdirP = exports.popd = exports.pushd = exports.cd = exports.checkPath = exports.cwd = exports.getSprint = exports.getAgentMode = exports.getNodeMajorVersion = void 0;
14
14
  var childProcess = require("child_process");
15
15
  var fs = require("fs");
16
16
  var path = require("path");
@@ -19,6 +19,7 @@ var minimatch = require("minimatch");
19
19
  var im = require("./internal");
20
20
  var tcm = require("./taskcommand");
21
21
  var trm = require("./toolrunner");
22
+ var eom = require("./externaloutput");
22
23
  var semver = require("semver");
23
24
  var TaskResult;
24
25
  (function (TaskResult) {
@@ -2414,6 +2415,49 @@ exports.updateReleaseName = updateReleaseName;
2414
2415
  exports.TaskCommand = tcm.TaskCommand;
2415
2416
  exports.commandFromString = tcm.commandFromString;
2416
2417
  exports.ToolRunner = trm.ToolRunner;
2418
+ /**
2419
+ * Creates a filtering stream for external output and pipes it to the destination
2420
+ * (default process.stdout). Pipe untrusted output (a remote response, a child process's
2421
+ * stdout, etc.) into the returned stream so that any "##vso[" command markers it contains
2422
+ * are neutralized instead of executed by the agent.
2423
+ *
2424
+ * By default every marker is blocked. Set enableVsoCommands to true (optionally with an
2425
+ * explicit allowedVsoCommands list) to permit a small set of command names on a
2426
+ * compatibility-sensitive path.
2427
+ *
2428
+ * @param options External output options. See ExternalOutputOptions.
2429
+ * @returns A writable/readable stream to pipe untrusted output into.
2430
+ */
2431
+ function createExternalOutputStream(options) {
2432
+ return eom.createExternalOutputStream(options);
2433
+ }
2434
+ exports.createExternalOutputStream = createExternalOutputStream;
2435
+ /**
2436
+ * Filters a complete piece of external output and writes it to the destination
2437
+ * (default process.stdout). Use this for output already held in a string or Buffer; for
2438
+ * output that streams in chunks that may split a marker, use createExternalOutputStream.
2439
+ *
2440
+ * @param data The external output to write.
2441
+ * @param options External output options. See ExternalOutputOptions.
2442
+ * @returns void
2443
+ */
2444
+ function writeExternalOutput(data, options) {
2445
+ eom.writeExternalOutput(data, options);
2446
+ }
2447
+ exports.writeExternalOutput = writeExternalOutput;
2448
+ /**
2449
+ * Filters a complete piece of external output and returns its bytes without writing them.
2450
+ *
2451
+ * @param data The external output to filter.
2452
+ * @param options External output options. See ExternalOutputOptions.
2453
+ * @returns The filtered output.
2454
+ */
2455
+ function filterExternalOutput(data, options) {
2456
+ return eom.filterExternalOutput(data, options);
2457
+ }
2458
+ exports.filterExternalOutput = filterExternalOutput;
2459
+ /** Commands allowed when VSO commands are enabled without an explicit allowlist. */
2460
+ exports.defaultAllowedVsoCommands = eom.defaultAllowedVsoCommands;
2417
2461
  //-----------------------------------------------------
2418
2462
  // Validation Checks
2419
2463
  //-----------------------------------------------------
package/toolrunner.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  /// <reference types="node" />
4
4
  import Q = require('q');
5
5
  import events = require('events');
6
+ import eom = require('./externaloutput');
6
7
  /**
7
8
  * Interface for exec options
8
9
  */
@@ -32,6 +33,13 @@ export interface IExecSyncOptions {
32
33
  windowsVerbatimArguments?: boolean;
33
34
  /** optional. Run command inside of the shell. Defaults to false. */
34
35
  shell?: boolean;
36
+ /**
37
+ * Optional. When set, the tool's *displayed* output (the command line, stdout and stderr
38
+ * copies written to the log) is passed through the external-output marker filter so that
39
+ * "##vso[" commands emitted by the child process are neutralized. Raw stdout/stderr/stdline/
40
+ * errline events and bytes piped to another tool are NOT affected. See ExternalOutputOptions.
41
+ */
42
+ externalOutput?: eom.ExternalOutputOptions;
35
43
  }
36
44
  /**
37
45
  * Interface for exec results returned from synchronous exec functions
@@ -94,6 +102,8 @@ export declare class ToolRunner extends events.EventEmitter {
94
102
  private _windowsQuoteCmdArg;
95
103
  private _uv_quote_cmd_arg;
96
104
  private _cloneExecOptions;
105
+ /** Builds one filtered writer per display destination. */
106
+ private _createDisplayFilter;
97
107
  private _getSpawnOptions;
98
108
  private _getSpawnSyncOptions;
99
109
  private execWithPipingAsync;
package/toolrunner.js CHANGED
@@ -22,6 +22,7 @@ var events = require("events");
22
22
  var child = require("child_process");
23
23
  var im = require("./internal");
24
24
  var fs = require("fs");
25
+ var eom = require("./externaloutput");
25
26
  var ToolRunner = /** @class */ (function (_super) {
26
27
  __extends(ToolRunner, _super);
27
28
  function ToolRunner(toolPath) {
@@ -494,12 +495,37 @@ var ToolRunner = /** @class */ (function (_super) {
494
495
  failOnStdErr: options.failOnStdErr || false,
495
496
  ignoreReturnCode: options.ignoreReturnCode || false,
496
497
  windowsVerbatimArguments: options.windowsVerbatimArguments || false,
497
- shell: options.shell || false
498
+ shell: options.shell || false,
499
+ externalOutput: options.externalOutput
498
500
  };
499
501
  result.outStream = options.outStream || process.stdout;
500
502
  result.errStream = options.errStream || process.stderr;
501
503
  return result;
502
504
  };
505
+ /** Builds one filtered writer per display destination. */
506
+ ToolRunner.prototype._createDisplayFilter = function (options) {
507
+ var ext = options.externalOutput;
508
+ if (!ext) {
509
+ return null;
510
+ }
511
+ var outStream = options.outStream;
512
+ var errDest = options.failOnStdErr ? options.errStream : options.outStream;
513
+ var stdoutWriter = eom.createFilteredWriter(ext, outStream);
514
+ var stderrWriter = errDest === outStream
515
+ ? stdoutWriter
516
+ : eom.createFilteredWriter(ext, errDest);
517
+ return {
518
+ commandLine: function (text) { outStream.write(eom.filterExternalOutput(text, ext)); },
519
+ stdout: function (data) { return stdoutWriter.write(data); },
520
+ stderr: function (data) { return stderrWriter.write(data); },
521
+ finalize: function () {
522
+ stdoutWriter.end();
523
+ if (stderrWriter !== stdoutWriter) {
524
+ stderrWriter.end();
525
+ }
526
+ }
527
+ };
528
+ };
503
529
  ToolRunner.prototype._getSpawnOptions = function (options) {
504
530
  options = options || {};
505
531
  var result = {};
@@ -527,8 +553,14 @@ var ToolRunner = /** @class */ (function (_super) {
527
553
  });
528
554
  var success = true;
529
555
  var optionsNonNull = this._cloneExecOptions(options);
556
+ var df = this._createDisplayFilter(optionsNonNull);
530
557
  if (!optionsNonNull.silent) {
531
- optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
558
+ if (df) {
559
+ df.commandLine(this._getCommandString(optionsNonNull) + os.EOL);
560
+ }
561
+ else {
562
+ optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
563
+ }
532
564
  }
533
565
  var cp;
534
566
  var toolPath = pipeOutputToTool.toolPath;
@@ -550,32 +582,32 @@ var ToolRunner = /** @class */ (function (_super) {
550
582
  fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null;
551
583
  return new Promise(function (resolve, reject) {
552
584
  var _a, _b, _c, _d;
585
+ var complete = function () {
586
+ if (waitingEvents != 0) {
587
+ return;
588
+ }
589
+ if (df) {
590
+ df.finalize();
591
+ }
592
+ if (error) {
593
+ reject(error);
594
+ }
595
+ else {
596
+ resolve(returnCode);
597
+ }
598
+ };
553
599
  if (fileStream) {
554
600
  waitingEvents++;
555
601
  fileStream.on('finish', function () {
556
602
  waitingEvents--; //file write is complete
557
603
  fileStream = null;
558
- if (waitingEvents == 0) {
559
- if (error) {
560
- reject(error);
561
- }
562
- else {
563
- resolve(returnCode);
564
- }
565
- }
604
+ complete();
566
605
  });
567
606
  fileStream.on('error', function (err) {
568
607
  waitingEvents--; //there were errors writing to the file, write is done
569
608
  _this._debug("Failed to pipe output of ".concat(toolPathFirst, " to file ").concat(_this.pipeOutputToFile, ". Error = ").concat(err));
570
609
  fileStream = null;
571
- if (waitingEvents == 0) {
572
- if (error) {
573
- reject(error);
574
- }
575
- else {
576
- resolve(returnCode);
577
- }
578
- }
610
+ complete();
579
611
  });
580
612
  }
581
613
  //pipe stdout of first tool to stdin of second tool
@@ -600,8 +632,13 @@ var ToolRunner = /** @class */ (function (_super) {
600
632
  }
601
633
  successFirst = !optionsNonNull.failOnStdErr;
602
634
  if (!optionsNonNull.silent) {
603
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
604
- s.write(data);
635
+ if (df) {
636
+ df.stderr(data);
637
+ }
638
+ else {
639
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
640
+ s.write(data);
641
+ }
605
642
  }
606
643
  });
607
644
  cpFirst.on('error', function (err) {
@@ -612,9 +649,7 @@ var ToolRunner = /** @class */ (function (_super) {
612
649
  }
613
650
  (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
614
651
  error = new Error(toolPathFirst + ' failed. ' + err.message);
615
- if (waitingEvents == 0) {
616
- reject(error);
617
- }
652
+ complete();
618
653
  });
619
654
  cpFirst.on('close', function (code, signal) {
620
655
  var _a;
@@ -629,20 +664,18 @@ var ToolRunner = /** @class */ (function (_super) {
629
664
  fileStream.end();
630
665
  }
631
666
  (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
632
- if (waitingEvents == 0) {
633
- if (error) {
634
- reject(error);
635
- }
636
- else {
637
- resolve(returnCode);
638
- }
639
- }
667
+ complete();
640
668
  });
641
669
  var stdLineBuffer = '';
642
670
  (_c = cp.stdout) === null || _c === void 0 ? void 0 : _c.on('data', function (data) {
643
671
  _this.emit('stdout', data);
644
672
  if (!optionsNonNull.silent) {
645
- optionsNonNull.outStream.write(data);
673
+ if (df) {
674
+ df.stdout(data);
675
+ }
676
+ else {
677
+ optionsNonNull.outStream.write(data);
678
+ }
646
679
  }
647
680
  stdLineBuffer = _this._processLineBuffer(data, stdLineBuffer, function (line) {
648
681
  _this.emit('stdline', line);
@@ -653,8 +686,13 @@ var ToolRunner = /** @class */ (function (_super) {
653
686
  _this.emit('stderr', data);
654
687
  success = !optionsNonNull.failOnStdErr;
655
688
  if (!optionsNonNull.silent) {
656
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
657
- s.write(data);
689
+ if (df) {
690
+ df.stderr(data);
691
+ }
692
+ else {
693
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
694
+ s.write(data);
695
+ }
658
696
  }
659
697
  errLineBuffer = _this._processLineBuffer(data, errLineBuffer, function (line) {
660
698
  _this.emit('errline', line);
@@ -663,9 +701,7 @@ var ToolRunner = /** @class */ (function (_super) {
663
701
  cp.on('error', function (err) {
664
702
  waitingEvents--; //process is done with errors
665
703
  error = new Error(toolPath + ' failed. ' + err.message);
666
- if (waitingEvents == 0) {
667
- reject(error);
668
- }
704
+ complete();
669
705
  });
670
706
  cp.on('close', function (code, signal) {
671
707
  waitingEvents--; //process is complete
@@ -687,14 +723,7 @@ var ToolRunner = /** @class */ (function (_super) {
687
723
  else if (!success) {
688
724
  error = new Error(toolPath + ' failed with return code: ' + code);
689
725
  }
690
- if (waitingEvents == 0) {
691
- if (error) {
692
- reject(error);
693
- }
694
- else {
695
- resolve(returnCode);
696
- }
697
- }
726
+ complete();
698
727
  });
699
728
  });
700
729
  };
@@ -709,8 +738,14 @@ var ToolRunner = /** @class */ (function (_super) {
709
738
  });
710
739
  var success = true;
711
740
  var optionsNonNull = this._cloneExecOptions(options);
741
+ var df = this._createDisplayFilter(optionsNonNull);
712
742
  if (!optionsNonNull.silent) {
713
- optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
743
+ if (df) {
744
+ df.commandLine(this._getCommandString(optionsNonNull) + os.EOL);
745
+ }
746
+ else {
747
+ optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
748
+ }
714
749
  }
715
750
  var cp;
716
751
  var toolPath = pipeOutputToTool.toolPath;
@@ -730,32 +765,32 @@ var ToolRunner = /** @class */ (function (_super) {
730
765
  waitingEvents++;
731
766
  cp = child.spawn(pipeOutputToTool._getSpawnFileName(optionsNonNull), pipeOutputToTool._getSpawnArgs(optionsNonNull), pipeOutputToTool._getSpawnOptions(optionsNonNull));
732
767
  fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null;
768
+ var complete = function () {
769
+ if (waitingEvents != 0) {
770
+ return;
771
+ }
772
+ if (df) {
773
+ df.finalize();
774
+ }
775
+ if (error) {
776
+ defer.reject(error);
777
+ }
778
+ else {
779
+ defer.resolve(returnCode);
780
+ }
781
+ };
733
782
  if (fileStream) {
734
783
  waitingEvents++;
735
784
  fileStream.on('finish', function () {
736
785
  waitingEvents--; //file write is complete
737
786
  fileStream = null;
738
- if (waitingEvents == 0) {
739
- if (error) {
740
- defer.reject(error);
741
- }
742
- else {
743
- defer.resolve(returnCode);
744
- }
745
- }
787
+ complete();
746
788
  });
747
789
  fileStream.on('error', function (err) {
748
790
  waitingEvents--; //there were errors writing to the file, write is done
749
791
  _this._debug("Failed to pipe output of ".concat(toolPathFirst, " to file ").concat(_this.pipeOutputToFile, ". Error = ").concat(err));
750
792
  fileStream = null;
751
- if (waitingEvents == 0) {
752
- if (error) {
753
- defer.reject(error);
754
- }
755
- else {
756
- defer.resolve(returnCode);
757
- }
758
- }
793
+ complete();
759
794
  });
760
795
  }
761
796
  //pipe stdout of first tool to stdin of second tool
@@ -778,8 +813,13 @@ var ToolRunner = /** @class */ (function (_super) {
778
813
  }
779
814
  successFirst = !optionsNonNull.failOnStdErr;
780
815
  if (!optionsNonNull.silent) {
781
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
782
- s.write(data);
816
+ if (df) {
817
+ df.stderr(data);
818
+ }
819
+ else {
820
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
821
+ s.write(data);
822
+ }
783
823
  }
784
824
  });
785
825
  cpFirst.on('error', function (err) {
@@ -790,9 +830,7 @@ var ToolRunner = /** @class */ (function (_super) {
790
830
  }
791
831
  (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
792
832
  error = new Error(toolPathFirst + ' failed. ' + err.message);
793
- if (waitingEvents == 0) {
794
- defer.reject(error);
795
- }
833
+ complete();
796
834
  });
797
835
  cpFirst.on('close', function (code, signal) {
798
836
  var _a;
@@ -807,20 +845,18 @@ var ToolRunner = /** @class */ (function (_super) {
807
845
  fileStream.end();
808
846
  }
809
847
  (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
810
- if (waitingEvents == 0) {
811
- if (error) {
812
- defer.reject(error);
813
- }
814
- else {
815
- defer.resolve(returnCode);
816
- }
817
- }
848
+ complete();
818
849
  });
819
850
  var stdLineBuffer = '';
820
851
  (_c = cp.stdout) === null || _c === void 0 ? void 0 : _c.on('data', function (data) {
821
852
  _this.emit('stdout', data);
822
853
  if (!optionsNonNull.silent) {
823
- optionsNonNull.outStream.write(data);
854
+ if (df) {
855
+ df.stdout(data);
856
+ }
857
+ else {
858
+ optionsNonNull.outStream.write(data);
859
+ }
824
860
  }
825
861
  stdLineBuffer = _this._processLineBuffer(data, stdLineBuffer, function (line) {
826
862
  _this.emit('stdline', line);
@@ -831,8 +867,13 @@ var ToolRunner = /** @class */ (function (_super) {
831
867
  _this.emit('stderr', data);
832
868
  success = !optionsNonNull.failOnStdErr;
833
869
  if (!optionsNonNull.silent) {
834
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
835
- s.write(data);
870
+ if (df) {
871
+ df.stderr(data);
872
+ }
873
+ else {
874
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
875
+ s.write(data);
876
+ }
836
877
  }
837
878
  errLineBuffer = _this._processLineBuffer(data, errLineBuffer, function (line) {
838
879
  _this.emit('errline', line);
@@ -841,9 +882,7 @@ var ToolRunner = /** @class */ (function (_super) {
841
882
  cp.on('error', function (err) {
842
883
  waitingEvents--; //process is done with errors
843
884
  error = new Error(toolPath + ' failed. ' + err.message);
844
- if (waitingEvents == 0) {
845
- defer.reject(error);
846
- }
885
+ complete();
847
886
  });
848
887
  cp.on('close', function (code, signal) {
849
888
  waitingEvents--; //process is complete
@@ -865,14 +904,7 @@ var ToolRunner = /** @class */ (function (_super) {
865
904
  else if (!success) {
866
905
  error = new Error(toolPath + ' failed with return code: ' + code);
867
906
  }
868
- if (waitingEvents == 0) {
869
- if (error) {
870
- defer.reject(error);
871
- }
872
- else {
873
- defer.resolve(returnCode);
874
- }
875
- }
907
+ complete();
876
908
  });
877
909
  return defer.promise;
878
910
  };
@@ -961,8 +993,14 @@ var ToolRunner = /** @class */ (function (_super) {
961
993
  _this._debug(' ' + arg);
962
994
  });
963
995
  var optionsNonNull = this._cloneExecOptions(options);
996
+ var df = this._createDisplayFilter(optionsNonNull);
964
997
  if (!optionsNonNull.silent) {
965
- optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
998
+ if (df) {
999
+ df.commandLine(this._getCommandString(optionsNonNull) + os.EOL);
1000
+ }
1001
+ else {
1002
+ optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
1003
+ }
966
1004
  }
967
1005
  var state = new ExecState(optionsNonNull, this.toolPath);
968
1006
  state.on('debug', function (message) {
@@ -1007,15 +1045,25 @@ var ToolRunner = /** @class */ (function (_super) {
1007
1045
  // it is possible for the child process to end its last line without a new line.
1008
1046
  // because stdout is buffered, this causes the last line to not get sent to the parent
1009
1047
  // stream. Adding this event forces a flush before the child streams are closed.
1010
- (_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('finish', function () {
1048
+ (_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('end', function () {
1011
1049
  if (!optionsNonNull.silent) {
1012
- optionsNonNull.outStream.write(os.EOL);
1050
+ if (df) {
1051
+ df.stdout(Buffer.from(os.EOL));
1052
+ }
1053
+ else {
1054
+ optionsNonNull.outStream.write(os.EOL);
1055
+ }
1013
1056
  }
1014
1057
  });
1015
1058
  (_b = cp.stdout) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
1016
1059
  _this.emit('stdout', data);
1017
1060
  if (!optionsNonNull.silent) {
1018
- optionsNonNull.outStream.write(data);
1061
+ if (df) {
1062
+ df.stdout(data);
1063
+ }
1064
+ else {
1065
+ optionsNonNull.outStream.write(data);
1066
+ }
1019
1067
  }
1020
1068
  stdLineBuffer = _this._processLineBuffer(data, stdLineBuffer, function (line) {
1021
1069
  _this.emit('stdline', line);
@@ -1025,8 +1073,13 @@ var ToolRunner = /** @class */ (function (_super) {
1025
1073
  state.processStderr = true;
1026
1074
  _this.emit('stderr', data);
1027
1075
  if (!optionsNonNull.silent) {
1028
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
1029
- s.write(data);
1076
+ if (df) {
1077
+ df.stderr(data);
1078
+ }
1079
+ else {
1080
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
1081
+ s.write(data);
1082
+ }
1030
1083
  }
1031
1084
  errLineBuffer = _this._processLineBuffer(data, errLineBuffer, function (line) {
1032
1085
  _this.emit('errline', line);
@@ -1046,6 +1099,9 @@ var ToolRunner = /** @class */ (function (_super) {
1046
1099
  state.CheckComplete();
1047
1100
  });
1048
1101
  cp.on('close', function (code, signal) {
1102
+ if (df) {
1103
+ df.finalize();
1104
+ }
1049
1105
  state.processCloseCode = code;
1050
1106
  state.processCloseSignal = signal;
1051
1107
  state.processClosed = true;
@@ -1077,8 +1133,14 @@ var ToolRunner = /** @class */ (function (_super) {
1077
1133
  _this._debug(' ' + arg);
1078
1134
  });
1079
1135
  var optionsNonNull = this._cloneExecOptions(options);
1136
+ var df = this._createDisplayFilter(optionsNonNull);
1080
1137
  if (!optionsNonNull.silent) {
1081
- optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
1138
+ if (df) {
1139
+ df.commandLine(this._getCommandString(optionsNonNull) + os.EOL);
1140
+ }
1141
+ else {
1142
+ optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
1143
+ }
1082
1144
  }
1083
1145
  var state = new ExecState(optionsNonNull, this.toolPath);
1084
1146
  state.on('debug', function (message) {
@@ -1119,15 +1181,25 @@ var ToolRunner = /** @class */ (function (_super) {
1119
1181
  // it is possible for the child process to end its last line without a new line.
1120
1182
  // because stdout is buffered, this causes the last line to not get sent to the parent
1121
1183
  // stream. Adding this event forces a flush before the child streams are closed.
1122
- (_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('finish', function () {
1184
+ (_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('end', function () {
1123
1185
  if (!optionsNonNull.silent) {
1124
- optionsNonNull.outStream.write(os.EOL);
1186
+ if (df) {
1187
+ df.stdout(Buffer.from(os.EOL));
1188
+ }
1189
+ else {
1190
+ optionsNonNull.outStream.write(os.EOL);
1191
+ }
1125
1192
  }
1126
1193
  });
1127
1194
  (_b = cp.stdout) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
1128
1195
  _this.emit('stdout', data);
1129
1196
  if (!optionsNonNull.silent) {
1130
- optionsNonNull.outStream.write(data);
1197
+ if (df) {
1198
+ df.stdout(data);
1199
+ }
1200
+ else {
1201
+ optionsNonNull.outStream.write(data);
1202
+ }
1131
1203
  }
1132
1204
  stdLineBuffer = _this._processLineBuffer(data, stdLineBuffer, function (line) {
1133
1205
  _this.emit('stdline', line);
@@ -1137,8 +1209,13 @@ var ToolRunner = /** @class */ (function (_super) {
1137
1209
  state.processStderr = true;
1138
1210
  _this.emit('stderr', data);
1139
1211
  if (!optionsNonNull.silent) {
1140
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
1141
- s.write(data);
1212
+ if (df) {
1213
+ df.stderr(data);
1214
+ }
1215
+ else {
1216
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
1217
+ s.write(data);
1218
+ }
1142
1219
  }
1143
1220
  errLineBuffer = _this._processLineBuffer(data, errLineBuffer, function (line) {
1144
1221
  _this.emit('errline', line);
@@ -1158,6 +1235,9 @@ var ToolRunner = /** @class */ (function (_super) {
1158
1235
  state.CheckComplete();
1159
1236
  });
1160
1237
  cp.on('close', function (code, signal) {
1238
+ if (df) {
1239
+ df.finalize();
1240
+ }
1161
1241
  state.processCloseCode = code;
1162
1242
  state.processCloseSignal = signal;
1163
1243
  state.processClosed = true;
@@ -1185,15 +1265,17 @@ var ToolRunner = /** @class */ (function (_super) {
1185
1265
  });
1186
1266
  var success = true;
1187
1267
  options = this._cloneExecOptions(options);
1268
+ var ext = options.externalOutput;
1188
1269
  if (!options.silent) {
1189
- options.outStream.write(this._getCommandString(options) + os.EOL);
1270
+ var cmdLine = this._getCommandString(options) + os.EOL;
1271
+ options.outStream.write(ext ? eom.filterExternalOutput(cmdLine, ext) : cmdLine);
1190
1272
  }
1191
1273
  var r = child.spawnSync(this._getSpawnFileName(options), this._getSpawnArgs(options), this._getSpawnSyncOptions(options));
1192
1274
  if (!options.silent && r.stdout && r.stdout.length > 0) {
1193
- options.outStream.write(r.stdout);
1275
+ options.outStream.write(ext ? eom.filterExternalOutput(r.stdout, ext) : r.stdout);
1194
1276
  }
1195
1277
  if (!options.silent && r.stderr && r.stderr.length > 0) {
1196
- options.errStream.write(r.stderr);
1278
+ options.errStream.write(ext ? eom.filterExternalOutput(r.stderr, ext) : r.stderr);
1197
1279
  }
1198
1280
  var res = { code: r.status, error: r.error };
1199
1281
  res.stdout = (r.stdout) ? r.stdout.toString() : '';