"use strict";
var _a, _b;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSigPipeError = exports._exposeCertSettings = exports._exposeProxySettings = exports._normalizeSeparators = exports._isRooted = exports._getDirectoryName = exports._ensureRooted = exports._isUncPath = exports._loadData = exports._ensurePatternRooted = exports._getFindInfoFromPattern = exports._cloneMatchOptions = exports._legacyFindFiles_convertPatternToRegExp = exports._which = exports._checkPath = exports._exist = exports._debug = exports._error = exports._warning = exports._command = exports._getVariableKey = exports._getVariable = exports._loc = exports._setResourcePath = exports._setErrStream = exports._setStdStream = exports._writeLine = exports._truncateBeforeSensitiveKeyword = exports._endsWith = exports._startsWith = exports.IssueAuditAction = exports.IssueSource = exports._vault = exports._knownVariableMap = void 0;
var fs = require("fs");
var path = require("path");
var os = require("os");
var minimatch = require("minimatch");
var util = require("util");
var tcm = require("./taskcommand");
var vm = require("./vault");
var semver = require("semver");
var crypto = require("crypto");
/**
 * Hash table of known variable info. The formatted env var name is the lookup key.
 *
 * The purpose of this hash table is to keep track of known variables. The hash table
 * needs to be maintained for multiple reasons:
 *  1) to distinguish between env vars and job vars
 *  2) to distinguish between secret vars and public
 *  3) to know the real variable name and not just the formatted env var name.
 */
exports._knownVariableMap = {};
var _commandCorrelationId;
//-----------------------------------------------------
// Enums
//-----------------------------------------------------
var IssueSource;
(function (IssueSource) {
    IssueSource["CustomerScript"] = "CustomerScript";
    IssueSource["TaskInternal"] = "TaskInternal";
})(IssueSource = exports.IssueSource || (exports.IssueSource = {}));
var IssueAuditAction;
(function (IssueAuditAction) {
    IssueAuditAction[IssueAuditAction["Unknown"] = 0] = "Unknown";
    IssueAuditAction[IssueAuditAction["ShellTasksValidation"] = 1] = "ShellTasksValidation";
})(IssueAuditAction = exports.IssueAuditAction || (exports.IssueAuditAction = {}));
//-----------------------------------------------------
// Validation Checks
//-----------------------------------------------------
// async await needs generators in node 4.x+
if (semver.lt(process.versions.node, '4.2.0')) {
    _warning('Tasks require a new agent.  Upgrade your agent or node to 4.2.0 or later', IssueSource.TaskInternal);
}
//-----------------------------------------------------
// String convenience
//-----------------------------------------------------
function _startsWith(str, start) {
    return str.slice(0, start.length) == start;
}
exports._startsWith = _startsWith;
function _endsWith(str, end) {
    return str.slice(-end.length) == end;
}
exports._endsWith = _endsWith;
function _truncateBeforeSensitiveKeyword(str, sensitiveKeywordsPattern) {
    if (!str) {
        return str;
    }
    var index = str.search(sensitiveKeywordsPattern);
    if (index <= 0) {
        return str;
    }
    return "".concat(str.substring(0, index), "...");
}
exports._truncateBeforeSensitiveKeyword = _truncateBeforeSensitiveKeyword;
//-----------------------------------------------------
// General Helpers
//-----------------------------------------------------
var _outStream = process.stdout;
var _errStream = process.stderr;
function _writeLine(str) {
    _outStream.write(str + os.EOL);
}
exports._writeLine = _writeLine;
function _setStdStream(stdStream) {
    _outStream = stdStream;
}
exports._setStdStream = _setStdStream;
function _setErrStream(errStream) {
    _errStream = errStream;
}
exports._setErrStream = _setErrStream;
//-----------------------------------------------------
// Loc Helpers
//-----------------------------------------------------
var _locStringCache = {};
var _resourceFiles = {};
var _libResourceFileLoaded = false;
var _resourceCulture = 'en-US';
function _loadResJson(resjsonFile) {
    var resJson;
    if (_exist(resjsonFile)) {
        var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString();
        // remove BOM
        if (resjsonContent.indexOf('\uFEFF') == 0) {
            resjsonContent = resjsonContent.slice(1);
        }
        try {
            resJson = JSON.parse(resjsonContent);
        }
        catch (err) {
            _debug('unable to parse resjson with err: ' + err.message);
        }
    }
    else {
        _debug('.resjson file not found: ' + resjsonFile);
    }
    return resJson;
}
function _loadLocStrings(resourceFile, culture) {
    var locStrings = {};
    if (_exist(resourceFile)) {
        var resourceJson = require(resourceFile);
        if (resourceJson && resourceJson.hasOwnProperty('messages')) {
            var locResourceJson;
            // load up resource resjson for different culture
            var localizedResourceFile = path.join(path.dirname(resourceFile), 'Strings', 'resources.resjson');
            var upperCulture = culture.toUpperCase();
            var cultures = [];
            try {
                cultures = fs.readdirSync(localizedResourceFile);
            }
            catch (ex) { }
            for (var i = 0; i < cultures.length; i++) {
                if (cultures[i].toUpperCase() == upperCulture) {
                    localizedResourceFile = path.join(localizedResourceFile, cultures[i], 'resources.resjson');
                    if (_exist(localizedResourceFile)) {
                        locResourceJson = _loadResJson(localizedResourceFile);
                    }
                    break;
                }
            }
            for (var key in resourceJson.messages) {
                if (locResourceJson && locResourceJson.hasOwnProperty('loc.messages.' + key)) {
                    locStrings[key] = locResourceJson['loc.messages.' + key];
                }
                else {
                    locStrings[key] = resourceJson.messages[key];
                }
            }
        }
    }
    else {
        _warning('LIB_ResourceFile does not exist', IssueSource.TaskInternal);
    }
    return locStrings;
}
/**
 * Sets the location of the resources json.  This is typically the task.json file.
 * Call once at the beginning of the script before any calls to loc.
 * @param     path      Full path to the json.
 * @param     ignoreWarnings  Won't throw warnings if path already set.
 * @returns   void
 */
function _setResourcePath(path, ignoreWarnings) {
    if (ignoreWarnings === void 0) { ignoreWarnings = false; }
    if (process.env['TASKLIB_INPROC_UNITS']) {
        _resourceFiles = {};
        _libResourceFileLoaded = false;
        _locStringCache = {};
        _resourceCulture = 'en-US';
    }
    if (!_resourceFiles[path]) {
        _checkPath(path, 'resource file path');
        _resourceFiles[path] = path;
        _debug('adding resource file: ' + path);
        _resourceCulture = _getVariable('system.culture') || _resourceCulture;
        var locStrs = _loadLocStrings(path, _resourceCulture);
        for (var key in locStrs) {
            //cache loc string
            _locStringCache[key] = locStrs[key];
        }
    }
    else {
        if (ignoreWarnings) {
        }
        else {
            _warning(_loc('LIB_ResourceFileAlreadySet', path), IssueSource.TaskInternal);
        }
    }
}
exports._setResourcePath = _setResourcePath;
/**
 * Gets the localized string from the json resource file.  Optionally formats with additional params.
 *
 * @param     key      key of the resources string in the resource file
 * @param     param    additional params for formatting the string
 * @returns   string
 */
function _loc(key) {
    var param = [];
    for (var _i = 1; _i < arguments.length; _i++) {
        param[_i - 1] = arguments[_i];
    }
    if (!_libResourceFileLoaded) {
        // merge loc strings from azure-pipelines-task-lib.
        var libResourceFile = path.join(__dirname, 'lib.json');
        var libLocStrs = _loadLocStrings(libResourceFile, _resourceCulture);
        for (var libKey in libLocStrs) {
            //cache azure-pipelines-task-lib loc string
            _locStringCache[libKey] = libLocStrs[libKey];
        }
        _libResourceFileLoaded = true;
    }
    var locString;
    ;
    if (_locStringCache.hasOwnProperty(key)) {
        locString = _locStringCache[key];
    }
    else {
        if (Object.keys(_resourceFiles).length <= 0) {
            _warning("Resource file haven't been set, can't find loc string for key: ".concat(key), IssueSource.TaskInternal);
        }
        else {
            _warning("Can't find loc string for key: ".concat(key));
        }
        locString = key;
    }
    if (param.length > 0) {
        return util.format.apply(this, [locString].concat(param));
    }
    else {
        return locString;
    }
}
exports._loc = _loc;
//-----------------------------------------------------
// Input Helpers
//-----------------------------------------------------
/**
 * Gets a variable value that is defined on the build/release definition or set at runtime.
 *
 * @param     name     name of the variable to get
 * @returns   string
 */
function _getVariable(name) {
    var varval;
    // get the metadata
    var info;
    var key = _getVariableKey(name);
    if (exports._knownVariableMap.hasOwnProperty(key)) {
        info = exports._knownVariableMap[key];
    }
    if (info && info.secret) {
        // get the secret value
        varval = exports._vault.retrieveSecret('SECRET_' + key);
    }
    else {
        // get the public value
        varval = process.env[key];
        // fallback for pre 2.104.1 agent
        if (!varval && name.toUpperCase() == 'AGENT.JOBSTATUS') {
            varval = process.env['agent.jobstatus'];
        }
    }
    _debug(name + '=' + varval);
    return varval;
}
exports._getVariable = _getVariable;
function _getVariableKey(name) {
    if (!name) {
        throw new Error(_loc('LIB_ParameterIsRequired', 'name'));
    }
    return name.replace(/\./g, '_').replace(/ /g, '_').toUpperCase();
}
exports._getVariableKey = _getVariableKey;
//-----------------------------------------------------
// Cmd Helpers
//-----------------------------------------------------
function _command(command, properties, message) {
    var taskCmd = new tcm.TaskCommand(command, properties, message);
    _writeLine(taskCmd.toString());
}
exports._command = _command;
function _warning(message, source, auditAction) {
    if (source === void 0) { source = IssueSource.TaskInternal; }
    _command('task.issue', {
        'type': 'warning',
        'source': source,
        'correlationId': _commandCorrelationId,
        'auditAction': auditAction
    }, message);
}
exports._warning = _warning;
function _error(message, source, auditAction) {
    if (source === void 0) { source = IssueSource.TaskInternal; }
    _command('task.issue', {
        'type': 'error',
        'source': source,
        'correlationId': _commandCorrelationId,
        'auditAction': auditAction
    }, message);
}
exports._error = _error;
var debugMode = ((_a = _getVariable('system.debug')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
var shouldCheckDebugMode = ((_b = _getVariable('DistributedTask.Tasks.Node.SkipDebugLogsWhenDebugModeOff')) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'true';
function _debug(message) {
    if (!shouldCheckDebugMode
        || (shouldCheckDebugMode && debugMode)) {
        _command('task.debug', null, message);
    }
}
exports._debug = _debug;
// //-----------------------------------------------------
// // Disk Functions
// //-----------------------------------------------------
/**
 * Returns whether a path exists.
 *
 * @param     path      path to check
 * @returns   boolean
 */
function _exist(path) {
    var exist = false;
    try {
        exist = !!(path && fs.statSync(path) != null);
    }
    catch (err) {
        if (err && err.code === 'ENOENT') {
            exist = false;
        }
        else {
            throw err;
        }
    }
    return exist;
}
exports._exist = _exist;
/**
 * Checks whether a path exists.
 * If the path does not exist, it will throw.
 *
 * @param     p         path to check
 * @param     name      name only used in error message to identify the path
 * @returns   void
 */
function _checkPath(p, name) {
    _debug('check path : ' + p);
    if (!_exist(p)) {
        throw new Error(_loc('LIB_PathNotFound', name, p));
    }
}
exports._checkPath = _checkPath;
/**
 * Returns path of a tool had the tool actually been invoked.  Resolves via paths.
 * If you check and the tool does not exist, it will throw.
 *
 * @param     tool       name of the tool
 * @param     check      whether to check if tool exists
 * @returns   string
 */
function _which(tool, check) {
    if (!tool) {
        throw new Error('parameter \'tool\' is required');
    }
    // recursive when check=true
    if (check) {
        var result = _which(tool, false);
        if (result) {
            return result;
        }
        else {
            if (process.platform == 'win32') {
                throw new Error(_loc('LIB_WhichNotFound_Win', tool));
            }
            else {
                throw new Error(_loc('LIB_WhichNotFound_Linux', tool));
            }
        }
    }
    _debug("which '".concat(tool, "'"));
    try {
        // build the list of extensions to try
        var extensions = [];
        if (process.platform == 'win32' && process.env['PATHEXT']) {
            for (var _i = 0, _a = process.env['PATHEXT'].split(path.delimiter); _i < _a.length; _i++) {
                var extension = _a[_i];
                if (extension) {
                    extensions.push(extension);
                }
            }
        }
        // if it's rooted, return it if exists. otherwise return empty.
        if (_isRooted(tool)) {
            var filePath = _tryGetExecutablePath(tool, extensions);
            if (filePath) {
                _debug("found: '".concat(filePath, "'"));
                return filePath;
            }
            _debug('not found');
            return '';
        }
        // if any path separators, return empty
        if (tool.indexOf('/') >= 0 || (process.platform == 'win32' && tool.indexOf('\\') >= 0)) {
            _debug('not found');
            return '';
        }
        // build the list of directories
        //
        // Note, technically "where" checks the current directory on Windows. From a task lib perspective,
        // it feels like we should not do this. Checking the current directory seems like more of a use
        // case of a shell, and the which() function exposed by the task lib should strive for consistency
        // across platforms.
        var directories = [];
        if (process.env['PATH']) {
            for (var _b = 0, _c = process.env['PATH'].split(path.delimiter); _b < _c.length; _b++) {
                var p = _c[_b];
                if (p) {
                    directories.push(p);
                }
            }
        }
        // return the first match
        for (var _d = 0, directories_1 = directories; _d < directories_1.length; _d++) {
            var directory = directories_1[_d];
            var filePath = _tryGetExecutablePath(directory + path.sep + tool, extensions);
            if (filePath) {
                _debug("found: '".concat(filePath, "'"));
                return filePath;
            }
        }
        _debug('not found');
        return '';
    }
    catch (err) {
        throw new Error(_loc('LIB_OperationFailed', 'which', err.message));
    }
}
exports._which = _which;
/**
 * Best effort attempt to determine whether a file exists and is executable.
 * @param filePath    file path to check
 * @param extensions  additional file extensions to try
 * @return if file exists and is executable, returns the file path. otherwise empty string.
 */
function _tryGetExecutablePath(filePath, extensions) {
    try {
        // test file exists
        var stats = fs.statSync(filePath);
        if (stats.isFile()) {
            if (process.platform == 'win32') {
                // on Windows, test for valid extension
                var isExecutable = false;
                var fileName = path.basename(filePath);
                var dotIndex = fileName.lastIndexOf('.');
                if (dotIndex >= 0) {
                    var upperExt_1 = fileName.substr(dotIndex).toUpperCase();
                    if (extensions.some(function (validExt) { return validExt.toUpperCase() == upperExt_1; })) {
                        return filePath;
                    }
                }
            }
            else {
                if (isUnixExecutable(stats)) {
                    return filePath;
                }
            }
        }
    }
    catch (err) {
        if (err.code != 'ENOENT') {
            _debug("Unexpected error attempting to determine if executable file exists '".concat(filePath, "': ").concat(err));
        }
    }
    // try each extension
    var originalFilePath = filePath;
    for (var _i = 0, extensions_1 = extensions; _i < extensions_1.length; _i++) {
        var extension = extensions_1[_i];
        var found = false;
        var filePath_1 = originalFilePath + extension;
        try {
            var stats = fs.statSync(filePath_1);
            if (stats.isFile()) {
                if (process.platform == 'win32') {
                    // preserve the case of the actual file (since an extension was appended)
                    try {
                        var directory = path.dirname(filePath_1);
                        var upperName = path.basename(filePath_1).toUpperCase();
                        for (var _a = 0, _b = fs.readdirSync(directory); _a < _b.length; _a++) {
                            var actualName = _b[_a];
                            if (upperName == actualName.toUpperCase()) {
                                filePath_1 = path.join(directory, actualName);
                                break;
                            }
                        }
                    }
                    catch (err) {
                        _debug("Unexpected error attempting to determine the actual case of the file '".concat(filePath_1, "': ").concat(err));
                    }
                    return filePath_1;
                }
                else {
                    if (isUnixExecutable(stats)) {
                        return filePath_1;
                    }
                }
            }
        }
        catch (err) {
            if (err.code != 'ENOENT') {
                _debug("Unexpected error attempting to determine if executable file exists '".concat(filePath_1, "': ").concat(err));
            }
        }
    }
    return '';
}
// on Mac/Linux, test the execute bit
//     R   W  X  R  W X R W X
//   256 128 64 32 16 8 4 2 1
function isUnixExecutable(stats) {
    return (stats.mode & 1) > 0 || ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || ((stats.mode & 64) > 0 && stats.uid === process.getuid());
}
function _legacyFindFiles_convertPatternToRegExp(pattern) {
    pattern = (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern) // normalize separator on Windows
        .replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // regex escape - from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
        .replace(/\\\/\\\*\\\*\\\//g, '((\/.+/)|(\/))') // replace directory globstar, e.g. /hello/**/world
        .replace(/\\\*\\\*/g, '.*') // replace remaining globstars with a wildcard that can span directory separators, e.g. /hello/**dll
        .replace(/\\\*/g, '[^\/]*') // replace asterisks with a wildcard that cannot span directory separators, e.g. /hello/*.dll
        .replace(/\\\?/g, '[^\/]'); // replace single character wildcards, e.g. /hello/log?.dll
    pattern = "^".concat(pattern, "$");
    var flags = process.platform == 'win32' ? 'i' : '';
    return new RegExp(pattern, flags);
}
exports._legacyFindFiles_convertPatternToRegExp = _legacyFindFiles_convertPatternToRegExp;
function _cloneMatchOptions(matchOptions) {
    return {
        debug: matchOptions.debug,
        nobrace: matchOptions.nobrace,
        noglobstar: matchOptions.noglobstar,
        dot: matchOptions.dot,
        noext: matchOptions.noext,
        nocase: matchOptions.nocase,
        nonull: matchOptions.nonull,
        matchBase: matchOptions.matchBase,
        nocomment: matchOptions.nocomment,
        nonegate: matchOptions.nonegate,
        flipNegate: matchOptions.flipNegate
    };
}
exports._cloneMatchOptions = _cloneMatchOptions;
function _getFindInfoFromPattern(defaultRoot, pattern, matchOptions) {
    // parameter validation
    if (!defaultRoot) {
        throw new Error('getFindRootFromPattern() parameter defaultRoot cannot be empty');
    }
    if (!pattern) {
        throw new Error('getFindRootFromPattern() parameter pattern cannot be empty');
    }
    if (!matchOptions.nobrace) {
        throw new Error('getFindRootFromPattern() expected matchOptions.nobrace to be true');
    }
    // for the sake of determining the findPath, pretend nocase=false
    matchOptions = _cloneMatchOptions(matchOptions);
    matchOptions.nocase = false;
    // check if basename only and matchBase=true
    if (matchOptions.matchBase &&
        !_isRooted(pattern) &&
        (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern).indexOf('/') < 0) {
        return {
            adjustedPattern: pattern,
            findPath: defaultRoot,
            statOnly: false,
        };
    }
    // the technique applied by this function is to use the information on the Minimatch object determine
    // the findPath. Minimatch breaks the pattern into path segments, and exposes information about which
    // segments are literal vs patterns.
    //
    // note, the technique currently imposes a limitation for drive-relative paths with a glob in the
    // first segment, e.g. C:hello*/world. it's feasible to overcome this limitation, but is left unsolved
    // for now.
    var minimatchObj = new minimatch.Minimatch(pattern, matchOptions);
    // the "set" property is an array of arrays of parsed path segment info. the outer array should only
    // contain one item, otherwise something went wrong. brace expansion can result in multiple arrays,
    // but that should be turned off by the time this function is reached.
    if (minimatchObj.set.length != 1) {
        throw new Error('getFindRootFromPattern() expected Minimatch(...).set.length to be 1. Actual: ' + minimatchObj.set.length);
    }
    var literalSegments = [];
    for (var _i = 0, _a = minimatchObj.set[0]; _i < _a.length; _i++) {
        var parsedSegment = _a[_i];
        if (typeof parsedSegment == 'string') {
            // the item is a string when the original input for the path segment does not contain any
            // unescaped glob characters.
            //
            // note, the string here is already unescaped (i.e. glob escaping removed), so it is ready
            // to pass to find() as-is. for example, an input string 'hello\\*world' => 'hello*world'.
            literalSegments.push(parsedSegment);
            continue;
        }
        break;
    }
    // join the literal segments back together. Minimatch converts '\' to '/' on Windows, then squashes
    // consequetive slashes, and finally splits on slash. this means that UNC format is lost, but can
    // be detected from the original pattern.
    var joinedSegments = literalSegments.join('/');
    if (joinedSegments && process.platform == 'win32' && _startsWith(pattern.replace(/\\/g, '/'), '//')) {
        joinedSegments = '/' + joinedSegments; // restore UNC format
    }
    // determine the find path
    var findPath;
    if (_isRooted(pattern)) { // the pattern was rooted
        findPath = joinedSegments;
    }
    else if (joinedSegments) { // the pattern was not rooted, and literal segments were found
        findPath = _ensureRooted(defaultRoot, joinedSegments);
    }
    else { // the pattern was not rooted, and no literal segments were found
        findPath = defaultRoot;
    }
    // clean up the path
    if (findPath) {
        findPath = _getDirectoryName(_ensureRooted(findPath, '_')); // hack to remove unnecessary trailing slash
        findPath = _normalizeSeparators(findPath); // normalize slashes
    }
    return {
        adjustedPattern: _ensurePatternRooted(defaultRoot, pattern),
        findPath: findPath,
        statOnly: literalSegments.length == minimatchObj.set[0].length,
    };
}
exports._getFindInfoFromPattern = _getFindInfoFromPattern;
function _ensurePatternRooted(root, p) {
    if (!root) {
        throw new Error('ensurePatternRooted() parameter "root" cannot be empty');
    }
    if (!p) {
        throw new Error('ensurePatternRooted() parameter "p" cannot be empty');
    }
    if (_isRooted(p)) {
        return p;
    }
    // normalize root
    root = _normalizeSeparators(root);
    // escape special glob characters
    root = (process.platform == 'win32' ? root : root.replace(/\\/g, '\\\\')) // escape '\' on OSX/Linux
        .replace(/(\[)(?=[^\/]+\])/g, '[[]') // escape '[' when ']' follows within the path segment
        .replace(/\?/g, '[?]') // escape '?'
        .replace(/\*/g, '[*]') // escape '*'
        .replace(/\+\(/g, '[+](') // escape '+('
        .replace(/@\(/g, '[@](') // escape '@('
        .replace(/!\(/g, '[!]('); // escape '!('
    return _ensureRooted(root, p);
}
exports._ensurePatternRooted = _ensurePatternRooted;
//-------------------------------------------------------------------
// Populate the vault with sensitive data.  Inputs and Endpoints
//-------------------------------------------------------------------
function _loadData() {
    // in agent, prefer TempDirectory then workFolder.
    // In interactive dev mode, it won't be
    var keyPath = _getVariable("agent.TempDirectory") || _getVariable("agent.workFolder") || process.cwd();
    exports._vault = new vm.Vault(keyPath);
    exports._knownVariableMap = {};
    _debug('loading inputs and endpoints');
    var loaded = 0;
    for (var envvar in process.env) {
        if (_startsWith(envvar, 'INPUT_') ||
            _startsWith(envvar, 'ENDPOINT_AUTH_') ||
            _startsWith(envvar, 'SECUREFILE_TICKET_') ||
            _startsWith(envvar, 'SECRET_') ||
            _startsWith(envvar, 'VSTS_TASKVARIABLE_')) {
            // Record the secret variable metadata. This is required by getVariable to know whether
            // to retrieve the value from the vault. In a 2.104.1 agent or higher, this metadata will
            // be overwritten when the VSTS_SECRET_VARIABLES env var is processed below.
            if (_startsWith(envvar, 'SECRET_')) {
                var variableName = envvar.substring('SECRET_'.length);
                if (variableName) {
                    // This is technically not the variable name (has underscores instead of dots),
                    // but it's good enough to make getVariable work in a pre-2.104.1 agent where
                    // the VSTS_SECRET_VARIABLES env var is not defined.
                    exports._knownVariableMap[_getVariableKey(variableName)] = { name: variableName, secret: true };
                }
            }
            // store the secret
            var value = process.env[envvar];
            if (value) {
                ++loaded;
                _debug('loading ' + envvar);
                exports._vault.storeSecret(envvar, value);
                delete process.env[envvar];
            }
        }
    }
    _debug('loaded ' + loaded);
    var correlationId = process.env["COMMAND_CORRELATION_ID"];
    delete process.env["COMMAND_CORRELATION_ID"];
    _commandCorrelationId = correlationId ? String(correlationId) : "";
    // store public variable metadata
    var names;
    try {
        names = JSON.parse(process.env['VSTS_PUBLIC_VARIABLES'] || '[]');
    }
    catch (err) {
        throw new Error('Failed to parse VSTS_PUBLIC_VARIABLES as JSON. ' + err); // may occur during interactive testing
    }
    names.forEach(function (name) {
        exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: false };
    });
    delete process.env['VSTS_PUBLIC_VARIABLES'];
    // store secret variable metadata
    try {
        names = JSON.parse(process.env['VSTS_SECRET_VARIABLES'] || '[]');
    }
    catch (err) {
        throw new Error('Failed to parse VSTS_SECRET_VARIABLES as JSON. ' + err); // may occur during interactive testing
    }
    names.forEach(function (name) {
        exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: true };
    });
    delete process.env['VSTS_SECRET_VARIABLES'];
    // avoid loading twice (overwrites .taskkey)
    global['_vsts_task_lib_loaded'] = true;
}
exports._loadData = _loadData;
//--------------------------------------------------------------------------------
// Internal path helpers.
//--------------------------------------------------------------------------------
/**
 * Defines if path is unc-path.
 *
 * @param path  a path to a file.
 * @returns     true if path starts with double backslash, otherwise returns false.
 */
function _isUncPath(path) {
    return /^\\\\[^\\]/.test(path);
}
exports._isUncPath = _isUncPath;
function _ensureRooted(root, p) {
    if (!root) {
        throw new Error('ensureRooted() parameter "root" cannot be empty');
    }
    if (!p) {
        throw new Error('ensureRooted() parameter "p" cannot be empty');
    }
    if (_isRooted(p)) {
        return p;
    }
    if (process.platform == 'win32' && root.match(/^[A-Z]:$/i)) { // e.g. C:
        return root + p;
    }
    // ensure root ends with a separator
    if (_endsWith(root, '/') || (process.platform == 'win32' && _endsWith(root, '\\'))) {
        // root already ends with a separator
    }
    else {
        root += path.sep; // append separator
    }
    return root + p;
}
exports._ensureRooted = _ensureRooted;
/**
 * Determines the parent path and trims trailing slashes (when safe). Path separators are normalized
 * in the result. This function works similar to the .NET System.IO.Path.GetDirectoryName() method.
 * For example, C:\hello\world\ returns C:\hello\world (trailing slash removed). Returns empty when
 * no higher directory can be determined.
 */
function _getDirectoryName(p) {
    // short-circuit if empty
    if (!p) {
        return '';
    }
    // normalize separators
    p = _normalizeSeparators(p);
    // on Windows, the goal of this function is to match the behavior of
    // [System.IO.Path]::GetDirectoryName(), e.g.
    //      C:/             =>
    //      C:/hello        => C:\
    //      C:/hello/       => C:\hello
    //      C:/hello/world  => C:\hello
    //      C:/hello/world/ => C:\hello\world
    //      C:              =>
    //      C:hello         => C:
    //      C:hello/        => C:hello
    //      /               =>
    //      /hello          => \
    //      /hello/         => \hello
    //      //hello         =>
    //      //hello/        =>
    //      //hello/world   =>
    //      //hello/world/  => \\hello\world
    //
    // unfortunately, path.dirname() can't simply be used. for example, on Windows
    // it yields different results from Path.GetDirectoryName:
    //      C:/             => C:/
    //      C:/hello        => C:/
    //      C:/hello/       => C:/
    //      C:/hello/world  => C:/hello
    //      C:/hello/world/ => C:/hello
    //      C:              => C:
    //      C:hello         => C:
    //      C:hello/        => C:
    //      /               => /
    //      /hello          => /
    //      /hello/         => /
    //      //hello         => /
    //      //hello/        => /
    //      //hello/world   => //hello/world
    //      //hello/world/  => //hello/world/
    //      //hello/world/again => //hello/world/
    //      //hello/world/again/ => //hello/world/
    //      //hello/world/again/again => //hello/world/again
    //      //hello/world/again/again/ => //hello/world/again
    if (process.platform == 'win32') {
        if (/^[A-Z]:\\?[^\\]+$/i.test(p)) { // e.g. C:\hello or C:hello
            return p.charAt(2) == '\\' ? p.substring(0, 3) : p.substring(0, 2);
        }
        else if (/^[A-Z]:\\?$/i.test(p)) { // e.g. C:\ or C:
            return '';
        }
        var lastSlashIndex = p.lastIndexOf('\\');
        if (lastSlashIndex < 0) { // file name only
            return '';
        }
        else if (p == '\\') { // relative root
            return '';
        }
        else if (lastSlashIndex == 0) { // e.g. \\hello
            return '\\';
        }
        else if (/^\\\\[^\\]+(\\[^\\]*)?$/.test(p)) { // UNC root, e.g. \\hello or \\hello\ or \\hello\world
            return '';
        }
        return p.substring(0, lastSlashIndex); // e.g. hello\world => hello or hello\world\ => hello\world
        // note, this means trailing slashes for non-root directories
        // (i.e. not C:\, \, or \\unc\) will simply be removed.
    }
    // OSX/Linux
    if (p.indexOf('/') < 0) { // file name only
        return '';
    }
    else if (p == '/') {
        return '';
    }
    else if (_endsWith(p, '/')) {
        return p.substring(0, p.length - 1);
    }
    return path.dirname(p);
}
exports._getDirectoryName = _getDirectoryName;
/**
 * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
 * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
 */
function _isRooted(p) {
    p = _normalizeSeparators(p);
    if (!p) {
        throw new Error('isRooted() parameter "p" cannot be empty');
    }
    if (process.platform == 'win32') {
        return _startsWith(p, '\\') || // e.g. \ or \hello or \\hello
            /^[A-Z]:/i.test(p); // e.g. C: or C:\hello
    }
    return _startsWith(p, '/'); // e.g. /hello
}
exports._isRooted = _isRooted;
function _normalizeSeparators(p) {
    p = p || '';
    if (process.platform == 'win32') {
        // convert slashes on Windows
        p = p.replace(/\//g, '\\');
        // remove redundant slashes
        var isUnc = /^\\\\+[^\\]/.test(p); // e.g. \\hello
        return (isUnc ? '\\' : '') + p.replace(/\\\\+/g, '\\'); // preserve leading // for UNC
    }
    // remove redundant slashes
    return p.replace(/\/\/+/g, '/');
}
exports._normalizeSeparators = _normalizeSeparators;
//-----------------------------------------------------
// Expose proxy information to vsts-node-api
//-----------------------------------------------------
function _exposeProxySettings() {
    var proxyUrl = _getVariable('Agent.ProxyUrl');
    if (proxyUrl && proxyUrl.length > 0) {
        var proxyUsername = _getVariable('Agent.ProxyUsername');
        var proxyPassword = _getVariable('Agent.ProxyPassword');
        var proxyBypassHostsJson = _getVariable('Agent.ProxyBypassList');
        global['_vsts_task_lib_proxy_url'] = proxyUrl;
        global['_vsts_task_lib_proxy_username'] = proxyUsername;
        global['_vsts_task_lib_proxy_bypass'] = proxyBypassHostsJson;
        global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword || '');
        _debug('expose agent proxy configuration.');
        global['_vsts_task_lib_proxy'] = true;
    }
}
exports._exposeProxySettings = _exposeProxySettings;
//-----------------------------------------------------
// Expose certificate information to vsts-node-api
//-----------------------------------------------------
function _exposeCertSettings() {
    var ca = _getVariable('Agent.CAInfo');
    if (ca) {
        global['_vsts_task_lib_cert_ca'] = ca;
    }
    var clientCert = _getVariable('Agent.ClientCert');
    if (clientCert) {
        var clientCertKey = _getVariable('Agent.ClientCertKey');
        var clientCertArchive = _getVariable('Agent.ClientCertArchive');
        var clientCertPassword = _getVariable('Agent.ClientCertPassword');
        global['_vsts_task_lib_cert_clientcert'] = clientCert;
        global['_vsts_task_lib_cert_key'] = clientCertKey;
        global['_vsts_task_lib_cert_archive'] = clientCertArchive;
        global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword || '');
    }
    if (ca || clientCert) {
        _debug('expose agent certificate configuration.');
        global['_vsts_task_lib_cert'] = true;
    }
    var skipCertValidation = _getVariable('Agent.SkipCertValidation') || 'false';
    if (skipCertValidation) {
        global['_vsts_task_lib_skip_cert_validation'] = skipCertValidation.toUpperCase() === 'TRUE';
    }
}
exports._exposeCertSettings = _exposeCertSettings;
// We store the encryption key on disk and hold the encrypted content and key file in memory
// return base64encoded<keyFilePath>:base64encoded<encryptedContent>
// downstream vsts-node-api will retrieve the secret later
function _exposeTaskLibSecret(keyFile, secret) {
    if (secret) {
        var encryptKey = crypto.randomBytes(32);
        var iv = crypto.randomBytes(16);
        var cipher = crypto.createCipheriv("aes-256-ctr", encryptKey, iv);
        var encryptedContent = cipher.update(secret, "utf8", "hex"); // CodeQL [SM01511] agent need to retrieve password later to connect to proxy server
        encryptedContent += cipher.final("hex");
        var storageFile = path.join(_getVariable('Agent.TempDirectory') || _getVariable("agent.workFolder") || process.cwd(), keyFile);
        var keyAndIv = encryptKey.toString('base64') + ':' + iv.toString('base64');
        fs.writeFileSync(storageFile, keyAndIv, { encoding: 'utf8' });
        return Buffer.from(storageFile).toString('base64') + ':' + Buffer.from(encryptedContent).toString('base64');
    }
}
function isSigPipeError(e) {
    var _a;
    if (!e || typeof e !== 'object') {
        return false;
    }
    return e.code === 'EPIPE' && ((_a = e.syscall) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === 'WRITE';
}
exports.isSigPipeError = isSigPipeError;
