diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..85c1f2d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,24 @@
+FROM mcr.microsoft.com/powershell:7.4-mariner-2.0-arm64
+
+ENV APP_ROOT_DIR="/app"
+
+RUN pwsh -Command Set-PSRepository -Name PSGallery -InstallationPolicy Trusted && \
+ pwsh -Command Install-Module -Name ExchangeOnlineManagement -Scope AllUsers -RequiredVersion 3.5.0 && \
+ pwsh -Command Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted
+
+RUN yum install -y nodejs npm
+
+# Set the working directory in the container
+WORKDIR /app
+
+# Copy package.json and package-lock.json
+COPY package*.json ./
+
+# Install dependencies
+RUN npm install
+
+# Copy the rest of the application code
+COPY . .
+
+# Command to run tests
+CMD ["npm", "run", "test-docker"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 3ad1e3f..01d5f7f 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@ Node.js module that provides a registry and gateway for execution of pre-defined
* [Overview](#overview)
* [Concepts](#concepts)
* [Usage](#usage)
+* [Testing](#testing)
* [History](#history)
* [Related tools](#related)
@@ -26,6 +27,20 @@ This provides the PSCommandService class which is a wrapper around [StatefulProc
This script simply exports a few useful pre-defined parameter sets (that one would pass to the constructor of StatefulProcessComamndProxy) for the initialization, destruction and auto-invalidation of "powershell" processes who connect to o365 and establish a remote PSSession that will be long lived. (and validate that the session is still legit)
+#### Exchange authentication
+
+`o365Utils.js` init command `getO365PSInitCommands` is using a deprecated authentication [method](https://techcommunity.microsoft.com/t5/exchange-team-blog/modern-auth-and-unattended-scripts-in-exchange-online-powershell/ba-p/1497387)
+
+Mictosoft has added [Exchange Online PowerShell V2](https://techcommunity.microsoft.com/t5/exchange-team-blog/announcing-general-availability-of-the-exchange-online/ba-p/1436623) that supports cerificate based authentication.
+
+Full setup is descibed [here](https://adamtheautomator.com/exchange-online-powershell-mfa/)
+
+Three sets of init commands are availiable as of version `1.1.0`:
+
+* `getO365PSInitCommands` - backward compatible old basic authentication
+* `getO365PSKeyInitCommands` - new Exchange authentication with private key and password
+* `getO365PSThumbprintInitCommands` - new Exchange authentication with the thumb print for the certificate
+
### Usage
1) Configure your o365 tenant with a user with the appropriate permissions to manage o365 via Powershell. [See this article to get going](https://bitsofinfo.wordpress.com/2015/01/06/configuring-powershell-for-azure-ad-and-o365-exchange-management/)
@@ -34,15 +49,77 @@ This script simply exports a few useful pre-defined parameter sets (that one wou
3) From within this project install the necessary npm dependencies for this module, including [stateful-process-command-proxy](https://github.com/bitsofinfo/stateful-process-command-proxy). You can checkout the latter manually and do a ```npm install stateful-process-command-proxy```
-4) Configure ```example.js``` appropriately, in particular the ```initCommands``` for the StatefulProcessCommandProxy; the paths to the items you created via the second step above
+4) Configure ```example.js```/```example_key_auth.js```/```examplekey_thumb_auth.js``` appropriately, in particular the ```initCommands``` for the StatefulProcessCommandProxy; the paths to the items you created via the second step above
+
+5) Tweak the group that is fetched at the bottom of ```example.js```/```example_key_auth.js```/```examplekey_thumb_auth.js```
+
+7) There is also a unit-test (```test\all.js```) for the command registry in ```o365Utils.js``` which gives an example of usage for all thre possible Exchange connect variations.
+
+### Testing
+Project test can be executed by running `npm test` command on Windows machine. Connection to Exchange Online is required for the tests to pass.
+
+There is also option to run Docker based tests. You need to configure `environment` variables in `docker-compose.yml` file in order to define connection parameters. To run tests in Docker container, execute `docker-compose run test` command once the configuration is done.
+
+Exchange online tests will be skipped if the connection is not available.
+
+### Empty Argument Values Support
+
+As of version 1.1.5, the module now supports passing empty string values to PowerShell command arguments when explicitly configured. This is useful for optional parameters that need to be passed as empty values rather than omitted entirely.
+
+To enable empty value support for a command argument, set the `empty` property to `true` in the argument configuration:
+
+```javascript
+const commandRegistry = {
+ 'myCommand': {
+ command: "Get-Content {{{arguments}}}",
+ arguments: {
+ 'Path': {},
+ 'Filter': {
+ empty: true, // Allow empty string values
+ },
+ },
+ return: {
+ type: "text",
+ }
+ }
+};
+```
+
+When `empty: true` is set, the argument will accept empty string values and include them in the generated PowerShell command:
+
+```javascript
+// This will generate: Get-Content -Path './test.txt' -Filter ''
+await psCommandService.execute("myCommand", {
+ Path: "./test.txt",
+ Filter: "" // Empty string value is now allowed
+});
+```
-5) Tweak the group that is fetched at the bottom of ```example.js```
-7) There is also a unit-test (```test\all.js```) for the command registry in ```o365Utils.js``` which gives an example of usage.
### History
```
+v1.1.5 - 2025-09-19
+ - Added support for empty argument values in commands via 'empty' property
+ - Fixed argument value bleed into the next empty argument
+
+v1.1.4 - 2024-11-22
+ - Extended testing and fixed escaping reserved variables and special characters in commands
+
+v1.1.3 - 2024-11-14
+ - Added support for [multivalued parameters](https://learn.microsoft.com/en-us/exchange/modifying-multivalued-properties-exchange-2013-help) in commands
+
+v1.1.2 - 2022-07-06
+ - Added support for usage of reserved powershell variables in commands [$null, $true, $false]
+
+
+v1.1.1 - 2020-12-07
+ - Fixed bug import of custom commands if provided for certificate based auth
+
+v1.1.0 - 2020-12-03
+ - Added option for key and thumbprint based Exchange authentication
+
v1.0.0 - 2016-06-08
- Get-DistributionGroupMember - added "-ResultSize Unlimited"
@@ -75,3 +152,10 @@ Have a look at these related projects which support and build on top of this mod
* https://github.com/bitsofinfo/stateful-process-command-proxy - The core dependency of this module, provides the actual bridging between node.js and a pool of external shell processes
* https://github.com/bitsofinfo/powershell-command-executor-ui - Builds on top of powershell-command-executor to provide a simple Node REST API and AngularJS interface for testing the execution of commands in the registry
* https://github.com/bitsofinfo/meteor-shell-command-mgr - Small Meteor app that lets you manage/generate a command registry for powershell-command-executor
+
+## notes
+
+```
+npm login
+npm publish
+```
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..a37e359
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,13 @@
+version: '3'
+services:
+ test:
+ build: .
+ volumes:
+ - .:/app
+ - /app/node_modules
+ environment:
+ - APPLICATION_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ - TENANT=XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ - CERTIFICATE_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ - CERTIFICATE=XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ - O365_TENANT_DOMAIN_NAME=sample.com
\ No newline at end of file
diff --git a/example.js b/example.js
index c709275..04f6a16 100644
--- a/example.js
+++ b/example.js
@@ -1,4 +1,3 @@
-var Promise = require('promise');
var StatefulProcessCommandProxy = require("stateful-process-command-proxy");
var PSCommandService = require('./psCommandService');
var o365Utils = require('./o365Utils');
diff --git a/example_key_auth.js b/example_key_auth.js
new file mode 100644
index 0000000..1d471aa
--- /dev/null
+++ b/example_key_auth.js
@@ -0,0 +1,81 @@
+var StatefulProcessCommandProxy = require("stateful-process-command-proxy");
+var PSCommandService = require('./psCommandService');
+var o365Utils = require('./o365Utils');
+
+
+
+
+var statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "StatefulProcessCommandProxy",
+ max: 1,
+ min: 1,
+ idleTimeoutMS:120000,
+ log: function(severity,origin,msg) {
+ console.log(severity.toUpperCase() + " " +origin+" "+ msg);
+ },
+
+ processCommand: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
+ processArgs: ['-Command','-'],
+
+
+ processRetainMaxCmdHistory : 20,
+ processInvalidateOnRegex : {
+ 'any':[],
+ 'stdout':[],
+ 'stderr':[{'regex':'.*error.*'}]
+ },
+ processCwd : null,
+ processEnvMap : null,
+ processUid : null,
+ processGid : null,
+
+ initCommands: o365Utils.getO365PSKeyInitCommands(
+ 'C:\\pathto\\decryptUtil.ps1',
+ 'C:\\pathto\\encrypted.credentials',
+ 'C:\\pathto\\secret.key',
+ 'C:\\pathto\\certificate',
+ 'certificatePassword',
+ '00000000-00000000-00000000-00000000',
+ 'your.exhange.domain.name',
+ 10000,30000,60000),
+
+
+ validateFunction: function(processProxy) {
+ var isValid = processProxy.isValid();
+ if(!isValid) {
+ console.log("ProcessProxy.isValid() returns FALSE!");
+ }
+ return isValid;
+ },
+
+
+ preDestroyCommands: o365Utils.getO365PSKeyDestroyCommands(),
+
+ processCmdWhitelistRegex: o365Utils.getO365WhitelistedCommands(),
+
+ processCmdBlacklistRegex: o365Utils.getO365BlacklistedCommands(),
+
+ autoInvalidationConfig: o365Utils.getO365AutoInvalidationConfig(30000)
+
+});
+
+var myLogFunction = function(severity,origin,message) {
+ console.log(severity.toUpperCase() + ' ' + origin + ' ' + message);
+}
+
+
+/**
+* Fetch a group!
+*/
+var psCommandService = new PSCommandService(statefulProcessCommandProxy,
+ o365Utils.o365CommandRegistry,
+ myLogFunction);
+
+psCommandService.execute('getDistributionGroup',{'Identity':"someGroupName"})
+ .then(function(groupJson) {
+ console.log(groupJson);
+ }).catch(function(error) {
+ console.log(error);
+ });
+
+setTimeout(function(){statefulProcessCommandProxy.shutdown()},80000);
diff --git a/example_key_thumb_auth.js b/example_key_thumb_auth.js
new file mode 100644
index 0000000..788fbc1
--- /dev/null
+++ b/example_key_thumb_auth.js
@@ -0,0 +1,80 @@
+var StatefulProcessCommandProxy = require("stateful-process-command-proxy");
+var PSCommandService = require('./psCommandService');
+var o365Utils = require('./o365Utils');
+
+
+
+
+var statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "StatefulProcessCommandProxy",
+ max: 1,
+ min: 1,
+ idleTimeoutMS:120000,
+ log: function(severity,origin,msg) {
+ console.log(severity.toUpperCase() + " " +origin+" "+ msg);
+ },
+
+ processCommand: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
+ processArgs: ['-Command','-'],
+
+
+ processRetainMaxCmdHistory : 20,
+ processInvalidateOnRegex : {
+ 'any':[],
+ 'stdout':[],
+ 'stderr':[{'regex':'.*error.*'}]
+ },
+ processCwd : null,
+ processEnvMap : null,
+ processUid : null,
+ processGid : null,
+
+ initCommands: o365Utils.getO365PSThumbprintInitCommands(
+ 'C:\\pathto\\decryptUtil.ps1',
+ 'C:\\pathto\\encrypted.credentials',
+ 'C:\\pathto\\secret.key',
+ 'certificatethumbprint',
+ '00000000-00000000-00000000-00000000',
+ 'your.exhange.domain.name',
+ 10000,30000,60000),
+
+
+ validateFunction: function(processProxy) {
+ var isValid = processProxy.isValid();
+ if(!isValid) {
+ console.log("ProcessProxy.isValid() returns FALSE!");
+ }
+ return isValid;
+ },
+
+
+ preDestroyCommands: o365Utils.getO365PSThumbprintDestroyCommands(),
+
+ processCmdWhitelistRegex: o365Utils.getO365WhitelistedCommands(),
+
+ processCmdBlacklistRegex: o365Utils.getO365BlacklistedCommands(),
+
+ autoInvalidationConfig: o365Utils.getO365AutoInvalidationConfig(30000)
+
+});
+
+var myLogFunction = function(severity,origin,message) {
+ console.log(severity.toUpperCase() + ' ' + origin + ' ' + message);
+}
+
+
+/**
+* Fetch a group!
+*/
+var psCommandService = new PSCommandService(statefulProcessCommandProxy,
+ o365Utils.o365CommandRegistry,
+ myLogFunction);
+
+psCommandService.execute('getDistributionGroup',{'Identity':"someGroupName"})
+ .then(function(groupJson) {
+ console.log(groupJson);
+ }).catch(function(error) {
+ console.log(error);
+ });
+
+setTimeout(function(){statefulProcessCommandProxy.shutdown()},80000);
diff --git a/o365Utils.js b/o365Utils.js
index 3f82154..6b0477a 100644
--- a/o365Utils.js
+++ b/o365Utils.js
@@ -1,34 +1,33 @@
-
/**
-* getO365PSInitCommands()
-*
-* Returns an array of Powershell initialization commands suitable
-* for setting up shells spawned with StatefulProcessCommandProxy
-* to be able to establish a remote PSSession with o365
-*
-* @see https://github.com/bitsofinfo/powershell-credential-encryption-tools
-*
-* This function takes the full path to:
-* - decryptUtil.ps1 from the project above
-* - path the encrypted credentials file generated with decryptUtil.ps1
-* - path to the secret key needed to decrypt the credentials
-*
-* In addition there are parameter to define the PSSessionOption timeouts
-*
-* Note this is just an example (which works) however you may want to
-* replace this with your own set of init command tailored to your specific
-* use-case
-*
-* @see the getO365PSDestroyCommands() below for the corresponding cleanup
-* commands for these init commands
-*/
-module.exports.getO365PSInitCommands = function(pathToDecryptUtilScript,
- pathToCredsFile,
- pathToKeyFile,
- openTimeout,
- operationTimeout,
- idleTimeout) {
- return [
+ * getO365PSInitCommands()
+ *
+ * Returns an array of Powershell initialization commands suitable
+ * for setting up shells spawned with StatefulProcessCommandProxy
+ * to be able to establish a remote PSSession with o365
+ *
+ * @see https://github.com/bitsofinfo/powershell-credential-encryption-tools
+ *
+ * This function takes the full path to:
+ * - decryptUtil.ps1 from the project above
+ * - path the encrypted credentials file generated with decryptUtil.ps1
+ * - path to the secret key needed to decrypt the credentials
+ *
+ * In addition there are parameter to define the PSSessionOption timeouts
+ *
+ * Note this is just an example (which works) however you may want to
+ * replace this with your own set of init command tailored to your specific
+ * use-case
+ *
+ * @see the getO365PSDestroyCommands() below for the corresponding cleanup
+ * commands for these init commands
+ */
+module.exports.getO365PSInitCommands = function (pathToDecryptUtilScript,
+ pathToCredsFile,
+ pathToKeyFile,
+ openTimeout,
+ operationTimeout,
+ idleTimeout) {
+ return [
// #0 Encoding UTF8
'chcp 65001',
'$OutputEncoding = [System.Text.Encoding]::GetEncoding(65001)',
@@ -45,7 +44,7 @@ module.exports.getO365PSInitCommands = function(pathToDecryptUtilScript,
('$PSCredential = decrypt2PSCredential ' + pathToCredsFile + ' ' + pathToKeyFile),
// #4+ establish the session to o365
- ('$sessionOpt = New-PSSessionOption -OpenTimeout '+openTimeout+' -OperationTimeout '+operationTimeout+' -IdleTimeout ' + idleTimeout),
+ ('$sessionOpt = New-PSSessionOption -OpenTimeout ' + openTimeout + ' -OperationTimeout ' + operationTimeout + ' -IdleTimeout ' + idleTimeout),
'$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $PSCredential -Authentication Basic -AllowRedirection -SessionOption $sessionOpt',
// #5 import the relevant cmdlets (TODO: make this configurable)
@@ -57,119 +56,336 @@ module.exports.getO365PSInitCommands = function(pathToDecryptUtilScript,
// #7 cleanup
'Remove-Variable -Force -ErrorAction SilentlyContinue $PSCredential'
- ]
-}
+ ];
+};
+
+
+/**
+ * Destroy commands that correspond to the session
+ * established w/ the initCommands above
+ */
+module.exports.getO365PSDestroyCommands = function () {
+ return [
+ 'Get-PSSession | Remove-PSSession -ErrorAction SilentlyContinue',
+ 'Remove-PSSession -Session $session',
+ 'Remove-Module MsOnline'
+ ];
+};
+
+/**
+ * getO365PSKeyInitCommands()
+ *
+ * Returns an array of Powershell initialization commands suitable
+ * for setting up shells spawned with StatefulProcessCommandProxy
+ * to be able to establish a remote PSSession with o365
+ *
+ * @see https://github.com/bitsofinfo/powershell-credential-encryption-tools
+ * @see https://docs.microsoft.com/en-us/powershell/module/exchange/connect-exchangeonline?view=exchange-ps
+ * @see https://adamtheautomator.com/exchange-online-powershell-mfa/#Authenticating_Using_Local_PFX_Certificate
+ *
+ * This function takes the full path to:
+ * - decryptUtil.ps1 from the project above
+ * - path the encrypted credentials file generated with decryptUtil.ps1
+ * - path to the secret key needed to decrypt the credentials
+ * - path to the certificate configured for Exchange app authentication
+ * - certificate password
+ * - application id created for the Exchange app integration
+ * - tenant/organizationId for the Exchange
+ * - comma separatged list of commands to import, widcard supported.
+ * Everything is imported if empty
+ *
+ * In addition there are parameter to define the PSSessionOption timeouts
+ *
+ * Note this is just an example (which works) however you may want to
+ * replace this with your own set of init command tailored to your specific
+ * use-case
+ *
+ * @see the getO365PSKeyDestroyCommandsgetO365PSKeyDestroyCommands() below
+ for the corresponding cleanup
+ * commands for these init commands
+ */
+module.exports.getO365PSKeyInitCommands = function (pathToDecryptUtilScript,
+ pathToCredsFile,
+ pathToKeyFile,
+ pathToAuthCertificate,
+ authCertificatePassword,
+ applicationId,
+ organizationId,
+ openTimeout,
+ operationTimeout,
+ idleTimeout,
+ commandsToImport = '') {
+
+ let psCommandsToImport = '';
+ if (commandsToImport != '') {
+ psCommandsToImport = '-CommandName ' + commandsToImport;
+ }
+ return [
+ // #0 Encoding UTF8
+ 'chcp 65001',
+ '$OutputEncoding = [System.Text.Encoding]::GetEncoding(65001)',
+
+ // #1 import some basics
+ 'Import-Module MSOnline',
+ 'Import-Module ExchangeOnlineManagement',
+
+ // #2 source the decrypt utils script
+ // https://github.com/bitsofinfo/powershell-credential-encryption-tools/blob/master/decryptUtil.ps1
+ ('. ' + pathToDecryptUtilScript),
+
+ // #3 invoke decrypt2PSCredential to get the PSCredential object
+ // this function is provided by the sourced file above
+ ('$PSCredential = decrypt2PSCredential ' + pathToCredsFile + ' ' + pathToKeyFile),
+
+ // #4 connect to azure as well
+ 'Connect-MsolService -Credential $PSCredential',
+
+ // #5 get session options, certificate file and secure password
+ ('$sessionOpt = New-PSSessionOption -OpenTimeout ' + openTimeout + ' -OperationTimeout ' + operationTimeout + ' -IdleTimeout ' + idleTimeout),
+ '$CertificateFilePath = (Resolve-Path "' + pathToAuthCertificate + '").Path',
+ '$CertificatePassword = (ConvertTo-SecureString -String "' + authCertificatePassword + '" -AsPlainText -Force)',
+
+ // #6 connect to exchange
+ 'Connect-ExchangeOnline -CertificateFilePath $CertificateFilePath -CertificatePassword $CertificatePassword -AppID ' + applicationId + ' -Organization ' + organizationId + ' ' + psCommandsToImport,
+
+ // #7 cleanup
+ 'Remove-Variable -Force -ErrorAction SilentlyContinue $PSCredential'
+ ];
+};
+
+/**
+ * Destroy commands that correspond to the session
+ * established w/ the KeyinitCommands above
+ */
+module.exports.getO365PSKeyDestroyCommands = function () {
+ return [
+ 'Get-PSSession | Remove-PSSession -ErrorAction SilentlyContinue',
+ 'Remove-Module MsOnline',
+ 'Remove-Module ExchangeOnlineManagement',
+ ];
+};
+
+/**
+ * getO365PSKeyInitCommands()
+ *
+ * Returns an array of Powershell initialization commands suitable
+ * for setting up shells spawned with StatefulProcessCommandProxy
+ * to be able to establish a remote PSSession with o365
+ *
+ * @see https://github.com/bitsofinfo/powershell-credential-encryption-tools
+ * @see https://docs.microsoft.com/en-us/powershell/module/exchange/connect-exchangeonline?view=exchange-ps
+ * @see https://adamtheautomator.com/exchange-online-powershell-mfa/#Authenticating_Using_Certificate_Thumbprint
+ *
+ * This function takes the full path to:
+ * - decryptUtil.ps1 from the project above
+ * - path the encrypted credentials file generated with decryptUtil.ps1
+ * - path to the secret key needed to decrypt the credentials
+ * - certificate thumbprint configured for Exchange app authentication
+ * - application id created for the Exchange app integration
+ * - tenant/organizationId for the Exchange
+ * - comma separatged list of commands to import, widcard supported.
+ * Everything is imported if empty
+ *
+ * In addition there are parameter to define the PSSessionOption timeouts
+ *
+ * Note this is just an example (which works) however you may want to
+ * replace this with your own set of init command tailored to your specific
+ * use-case
+ *
+ * @see the getO365PSKeyDestroyCommandsgetO365PSKeyDestroyCommands() below
+ for the corresponding cleanup
+ * commands for these init commands
+ */
+module.exports.getO365PSThumbprintInitCommands = function (pathToDecryptUtilScript,
+ pathToCredsFile,
+ pathToKeyFile,
+ certificateThumbPrint,
+ applicationId,
+ organizationId,
+ openTimeout,
+ operationTimeout,
+ idleTimeout,
+ commandsToImport = '') {
+
+ let psCommandsToImport = '';
+ if (commandsToImport != '') {
+ psCommandsToImport = '-CommandName ' + commandsToImport;
+ }
+ return [
+ // #0 Encoding UTF8
+ 'chcp 65001',
+ '$OutputEncoding = [System.Text.Encoding]::GetEncoding(65001)',
+
+ // #1 import some basics
+ 'Import-Module MSOnline',
+ 'Import-Module ExchangeOnlineManagement',
+
+ // #2 source the decrypt utils script
+ // https://github.com/bitsofinfo/powershell-credential-encryption-tools/blob/master/decryptUtil.ps1
+ ('. ' + pathToDecryptUtilScript),
+
+ // #3 invoke decrypt2PSCredential to get the PSCredential object
+ // this function is provided by the sourced file above
+ ('$PSCredential = decrypt2PSCredential ' + pathToCredsFile + ' ' + pathToKeyFile),
+
+ // #4 connect to azure as well
+ 'Connect-MsolService -Credential $PSCredential',
+
+ // #5 get session options
+ ('$sessionOpt = New-PSSessionOption -OpenTimeout ' + openTimeout + ' -OperationTimeout ' + operationTimeout + ' -IdleTimeout ' + idleTimeout),
+
+ // #6 connect to exchange
+ 'Connect-ExchangeOnline -CertificateThumbPrint ' + certificateThumbPrint + ' -AppID ' + applicationId + ' -Organization ' + organizationId + ' ' + psCommandsToImport,
+
+ // #7 cleanup
+ 'Remove-Variable -Force -ErrorAction SilentlyContinue $PSCredential'
+ ];
+};
/**
-* Destroy commands that correspond to the session
-* established w/ the initCommands above
-*/
-module.exports.getO365PSDestroyCommands = function() {
+ * Destroy commands that correspond to the session
+ * established w/ the KeyinitCommands above
+ */
+module.exports.getO365PSThumbprintDestroyCommands = function () {
return [
- 'Get-PSSession | Remove-PSSession',
- 'Remove-PSSession -Session $session',
- 'Remove-Module MsOnline'
- ]
- }
+ 'Get-PSSession | Remove-PSSession -ErrorAction SilentlyContinue',
+ 'Remove-Module MsOnline',
+ 'Remove-Module ExchangeOnlineManagement',
+ ];
+};
/**
-* Some example blacklisted commands
-*/
-module.exports.getO365BlacklistedCommands = function() {
- return [
- {'regex':'.*Invoke-Expression.*', 'flags':'i'},
- {'regex':'.*ScriptBlock.*', 'flags':'i'},
- {'regex':'.*Get-Acl.*', 'flags':'i'},
- {'regex':'.*Set-Acl.*', 'flags':'i'},
- {'regex':'.*Get-Content.*', 'flags':'i'},
- {'regex':'.*-History.*', 'flags':'i'},
- {'regex':'.*Out-File.*', 'flags':'i'}
- ]
-}
+ * Some example blacklisted commands
+ */
+module.exports.getO365BlacklistedCommands = function () {
+ return [{
+ 'regex': '.*Invoke-Expression.*',
+ 'flags': 'i'
+ },
+ {
+ 'regex': '.*ScriptBlock.*',
+ 'flags': 'i'
+ },
+ {
+ 'regex': '.*Get-Acl.*',
+ 'flags': 'i'
+ },
+ {
+ 'regex': '.*Set-Acl.*',
+ 'flags': 'i'
+ },
+ {
+ 'regex': '.*Get-Content.*',
+ 'flags': 'i'
+ },
+ {
+ 'regex': '.*-History.*',
+ 'flags': 'i'
+ },
+ {
+ 'regex': '.*Out-File.*',
+ 'flags': 'i'
+ }
+ ];
+};
/**
-* Configuration auto invalidation, checking PSSession availability
-* @param checkIntervalMS
-*/
-module.exports.getO365AutoInvalidationConfig = function(checkIntervalMS) {
- return {
- 'checkIntervalMS': checkIntervalMS,
- 'commands': [
- // no remote pssession established? invalid!
- { 'command': 'Get-PSSession',
+ * Configuration auto invalidation, checking PSSession availability
+ * @param checkIntervalMS
+ */
+module.exports.getO365AutoInvalidationConfig = function (checkIntervalMS) {
+ return {
+ 'checkIntervalMS': checkIntervalMS,
+ 'commands': [
+ // no remote pssession established? invalid!
+ {
+ 'command': 'Get-PSSession',
'regexes': {
- 'stdout' : [ {'regex':'.*Opened.*', 'flags':'i', 'invalidOn':'noMatch'}]
+ 'stdout': [{
+ 'regex': '.*Opened.*',
+ 'flags': 'i',
+ 'invalidOn': 'noMatch'
+ }]
}
- }]
- };
- }
+ }
+ ]
+ };
+};
/**
-* Defines a registry of Powershell commands
-* that can be injected into the PSCommandService
-* instance.
-*
-* Note these are just some example configurations specifically for a few
-* o365 functions and limited arguments for each, (they work) however you may want to
-* replace this with your own set of init command tailored to your specific
-* use-case
-*/
+ * Defines a registry of Powershell commands
+ * that can be injected into the PSCommandService
+ * instance.
+ *
+ * Note these are just some example configurations specifically for a few
+ * o365 functions and limited arguments for each, (they work) however you may want to
+ * replace this with your own set of init command tailored to your specific
+ * use-case
+ */
var o365CommandRegistry = {
/*******************************
- *
- * o365 Powershell Command registry
- *
- * argument properties (optional):
- * - quoted: true|false, default true
- * - valued: true|false, default true
- * - default: optional default value (only if valued..)
- *
- * return properties:
- * type: none, text or json are valid values
- *
- ********************************/
+ *
+ * o365 Powershell Command registry
+ *
+ * argument properties (optional):
+ * - quoted: true|false, default true
+ * - valued: true|false, default true
+ * - default: optional default value (only if valued..)
+ *
+ * return properties:
+ * type: none, text or json are valid values
+ *
+ ********************************/
/*******************************
- * MsolUser
- ********************************/
+ * MsolUser
+ ********************************/
'getMsolUser': {
- 'command': 'Get-MsolUser {{{arguments}}} | ConvertTo-Json',
- 'arguments': {
- 'UserPrincipalName': {}
- },
- 'return': { type: 'json' }
+ 'command': 'Get-MsolUser {{{arguments}}} | ConvertTo-Json',
+ 'arguments': {
+ 'UserPrincipalName': {}
+ },
+ 'return': {
+ type: 'json'
+ }
},
'newMsolUser': {
- 'command': 'New-MsolUser {{{arguments}}} | ConvertTo-Json',
- 'arguments': {
- 'DisplayName': {},
- 'UserPrincipalName': {}
- },
- 'return': { type: 'json' }
+ 'command': 'New-MsolUser {{{arguments}}} | ConvertTo-Json',
+ 'arguments': {
+ 'DisplayName': {},
+ 'UserPrincipalName': {}
+ },
+ 'return': {
+ type: 'json'
+ }
},
'removeMsolUser': {
- 'command': 'Remove-MsolUser -Force {{{arguments}}} ',
- 'arguments': {
- 'UserPrincipalName': {}
- },
- 'return': { type: 'none' }
+ 'command': 'Remove-MsolUser -Force {{{arguments}}} ',
+ 'arguments': {
+ 'UserPrincipalName': {}
+ },
+ 'return': {
+ type: 'none'
+ }
},
/*******************************
- * DistributionGroups
- ********************************/
+ * DistributionGroups
+ ********************************/
'getDistributionGroup': {
'command': 'Get-DistributionGroup {{{arguments}}} | ConvertTo-Json',
'arguments': {
'Identity': {}
},
- 'return': { type: 'json' }
+ 'return': {
+ type: 'json'
+ }
},
'newDistributionGroup': {
@@ -177,20 +393,35 @@ var o365CommandRegistry = {
'command': 'New-DistributionGroup -Confirm:$False {{{arguments}}} | ConvertTo-Json',
'arguments': {
- 'Name': {},
- 'DisplayName': {},
- 'Alias': {},
+ 'Name': {},
+ 'DisplayName': {},
+ 'Alias': {},
'PrimarySmtpAddress': {},
- 'Type': {'quoted':false, 'default':'Security'},
- 'ManagedBy': {},
- 'Members': {}, // specifying members on create does not seem to work
- 'ModerationEnabled': { 'default':'0', 'quoted':false},
- 'MemberDepartRestriction': { 'default':'Closed'},
- 'MemberJoinRestriction': { 'default':'Closed'},
- 'SendModerationNotifications': { 'default':'Never', 'quoted':false},
+ 'Type': {
+ 'quoted': false,
+ 'default': 'Security'
+ },
+ 'ManagedBy': {},
+ 'Members': {}, // specifying members on create does not seem to work
+ 'ModerationEnabled': {
+ 'default': '0',
+ 'quoted': false
+ },
+ 'MemberDepartRestriction': {
+ 'default': 'Closed'
+ },
+ 'MemberJoinRestriction': {
+ 'default': 'Closed'
+ },
+ 'SendModerationNotifications': {
+ 'default': 'Never',
+ 'quoted': false
+ },
},
- 'return': { type: 'json' }
+ 'return': {
+ type: 'json'
+ }
},
'setDistributionGroup': {
@@ -198,21 +429,35 @@ var o365CommandRegistry = {
'command': 'Set-DistributionGroup -Confirm:$False {{{arguments}}}',
'arguments': {
- 'Identity': {},
- 'Name': {},
- 'DisplayName': {},
- 'Alias': {},
+ 'Identity': {},
+ 'Name': {},
+ 'DisplayName': {},
+ 'Alias': {},
'PrimarySmtpAddress': {},
- 'ManagedBy': {},
- 'Members': {},
- 'MailTip': {},
- 'ModerationEnabled': { 'default':'0', 'quoted':false},
- 'MemberDepartRestriction': { 'default':'Closed'},
- 'MemberJoinRestriction': { 'default':'Closed'},
- 'SendModerationNotifications': { 'default':'Never', 'quoted':false},
- 'BypassSecurityGroupManagerCheck': {'valued': false}
+ 'ManagedBy': {},
+ 'Members': {},
+ 'MailTip': {},
+ 'ModerationEnabled': {
+ 'default': '0',
+ 'quoted': false
+ },
+ 'MemberDepartRestriction': {
+ 'default': 'Closed'
+ },
+ 'MemberJoinRestriction': {
+ 'default': 'Closed'
+ },
+ 'SendModerationNotifications': {
+ 'default': 'Never',
+ 'quoted': false
+ },
+ 'BypassSecurityGroupManagerCheck': {
+ 'valued': false
+ }
},
- 'return': { type: 'none' }
+ 'return': {
+ type: 'none'
+ }
},
@@ -221,11 +466,15 @@ var o365CommandRegistry = {
'command': 'Remove-DistributionGroup {{{arguments}}} -Confirm:$false',
'arguments': {
- 'Identity': {},
+ 'Identity': {},
// needed if invoking as global admin who is not explicitly a group admin.. stupid... yes.
- 'BypassSecurityGroupManagerCheck': {'valued': false}
+ 'BypassSecurityGroupManagerCheck': {
+ 'valued': false
+ }
},
- 'return': { type: 'none' }
+ 'return': {
+ type: 'none'
+ }
},
@@ -234,9 +483,11 @@ var o365CommandRegistry = {
'command': 'Get-DistributionGroupMember {{{arguments}}} -ResultSize Unlimited | ConvertTo-Json',
'arguments': {
- 'Identity': {}
+ 'Identity': {}
},
- 'return': { type: 'json' }
+ 'return': {
+ type: 'json'
+ }
},
@@ -245,12 +496,16 @@ var o365CommandRegistry = {
'command': 'Add-DistributionGroupMember {{{arguments}}}',
'arguments': {
- 'Identity': {},
- 'Member': {},
+ 'Identity': {},
+ 'Member': {},
// needed if invoking as global admin who is not explicitly a group admin.. stupid... yes.
- 'BypassSecurityGroupManagerCheck': {'valued': false}
+ 'BypassSecurityGroupManagerCheck': {
+ 'valued': false
+ }
},
- 'return': { type: 'none' }
+ 'return': {
+ type: 'none'
+ }
},
// members specified w/ this are a full overwrite..
@@ -259,12 +514,16 @@ var o365CommandRegistry = {
'command': 'Update-DistributionGroupMember -Confirm:$false {{{arguments}}}',
'arguments': {
- 'Identity': {},
- 'Members': {},
+ 'Identity': {},
+ 'Members': {},
// needed if invoking as global admin who is not explicitly a group admin.. stupid... yes.
- 'BypassSecurityGroupManagerCheck': {'valued': false}
+ 'BypassSecurityGroupManagerCheck': {
+ 'valued': false
+ }
},
- 'return': { type: 'none' }
+ 'return': {
+ type: 'none'
+ }
},
'removeDistributionGroupMember': {
@@ -272,27 +531,33 @@ var o365CommandRegistry = {
'command': 'Remove-DistributionGroupMember {{{arguments}}} -Confirm:$false',
'arguments': {
- 'Identity': {},
- 'Member': {},
+ 'Identity': {},
+ 'Member': {},
// needed if invoking as global admin who is not explicitly a group admin.. stupid... yes.
- 'BypassSecurityGroupManagerCheck': {'valued': false}
+ 'BypassSecurityGroupManagerCheck': {
+ 'valued': false
+ }
},
- 'return': { type: 'none' }
+ 'return': {
+ type: 'none'
+ }
},
/*******************************
- * MailContacts
- ********************************/
+ * MailContacts
+ ********************************/
'getMailContact': {
'command': 'Get-MailContact {{{arguments}}} | ConvertTo-Json',
'arguments': {
'Identity': {}
},
- 'return': { type: 'json' }
+ 'return': {
+ type: 'json'
+ }
},
'newMailContact': {
@@ -300,11 +565,13 @@ var o365CommandRegistry = {
'command': 'New-MailContact -Confirm:$False {{{arguments}}} | ConvertTo-Json',
'arguments': {
- 'Name': {},
- 'ExternalEmailAddress': {}
+ 'Name': {},
+ 'ExternalEmailAddress': {}
},
- 'return': { type: 'json' }
+ 'return': {
+ type: 'json'
+ }
},
'setMailContact': {
@@ -312,13 +579,15 @@ var o365CommandRegistry = {
'command': 'Set-MailContact -Confirm:$False {{{arguments}}}',
'arguments': {
- 'Identity': {},
- 'Name': {},
- 'DisplayName': {},
+ 'Identity': {},
+ 'Name': {},
+ 'DisplayName': {},
'ExternalEmailAddress': {}
},
- 'return': { type: 'none' }
+ 'return': {
+ type: 'none'
+ }
},
@@ -327,25 +596,36 @@ var o365CommandRegistry = {
'command': 'Remove-MailContact {{{arguments}}} -Confirm:$false',
'arguments': {
- 'Identity': {}
+ 'Identity': {}
},
- 'return': { type: 'none' }
- }
+ 'return': {
+ type: 'none'
+ }
+ },
+ getStatus: {
+ command: 'Get-ConnectionInformation | ConvertTo-Json',
+ return: {
+ type: 'json'
+ }
+ },
};
module.exports.o365CommandRegistry = o365CommandRegistry;
/**
-* Some example whitelisted commands
-* (only permit) what is in the registry
-*/
-module.exports.getO365WhitelistedCommands = function() {
+ * Some example whitelisted commands
+ * (only permit) what is in the registry
+ */
+module.exports.getO365WhitelistedCommands = function () {
var whitelist = [];
for (var cmdName in o365CommandRegistry) {
var config = o365CommandRegistry[cmdName];
- var commandStart = config.command.substring(0,config.command.indexOf(' ')).trim();
- whitelist.push({'regex':'^'+commandStart+'\\s+.*', 'flags':'i'});
+ var commandStart = config.command.substring(0, config.command.indexOf(' ')).trim();
+ whitelist.push({
+ 'regex': '^' + commandStart + '\\s+.*',
+ 'flags': 'i'
+ });
}
return whitelist;
-}
+};
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index f75519d..03f427f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,890 +1,980 @@
{
"name": "powershell-command-executor",
- "version": "1.0.0",
- "lockfileVersion": 1,
+ "version": "1.1.4",
+ "lockfileVersion": 3,
"requires": true,
- "dependencies": {
- "@ungap/promise-all-settled": {
+ "packages": {
+ "": {
+ "name": "powershell-command-executor",
+ "version": "1.1.4",
+ "license": "ISC",
+ "dependencies": {
+ "mustache": "^4.1.0",
+ "promise": "latest",
+ "stateful-process-command-proxy": "latest"
+ },
+ "devDependencies": {
+ "mocha": "latest"
+ }
+ },
+ "node_modules/@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
- "ansi-colors": {
+ "node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
},
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "ansi-styles": {
+ "node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
- "requires": {
+ "dependencies": {
"color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "anymatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
- "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "node_modules/anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
- "requires": {
+ "dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "requires": {
- "sprintf-js": "~1.0.2"
- }
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
},
- "asap": {
+ "node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
},
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
- "binary-extensions": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
- "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
- "dev": true
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "brace-expansion": {
+ "node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "requires": {
+ "dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
- "braces": {
+ "node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
- "requires": {
+ "dependencies": {
"fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "browser-stdout": {
+ "node_modules/browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
- "buffer-builder": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
- "integrity": "sha1-MyLNMH2Cltqx9gRhhZOyYaP63o8="
- },
- "camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "chalk": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
- "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
- "requires": {
+ "dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "chokidar": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
- "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
- "requires": {
- "anymatch": "~3.1.1",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
"braces": "~3.0.2",
- "fsevents": "~2.1.2",
- "glob-parent": "~5.1.0",
+ "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.5.0"
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
}
},
- "cliui": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
- "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
- "requires": {
- "string-width": "^3.1.0",
- "strip-ansi": "^5.2.0",
- "wrap-ansi": "^5.1.0"
- },
"dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
}
},
- "color-convert": {
+ "node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
}
},
- "color-name": {
+ "node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "concat-map": {
+ "node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
- "debug": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
- "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "node_modules/debug/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
- "diff": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
- "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
},
- "emoji-regex": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
- "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
- "escape-string-regexp": {
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true
- },
- "fifo": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/fifo/-/fifo-2.3.0.tgz",
- "integrity": "sha1-GC3o3QYyqkfPaBbZvEMjMX1SWdw="
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "fill-range": {
+ "node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "find-up": {
+ "node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
- "requires": {
+ "dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "flat": {
+ "node_modules/flat": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
- "dev": true
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
},
- "fs.realpath": {
+ "node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
- "fsevents": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
- "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
- "optional": true
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
},
- "generic-pool": {
+ "node_modules/generic-pool": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.4.tgz",
- "integrity": "sha1-SdkMXo0Tit7woSeAqG5Ky6SIktI="
+ "integrity": "sha512-9GicuYkryPAwzKW2Vpfus1zC28ER7mwWEZgY7F+6RqpPMK4N8XpkxzAr4CuPf10NrzW0f/kQ4AmeDVgFhdwjAQ==",
+ "engines": {
+ "node": ">= 0.2.0"
+ }
},
- "get-caller-file": {
+ "node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
},
- "glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
- "requires": {
+ "dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "glob-parent": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
- "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
- "requires": {
+ "dependencies": {
"is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
- "growl": {
- "version": "1.10.5",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
- "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
- "dev": true
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
},
- "has-flag": {
+ "node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "he": {
+ "node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
},
- "inflight": {
+ "node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
- "requires": {
+ "dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
- "inherits": {
+ "node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "is-binary-path": {
+ "node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
- "requires": {
+ "dependencies": {
"binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "is-extglob": {
+ "node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "is-glob": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
- "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
- "requires": {
+ "dependencies": {
"is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "is-number": {
+ "node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
},
- "is-plain-obj": {
+ "node_modules/is-plain-obj": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
- "dev": true
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "js-yaml": {
- "version": "3.14.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
- "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "locate-path": {
+ "node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
- "requires": {
+ "dependencies": {
"p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "log-symbols": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
- "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
- "requires": {
- "chalk": "^4.0.0"
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "node_modules/minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dev": true,
- "requires": {
- "brace-expansion": "^1.1.7"
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "mocha": {
- "version": "8.2.1",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz",
- "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==",
+ "node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
- "requires": {
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz",
+ "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==",
+ "dev": true,
+ "dependencies": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
- "chokidar": "3.4.3",
- "debug": "4.2.0",
- "diff": "4.0.2",
+ "chokidar": "3.5.3",
+ "debug": "4.3.4",
+ "diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
- "glob": "7.1.6",
- "growl": "1.10.5",
+ "glob": "7.2.0",
"he": "1.2.0",
- "js-yaml": "3.14.0",
- "log-symbols": "4.0.0",
- "minimatch": "3.0.4",
- "ms": "2.1.2",
- "nanoid": "3.1.12",
- "serialize-javascript": "5.0.1",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "5.0.1",
+ "ms": "2.1.3",
+ "nanoid": "3.3.3",
+ "serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
- "supports-color": "7.2.0",
- "which": "2.0.2",
- "wide-align": "1.1.3",
- "workerpool": "6.0.2",
- "yargs": "13.3.2",
- "yargs-parser": "13.1.2",
+ "supports-color": "8.1.1",
+ "workerpool": "6.2.1",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
}
},
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
- "mustache": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.1.tgz",
- "integrity": "sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA=="
+ "node_modules/mustache": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.1.0.tgz",
+ "integrity": "sha512-0FsgP/WVq4mKyjolIyX+Z9Bd+3WS8GOwoUTyKXT5cTYMGeauNTi2HPCwERqseC1IHAy0Z7MDZnJBfjabd4O8GQ==",
+ "bin": {
+ "mustache": "bin/mustache"
+ }
},
- "nanoid": {
- "version": "3.1.12",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz",
- "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==",
- "dev": true
+ "node_modules/nanoid": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
+ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
},
- "normalize-path": {
+ "node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "once": {
+ "node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
- "requires": {
+ "dependencies": {
"wrappy": "1"
}
},
- "p-limit": {
+ "node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "p-locate": {
+ "node_modules/p-locate": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
- "requires": {
+ "dependencies": {
"p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true
- },
- "path-exists": {
+ "node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
},
- "path-is-absolute": {
+ "node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "picomatch": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
- "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
- "dev": true
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
},
- "promise": {
+ "node_modules/promise": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz",
"integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==",
- "requires": {
+ "dependencies": {
"asap": "~2.0.6"
}
},
- "randombytes": {
+ "node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"safe-buffer": "^5.1.0"
}
},
- "readdirp": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
- "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
- "requires": {
+ "dependencies": {
"picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
}
},
- "require-directory": {
+ "node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
- "dev": true
- },
- "require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "safe-buffer": {
+ "node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
},
- "serialize-javascript": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
- "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
"dev": true,
- "requires": {
+ "dependencies": {
"randombytes": "^2.1.0"
}
},
- "set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
- "dev": true
- },
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
- "dev": true
- },
- "stateful-process-command-proxy": {
+ "node_modules/stateful-process-command-proxy": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/stateful-process-command-proxy/-/stateful-process-command-proxy-1.0.1.tgz",
- "integrity": "sha1-dZbKKOyAS6UdoULqLEeWsbsFkoA=",
- "requires": {
- "buffer-builder": "^0.2.0",
- "fifo": "^2.3.0",
+ "integrity": "sha512-G2Hz3LPNfCnJOmZeEHz466cqsfsfIzDUDdCr+C9BFM2TEDpju4a8slFDSZ+B2inaiRCX/uQxCVnL8tb399AYrw==",
+ "dependencies": {
+ "buffer-builder": "latest",
+ "fifo": "latest",
"generic-pool": "2.4.4",
- "promise": "^8.1.0"
+ "promise": "latest"
}
},
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "node_modules/stateful-process-command-proxy/node_modules/buffer-builder": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
+ "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg=="
+ },
+ "node_modules/stateful-process-command-proxy/node_modules/fifo": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/fifo/-/fifo-2.4.1.tgz",
+ "integrity": "sha512-XTbUCNmo54Jav0hcL6VxDuY4x1eCQH61HEF80C2Oww283pfjQ2C8avZeyq4v43sW2S2403kmzssE9j4lbF66Sg=="
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
- "requires": {
- "ansi-regex": "^3.0.0"
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "strip-json-comments": {
+ "node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
- "requires": {
+ "dependencies": {
"has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "to-regex-range": {
+ "node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
}
},
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "which-module": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
- "dev": true
- },
- "wide-align": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
- "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
- "dev": true,
- "requires": {
- "string-width": "^1.0.2 || 2"
- }
- },
- "workerpool": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz",
- "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==",
+ "node_modules/workerpool": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
"dev": true
},
- "wrap-ansi": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
- "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "string-width": "^3.0.0",
- "strip-ansi": "^5.0.0"
- },
"dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "wrappy": {
+ "node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
- "y18n": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
- "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
- "dev": true
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
},
- "yargs": {
- "version": "13.3.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
- "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
- "requires": {
- "cliui": "^5.0.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^2.0.1",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^3.0.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^13.1.2"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "requires": {
- "locate-path": "^3.0.0"
- }
- },
- "locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "requires": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "requires": {
- "p-limit": "^2.0.0"
- }
- },
- "path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "yargs-parser": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
- "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "node_modules/yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
"dev": true,
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
+ "engines": {
+ "node": ">=10"
}
},
- "yargs-unparser": {
+ "node_modules/yargs-unparser": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
"integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
"dev": true,
- "requires": {
+ "dependencies": {
"camelcase": "^6.0.0",
"decamelize": "^4.0.0",
"flat": "^5.0.2",
"is-plain-obj": "^2.1.0"
},
- "dependencies": {
- "camelcase": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
- "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
- "dev": true
- },
- "decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
- "dev": true
- }
+ "engines": {
+ "node": ">=10"
}
},
- "yocto-queue": {
+ "node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
}
}
}
diff --git a/package.json b/package.json
index 1a18673..6e1f02f 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,14 @@
{
"name": "powershell-command-executor",
- "version": "1.1.0",
+ "version": "1.1.4",
"description": "Provides a registry and gateway for execution powershell commands through long-lived established remote PSSessions via a stateful-process-command-proxy pool of powershell processes",
"main": "psCommandService.js",
"directories": {
"test": "test"
},
"scripts": {
- "test": "mocha test/all.js"
+ "test": "mocha test/all.js",
+ "test-docker": "mocha test/unit.js"
},
"keywords": [
"command",
@@ -23,9 +24,9 @@
"exec"
],
"dependencies": {
- "stateful-process-command-proxy": "latest",
+ "mustache": "^4.1.0",
"promise": "latest",
- "mustache": "latest"
+ "stateful-process-command-proxy": "latest"
},
"devDependencies": {
"mocha": "latest"
diff --git a/psCommandService.js b/psCommandService.js
index 64e9896..05aee60 100644
--- a/psCommandService.js
+++ b/psCommandService.js
@@ -3,6 +3,12 @@ module.exports = PSCommandService;
var Promise = require('promise');
var Mustache = require('mustache');
+/**
+ * Reserved variables in Powershell to allow as arguments
+ * @see https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.2
+ */
+const reservedVariableNames = ['$null', '$false', '$true'];
+
/**
* PSCommandService
*
@@ -276,6 +282,7 @@ PSCommandService.prototype._generateCommand = function(commandConfig, argument2V
if ((argument.hasOwnProperty('valued') ? argument.valued : true)) {
var isQuoted = (argument.hasOwnProperty('quoted') ? argument.quoted : true);
+ var isEmpty = (argument.hasOwnProperty('empty') ? argument.empty : false);
var passedArgValues = argument2ValueMap[argumentName];
if (!(passedArgValues instanceof Array)) {
@@ -298,7 +305,7 @@ PSCommandService.prototype._generateCommand = function(commandConfig, argument2V
var passedArgValue = passedArgValues[i];
- var valueToSet;
+ var valueToSet = "";
if (passedArgValue && passedArgValue != 'undefined') {
valueToSet = passedArgValue;
@@ -308,7 +315,7 @@ PSCommandService.prototype._generateCommand = function(commandConfig, argument2V
}
// append the value
- if (valueToSet && valueToSet.trim().length > 0) {
+ if (valueToSet !== null && valueToSet !== undefined && (isEmpty || valueToSet.trim().length > 0)) {
// sanitize
valueToSet = this._sanitize(valueToSet,isQuoted);
@@ -354,22 +361,48 @@ PSCommandService.prototype._finalizeParameterValue = function(valueToSet, applyQ
return valueToSet;
}
-PSCommandService.prototype._sanitize = function(toSanitize,isQuoted) {
- toSanitize = toSanitize.replace(/([\n\r])/g, ""); // kill true newlines/feeds
-
- toSanitize = toSanitize.replace(/(\\n)/g, "\\$1"); // kill string based newline attempts
-
- // escape stuff that could screw up variables
- toSanitize = toSanitize.replace(/([`#])/g, "`$1");
-
- // if quoted, escape all quotes
- if (isQuoted) {
- toSanitize = toSanitize.replace(/(['])/g, "'$1");
-
- // if not quoted, stop $ and |
- } else {
- toSanitize = toSanitize.replace(/([;\$\|\(\)\{\}\[\]\\])/g, "`$1");
+PSCommandService.prototype._sanitize = function (toSanitize, isQuoted) {
+ toSanitize = toSanitize
+ .replace(/[\n\r]/g, "") // kill true newlines/feeds
+ .replace(/\\n/g, "\\$&") // kill string based newline attempts
+ .replace(/[`#]/g, "`$&"); // escape stuff that could screw up variables
+
+ const sanitizeRegex = /[;\$\|\(\)\{\}\[\]\\]/g;
+ const multiValuedRegex = /@\{([^}]*)\}/g;
+
+ if (isQuoted) { // if quoted, escape all quotes
+ toSanitize = toSanitize.replace(/'/g, "'$&");
+ } else if (multiValuedRegex.test(toSanitize)) {
+ // process is this is multi-valued parameter
+ const extractParams = (str, key) => {
+ // values must be wrapped in double quotes, so we can split them by comma
+ const match = str.match(new RegExp(`${key}="([^;]+)(?:";|"})`, "i"));
+ return match
+ ? match[1]
+ .split(",")
+ .map((param) =>
+ param.trim().replace(sanitizeRegex, "`$&").replace(/^"|"$/g, "")
+ )
+ : [];
+ };
+
+ const addItemsSanitized = extractParams(toSanitize, "Add");
+ const removeItemsSanitized = extractParams(toSanitize, "Remove");
+ if (addItemsSanitized.length > 0 || removeItemsSanitized.length > 0) {
+ let result = "@{";
+ if (addItemsSanitized.length > 0) {
+ result += `Add="${addItemsSanitized.join('","')}"`;
+ }
+ if (removeItemsSanitized.length > 0) {
+ if (addItemsSanitized.length > 0) result += "; ";
+ result += `Remove="${removeItemsSanitized.join('","')}"`;
+ }
+ result += "}";
+ toSanitize = result;
}
+ } else if (!reservedVariableNames.includes(toSanitize)) { // skip if this is reserved variable name
+ toSanitize = toSanitize.replace(sanitizeRegex, "`$&");
+ }
- return toSanitize;
-}
+ return toSanitize;
+};
diff --git a/test/all.js b/test/all.js
index 044a59c..c5dc463 100644
--- a/test/all.js
+++ b/test/all.js
@@ -1,74 +1,79 @@
var assert = require('assert');
-var Promise = require('promise');
-var fs = require('fs');
var o365Utils = require('../o365Utils');
var PSCommandService = require('../psCommandService');
/**
-* IMPORTANT!
-* To run this test, you need to configure
-* the following 4 variables!
-*
-* The credentials you are using to access o365 should
-* be for a user that is setup as follows @:
-* https://bitsofinfo.wordpress.com/2015/01/06/configuring-powershell-for-azure-ad-and-o365-exchange-management/
-*
-* @see https://github.com/bitsofinfo/powershell-credential-encryption-tools
-*/
+ * IMPORTANT!
+ * To run this test, you need to configure
+ * the following 4 variables!
+ *
+ * The credentials you are using to access o365 should
+ * be for a user that is setup as follows @:
+ * https://bitsofinfo.wordpress.com/2015/01/06/configuring-powershell-for-azure-ad-and-o365-exchange-management/
+ *
+ * @see https://github.com/bitsofinfo/powershell-credential-encryption-tools
+ */
var PATH_TO_DECRYPT_UTIL_SCRIPT = 'C:\\pathto\\decryptUtil.ps1';
var PATH_TO_ENCRYPTED_CREDENTIALS = 'C:\\pathto\\encrypted.credentials';
var PATH_TO_SECRET_KEY = 'C:\\pathto\\secret.key';
var O365_TENANT_DOMAIN_NAME = "somedomain.com";
-describe('test PSCommandService w/ o365CommandRegistry', function() {
-
- it('Should test all group and mail contact commands then cleanup', function(done) {
-
- this.timeout(120000);
-
- var Promise = require('promise');
+/**
+ * Following variables needed to test Certificate based connection to Exchange server
+ *
+ * @see https: //adamtheautomator.com/exchange-online-powershell-mfa/
+ * for setup instructions
+ */
+var PATH_TO_AUTH_CERTIFICATE = 'C:\\pathto\\certificate';
+var CERTIFICATE_PASSWORD = 'xxxxxx';
+var CERTIFICATE_THUMBPRINT = 'xxxxxxxxxx';
+var APPLICATION_ID = '00000000-00000000-00000000-00000000';
+var TENANT_ID = 'your.exhange.domain.name';
+
+var testRun = function (done, initCommands, preDestroyCommands) {
var StatefulProcessCommandProxy = require("stateful-process-command-proxy");
// configure our proxy/pool of processes
- var statefulProcessCommandProxy = new StatefulProcessCommandProxy(
- {
+ var statefulProcessCommandProxy = new StatefulProcessCommandProxy({
name: "o365 RemotePSSession powershell pool",
max: 1,
min: 1,
idleTimeoutMS: 30000,
- logFunction: function(severity,origin,msg) {
- if (origin != 'Pool') {
- console.log(severity.toUpperCase() + " " +origin+" "+ msg);
- }
+ logFunction: function (severity, origin, msg) {
+ if (origin != 'Pool') {
+ console.log(severity.toUpperCase() + " " + origin + " " + msg);
+ }
},
processCommand: 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
- processArgs: ['-Command','-'],
+ processArgs: ['-Command', '-'],
+
+ processRetainMaxCmdHistory: 30,
+ processInvalidateOnRegex: {
+ 'any': [{
+ 'regex': '.*nomatch.*',
+ 'flags': 'i'
+ }],
+ 'stdout': [{
+ 'regex': '.*nomatch.*'
+ }],
+ 'stderr': [{
+ 'regex': '.*nomatch.*'
+ }]
+ },
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+ initCommands: initCommands,
- processRetainMaxCmdHistory : 30,
- processInvalidateOnRegex : {
- 'any':[{'regex':'.*nomatch.*','flags':'i'}],
- 'stdout':[{'regex':'.*nomatch.*'}],
- 'stderr':[{'regex':'.*nomatch.*'}]
- },
- processCwd : null,
- processEnvMap : null,
- processUid : null,
- processGid : null,
-
- initCommands: o365Utils.getO365PSInitCommands(
- PATH_TO_DECRYPT_UTIL_SCRIPT,
- PATH_TO_ENCRYPTED_CREDENTIALS,
- PATH_TO_SECRET_KEY,
- 10000,30000,60000),
-
- validateFunction: function(processProxy) {
+ validateFunction: function (processProxy) {
return processProxy.isValid();
},
- preDestroyCommands: o365Utils.getO365PSDestroyCommands(),
+ preDestroyCommands: preDestroyCommands,
processCmdBlacklistRegex: o365Utils.getO365BlacklistedCommands(),
@@ -76,400 +81,398 @@ describe('test PSCommandService w/ o365CommandRegistry', function() {
autoInvalidationConfig: o365Utils.getO365AutoInvalidationConfig(30000)
- });
+ });
- var myLogFunction = function(severity,origin,message) {
- console.log(severity.toUpperCase() + ' ' + origin + ' ' + message);
- }
+ var myLogFunction = function (severity, origin, message) {
+ console.log(severity.toUpperCase() + ' ' + origin + ' ' + message);
+ };
- // create our PSCommandService
- var psCommandService = new PSCommandService(statefulProcessCommandProxy,
- o365Utils.o365CommandRegistry,
- myLogFunction);
+ // create our PSCommandService
+ var psCommandService = new PSCommandService(statefulProcessCommandProxy,
+ o365Utils.o365CommandRegistry,
+ myLogFunction);
- // random seed for generated data
- var random = "unitTest"+Math.abs(Math.floor(Math.random() * (1000 - 99999 + 1) + 1000));
+ // random seed for generated data
+ var random = "unitTest" + Math.abs(Math.floor(Math.random() * (1000 - 99999 + 1) + 1000));
- var testUserName = "auser-"+random;
- var testUserEmail = testUserName+"@"+O365_TENANT_DOMAIN_NAME;
+ var testUserName = "auser-" + random;
+ var testUserEmail = testUserName + "@" + O365_TENANT_DOMAIN_NAME;
- var testUser2Name = "auser2-"+random;
- var testUser2Email = testUser2Name+"@"+O365_TENANT_DOMAIN_NAME;
+ var testUser2Name = "auser2-" + random;
+ var testUser2Email = testUser2Name + "@" + O365_TENANT_DOMAIN_NAME;
- var testMailContactName = "amailContact-"+random;
- var testMailContactEmail = testMailContactName+"@"+O365_TENANT_DOMAIN_NAME;
+ var testMailContactName = "amailContact-" + random;
+ var testMailContactEmail = testMailContactName + "@" + O365_TENANT_DOMAIN_NAME;
- var testGroupName = "agroup-"+random;
- var testGroupEmail = testGroupName+"@"+O365_TENANT_DOMAIN_NAME;
+ var testGroupName = "agroup-" + random;
+ var testGroupEmail = testGroupName + "@" + O365_TENANT_DOMAIN_NAME;
- // total hack, needed due to deplays on ms side
- var sleep = function(milliseconds) {
+ // total hack, needed due to deplays on ms side
+ var sleep = function (milliseconds) {
var start = new Date().getTime();
var c = 0;
for (var i = 0; i < 1e7; i++) {
- if ((new Date().getTime() - start) > milliseconds){
- break;
+ if ((new Date().getTime() - start) > milliseconds) {
+ break;
- } else {
- console.log("SLEEP....");
- }
+ } else {
+ console.log("SLEEP....");
+ }
}
- }
+ };
- var evalCmdResult = function(cmdResult, doWithCmdResult) {
- if (cmdResult.stderr && cmdResult.stderr.length > 0) {
+ var evalCmdResult = function (cmdResult, doWithCmdResult) {
+ if (cmdResult.stderr && cmdResult.stderr.length > 0) {
console.log("Stderr received: " + cmdResult.stderr);
assert(false);
+ // otherwise assume ok
+ } else {
+ return doWithCmdResult(cmdResult);
+ }
+ };
- // otherwise assume ok
- } else {
- return doWithCmdResult(cmdResult);
- }
- }
-
- var evalCmdResults = function(cmdResults, doWithCmdResults) {
+ var evalCmdResults = function (cmdResults, doWithCmdResults) {
var hasErrors = false;
- for (var i=0; i 0) {
- console.log("Stderr received: " + cmdResult.stderr);
- hasErrors = true;
+ console.log("Stderr received: " + cmdResult.stderr);
+ hasErrors = true;
}
}
if (hasErrors) {
- assert(false);
-
- // otherwise assume ok
+ assert(false);
+ // otherwise assume ok
} else {
- return doWithCmdResults(cmdResults);
+ return doWithCmdResults(cmdResults);
}
- }
+ };
- var cleanupAndShutdown = function(done,error) {
- psCommandService.execute('removeMsolUser', {'UserPrincipalName':testUserEmail });
- psCommandService.execute('removeMsolUser', {'UserPrincipalName':testUser2Email });
- psCommandService.execute('removeDistributionGroup', {'Identity':testGroupEmail });
- psCommandService.execute('removeMailContact', {'Identity':testMailContactEmail });
+ var cleanupAndShutdown = function (done, error) {
+ psCommandService.execute('removeMsolUser', {
+ 'UserPrincipalName': testUserEmail
+ });
+ psCommandService.execute('removeMsolUser', {
+ 'UserPrincipalName': testUser2Email
+ });
+ psCommandService.execute('removeDistributionGroup', {
+ 'Identity': testGroupEmail
+ });
+ psCommandService.execute('removeMailContact', {
+ 'Identity': testMailContactEmail
+ });
- // shut it all down
- setTimeout(function() {
- statefulProcessCommandProxy.shutdown();
- },5000);
+ // shut it all down
+ setTimeout(function () {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
- setTimeout(function() {
- if (error) {
+ setTimeout(function () {
+ if (error) {
done(error);
- } else {
+ } else {
done();
- }
-
- },10000);
-
- // throw, it will stop the rest of the execution.
- if (error) {
- throw error;
- }
- }
-
-
- // #1 create test users that we will use
- var promise = psCommandService.executeAll(
- [
- {'commandName':'newMsolUser',
- 'argMap': {
- 'DisplayName':testUserName,
- 'UserPrincipalName':testUserEmail
- }
- },
- {'commandName':'newMsolUser',
- 'argMap': {
- 'DisplayName':testUser2Name,
- 'UserPrincipalName':testUser2Email
- }
- },
- ])
+ }
+ }, 10000);
+
+ // throw, it will stop the rest of the execution.
+ if (error) {
+ throw error;
+ }
+ };
+
+ // #1 create test users that we will use
+ var promise = psCommandService.executeAll(
+ [{
+ 'commandName': 'newMsolUser',
+ 'argMap': {
+ 'DisplayName': testUserName,
+ 'UserPrincipalName': testUserEmail
+ }
+ },
+ {
+ 'commandName': 'newMsolUser',
+ 'argMap': {
+ 'DisplayName': testUser2Name,
+ 'UserPrincipalName': testUser2Email
+ }
+ },
+ ])
// handle newMsolUsers results... if ok getMsolUsers
- .then(function(cmdResults) {
-
- return evalCmdResults(cmdResults, function(cmdResults) {
- try {
- assert.equal(2,cmdResults.length);
- } catch(e) {
- cleanupAndShutdown(done,e);
- }
- console.log("msolUsers added OK: " + testUserEmail + " & " + testUser2Email);
- return psCommandService.executeAll(
- [
- {'commandName':'getMsolUser', 'argMap': {'UserPrincipalName':testUserEmail }},
- {'commandName':'getMsolUser', 'argMap': {'UserPrincipalName':testUser2Email }}
+ .then(function (cmdResults) {
+
+ return evalCmdResults(cmdResults, function (cmdResults) {
+ try {
+ assert.equal(2, cmdResults.length);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
+ }
+ console.log("msolUsers added OK: " + testUserEmail + " & " + testUser2Email);
+ return psCommandService.executeAll(
+ [{
+ 'commandName': 'getMsolUser',
+ 'argMap': {
+ 'UserPrincipalName': testUserEmail
+ }
+ },
+ {
+ 'commandName': 'getMsolUser',
+ 'argMap': {
+ 'UserPrincipalName': testUser2Email
+ }
+ }
]);
- });
+ });
})
-
// handle getMsolUsers result... if ok create distributionGroup
- .then(function(cmdResults) {
-
- return evalCmdResults(cmdResults, function(cmdResults) {
+ .then(function (cmdResults) {
+ return evalCmdResults(cmdResults, function (cmdResults) {
try {
- assert.equal(2,cmdResults.length);
- } catch(e) {
- cleanupAndShutdown(done,e);
+ assert.equal(2, cmdResults.length);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
}
-
- for (var i=0; i {
+ console.log(severity.toUpperCase() + " " + origin + " " + message);
+};
+const logFunction = (severity, origin, msg) => {
+ if (origin != "Pool") {
+ console.log(severity.toUpperCase() + " " + origin + " " + msg);
+ }
+};
+
+const commandRegistry = {
+ setClipboard: {
+ command: "Set-Clipboard {{{arguments}}}",
+ arguments: {
+ 'Value': {
+ quoted: false,
+ },
+ },
+ return: {
+ type: 'none'
+ },
+ },
+ getClipboard: {
+ command: "Get-Clipboard",
+ arguments: {},
+ return: {
+ type: "text",
+ },
+ },
+ setContent: {
+ command: "Set-Content {{{arguments}}}",
+ arguments: {
+ 'Path': {},
+ 'Value': {},
+ 'Filter': {},
+ },
+ },
+ getContent: {
+ command: "Get-Content {{{arguments}}}",
+ arguments: {
+ 'Path': {},
+ 'Filter': {
+ empty: true,
+ },
+ },
+ return: {
+ type: "text",
+ },
+ },
+ removeItem: {
+ command: "Remove-Item {{{arguments}}}",
+ arguments: {
+ 'Path': {},
+ },
+ },
+};
+
+
+const StatefulProcessCommandProxy = require("stateful-process-command-proxy");
+
+const testRun = async (done, initCommands, preDestroyCommands) => {
+ const statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "o365 RemotePSSession powershell pool",
+ max: 1,
+ min: 1,
+ idleTimeoutMS: 30000,
+
+ logFunction: logFunction,
+
+ processCommand: "pwsh",
+ processArgs: ["-Command", "-"],
+
+ processRetainMaxCmdHistory: 30,
+ processInvalidateOnRegex: {
+ any: [
+ {
+ regex: ".*nomatch.*",
+ flags: "i",
+ },
+ ],
+ stdout: [
+ {
+ regex: ".*nomatch.*",
+ },
+ ],
+ stderr: [
+ {
+ regex: ".*nomatch.*",
+ },
+ ],
+ },
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+
+ initCommands: initCommands,
+
+ validateFunction: (processProxy) => processProxy.isValid(),
+
+ preDestroyCommands: preDestroyCommands,
+
+ processCmdBlacklistRegex: o365Utils.getO365BlacklistedCommands(),
+
+ processCmdWhitelistRegex: o365Utils.getO365WhitelistedCommands(),
+
+ autoInvalidationConfig: o365Utils.getO365AutoInvalidationConfig(30000),
+ });
+
+ const psCommandService = new PSCommandService(
+ statefulProcessCommandProxy,
+ o365Utils.o365CommandRegistry,
+ myLogFunction
+ );
+
+ const statusResponse = await psCommandService.execute("getStatus", {});
+ if (statusResponse.stderr == '' && statusResponse.stdout == '') {
+ console.log('Skipping test as getStatus command failed');
+ statefulProcessCommandProxy.shutdown();
+ done();
+ }
+
+ const random =
+ "unitTest" +
+ Math.abs(Math.floor(Math.random() * (1000 - 99999 + 1) + 1000));
+
+ const testMailContactName = "amailContact-" + random;
+ const testMailContactEmail =
+ testMailContactName + "@" + O365_TENANT_DOMAIN_NAME;
+
+ const testOwnerGroupName = "owneragroup-" + random;
+ const testOwnerGroupEmail =
+ testOwnerGroupName + "@" + O365_TENANT_DOMAIN_NAME;
+
+ const testGroupName = "agroup-" + random;
+ const testGroupEmail = testGroupName + "@" + O365_TENANT_DOMAIN_NAME;
+
+ const testGroupName2 = "agroup-2" + random;
+ const testGroupEmail2 = testGroupName2 + "@" + O365_TENANT_DOMAIN_NAME;
+
+ const cleanupAndShutdown = async (done, error) => {
+ await psCommandService.execute("removeDistributionGroup", {
+ Identity: testOwnerGroupEmail,
+ });
+ await psCommandService.execute("removeDistributionGroup", {
+ Identity: testGroupEmail,
+ });
+ await psCommandService.execute("removeDistributionGroup", {
+ Identity: testGroupEmail2,
+ });
+ await psCommandService.execute("removeMailContact", {
+ Identity: testMailContactEmail,
+ });
+
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+
+ setTimeout(() => {
+ if (error) {
+ done(error);
+ } else {
+ done();
+ }
+ }, 10000);
+
+ if (error) {
+ throw error;
+ }
+ };
+
+ try {
+ const ownerGroupCreateResult = await psCommandService.execute(
+ "newDistributionGroup",
+ {
+ Name: testOwnerGroupName,
+ DisplayName: testOwnerGroupName,
+ PrimarySmtpAddress: testOwnerGroupEmail,
+ }
+ );
+ assert.equal(ownerGroupCreateResult.stderr, "");
+
+ const testGroupCreateResult = await psCommandService.execute(
+ "newDistributionGroup",
+ {
+ Name: testGroupName,
+ DisplayName: testGroupName,
+ PrimarySmtpAddress: testGroupEmail,
+ ManagedBy: testOwnerGroupEmail,
+ }
+ );
+
+ assert.equal(testGroupCreateResult.stderr, "");
+ assert.equal(testGroupCreateResult.commandName, "newDistributionGroup");
+
+ const distributionGroup = JSON.parse(testGroupCreateResult.stdout);
+ try {
+ assert.equal(testGroupEmail, distributionGroup.PrimarySmtpAddress);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
+ }
+ console.log(
+ "distributionGroup created OK: " + distributionGroup.PrimarySmtpAddress
+ );
+
+ const testGroup2CreateResult = await psCommandService.execute(
+ "newDistributionGroup",
+ {
+ Name: testGroupName2,
+ DisplayName: testGroupName2,
+ PrimarySmtpAddress: testGroupEmail2,
+ ManagedBy: testOwnerGroupEmail,
+ }
+ );
+
+ assert.equal(testGroup2CreateResult.stderr, "");
+ assert.equal(testGroup2CreateResult.commandName, "newDistributionGroup");
+
+ const distributionGroup2 = JSON.parse(testGroup2CreateResult.stdout);
+ try {
+ assert.equal(testGroupEmail2, distributionGroup2.PrimarySmtpAddress);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
+ }
+ console.log(
+ "distributionGroup created OK: " + distributionGroup2.PrimarySmtpAddress
+ );
+
+ await psCommandService.executeAll([
+ {
+ commandName: "addDistributionGroupMember",
+ argMap: {
+ Identity: testGroupEmail,
+ Member: testGroupEmail2,
+ BypassSecurityGroupManagerCheck: null,
+ },
+ },
+ {
+ commandName: "addDistributionGroupMember",
+ argMap: {
+ Identity: testGroupEmail,
+ Member: testOwnerGroupEmail,
+ BypassSecurityGroupManagerCheck: null,
+ },
+ },
+ ]);
+ console.log("distributionGroupMembers added OK");
+
+ const groupMembersResult = await psCommandService.execute(
+ "getDistributionGroupMember",
+ {
+ Identity: testGroupEmail,
+ }
+ );
+
+ assert.equal(groupMembersResult.stderr, "");
+ assert.equal(groupMembersResult.commandName, "getDistributionGroupMember");
+
+ var members = JSON.parse(groupMembersResult.stdout);
+ try {
+ assert.equal(members.length, 2);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
+ }
+ console.log("distributionGroup members fetched OK: " + members.length);
+ const removeResult = await psCommandService.execute(
+ "removeDistributionGroupMember",
+ {
+ Identity: testGroupEmail,
+ Member: testGroupEmail2,
+ }
+ );
+ assert.equal(removeResult.stderr, "");
+ assert.equal(removeResult.commandName, "removeDistributionGroupMember");
+
+ console.log(`distributionGroupMember (${testGroupEmail2}) removed OK`);
+
+ const refetchGroupMembersResult = await psCommandService.execute(
+ "getDistributionGroupMember",
+ {
+ Identity: testGroupEmail,
+ }
+ );
+ var members = JSON.parse("[" + refetchGroupMembersResult.stdout + "]");
+ try {
+ assert.equal(members.length, 1);
+ assert.equal(members[0].PrimarySmtpAddress, testOwnerGroupEmail);
+ } catch (e) {
+ return cleanupAndShutdown(done, e);
+ }
+ console.log(
+ "getDistributionGroupMember fetched OK: only owner group remains " +
+ members.length
+ );
+ const contactResult = await psCommandService.execute("newMailContact", {
+ Name: testMailContactName,
+ ExternalEmailAddress: testMailContactEmail,
+ });
+
+ assert.equal(contactResult.stderr, "");
+ assert.equal(contactResult.commandName, "newMailContact");
+
+ console.log("newMailContact added OK: " + testMailContactEmail);
+ const getContactResult = await psCommandService.execute("getMailContact", {
+ Identity: testMailContactEmail,
+ });
+
+ var contact = JSON.parse(getContactResult.stdout);
+ try {
+ assert.equal(testMailContactEmail, contact.PrimarySmtpAddress);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
+ }
+ console.log("getMailContact fetched OK: " + testMailContactEmail);
+ await psCommandService.execute("addDistributionGroupMember", {
+ Identity: testGroupEmail,
+ Member: testMailContactEmail,
+ });
+
+ console.log(
+ "addDistributionGroupMember mailContact added OK: " + testMailContactEmail
+ );
+ const getGroupMembersResult = await psCommandService.execute(
+ "getDistributionGroupMember",
+ {
+ Identity: testGroupEmail,
+ }
+ );
+
+ var members = JSON.parse(getGroupMembersResult.stdout);
+ try {
+ assert.equal(members.length, 2);
+ } catch (e) {
+ cleanupAndShutdown(done, e);
+ }
+ console.log(
+ "getDistributionGroupMember fetched OK: one mail contact and one group exist " +
+ members.length
+ );
+ await psCommandService.execute("removeDistributionGroup", {
+ Identity: testGroupEmail,
+ });
+
+ console.log("distributionGroup removed OK: " + testGroupEmail);
+
+ done();
+ } catch (error) {
+ cleanupAndShutdown(done, error);
+ }
+};
+
+describe("test PSCommandService w/ o365CommandRegistry", function () {
+ it("Should test all group and mail contact commands then cleanup with Certificate based auth", function (done) {
+ this.timeout(120000);
+ testRun(done, initExchangeCommands, preDestroyCommands);
+ });
+ it("Should test whitelist", async function () {
+ this.timeout(10000);
+ const statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "Powershell pool",
+ max: 1,
+ min: 1,
+ idleTimeoutMS: 30000,
+
+ logFunction: logFunction,
+ processCommand: "pwsh",
+ processArgs: ["-Command", "-"],
+ processRetainMaxCmdHistory: 30,
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+ initCommands: initCommands,
+ processCmdWhitelistRegex: [{ regex: '^Set-Clipboard\\s+.*', flags: 'i' }],
+ validateFunction: (processProxy) => processProxy.isValid(),
+ });
+
+ const psCommandService = new PSCommandService(
+ statefulProcessCommandProxy,
+ commandRegistry,
+ myLogFunction
+ );
+
+ try {
+ const value = "'test clipboard value'";
+ const setResult = await psCommandService.execute("setClipboard", {
+ Value: value,
+ });
+ assert.equal(setResult.stderr, "");
+ try {
+ await psCommandService.execute("getClipboard", {});
+ } catch (e) {
+ assert.match(e.message, /Command cannot be executed it does not match our set of whitelisted commands/);
+ }
+
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+
+ return;
+ } catch (e) {
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+ throw e;
+ }
+ });
+ it("Should test blacklist", async function () {
+ this.timeout(10000);
+ const statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "Powershell pool",
+ max: 1,
+ min: 1,
+ idleTimeoutMS: 30000,
+
+ logFunction: logFunction,
+ processCommand: "pwsh",
+ processArgs: ["-Command", "-"],
+ processRetainMaxCmdHistory: 30,
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+ initCommands: initCommands,
+ processCmdBlacklistRegex: o365Utils.getO365BlacklistedCommands(),
+ validateFunction: (processProxy) => processProxy.isValid(),
+ });
+ const extendedCommandRegistry = {...commandRegistry, ...{
+ getHistory: {
+ command: "Get-History",
+ arguments: {},
+ return: {
+ type: "text",
+ },
+ },
+ }};
+
+ const psCommandService = new PSCommandService(
+ statefulProcessCommandProxy,
+ extendedCommandRegistry,
+ myLogFunction
+ );
+
+ const allowResult = await psCommandService.execute("getClipboard", {});
+ assert.equal(allowResult.stderr, "");
+ assert.equal(allowResult.stdout, "");
+ try {
+ await psCommandService.execute("getHistory", {});
+ } catch (e) {
+ assert.match(e.message, /Command cannot be executed as it matches a blacklist regex pattern/);
+ }
+
+ try {
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+
+ return;
+ } catch (e) {
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+ throw e;
+ }
+ });
+ it("Should test validation", async function () {
+ this.timeout(10000);
+ const statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "Powershell pool",
+ max: 1,
+ min: 1,
+ idleTimeoutMS: 30000,
+
+ logFunction: logFunction,
+ processCommand: "pwsh",
+ processArgs: ["-Command", "-"],
+ processRetainMaxCmdHistory: 30,
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+ initCommands: initCommands,
+ validateFunction: (processProxy) => processProxy.isValid(),
+ });
+
+ const psCommandService = new PSCommandService(
+ statefulProcessCommandProxy,
+ commandRegistry,
+ myLogFunction
+ );
+
+ const assertClipboard = async (value) => {
+ const setResult = await psCommandService.execute("setClipboard", {
+ Value: value,
+ });
+ assert.equal(setResult.stderr, "");
+ const getResult = await psCommandService.execute("getClipboard", {});
+ assert.equal(getResult.stderr, "");
+ return getResult;
+ }
+
+ try {
+ // non quoted value
+ var value = "plain text in clipboard";
+ var setResult = await psCommandService.execute("setClipboard", {
+ Value: value,
+ });
+ assert.equal(setResult.stdout, "");
+ assert.match(setResult.stderr, /A positional parameter cannot be found that accepts argument/);
+ await psCommandService.execute("getClipboard", {});
+ // simple multi param value
+ var res = await assertClipboard('@{add="test","test2";remove="test3","test4"}');
+ assert.equal(res.stdout, "System.Collections.Hashtable");
+ // multi params value with unsupported keys
+ value = '@{add="test","test2";remove="test3","test4";fake="test5","test6"}';
+ setResult = await psCommandService.execute("setClipboard", {
+ Value: value,
+ });
+ assert.equal(setResult.command, 'Set-Clipboard -Value @{Add="test","test2"; Remove="test3","test4"} ');
+ assert.equal(setResult.stderr, "");
+ getResult = await psCommandService.execute("getClipboard", {});
+ assert.equal(getResult.stderr, "");
+ assert.equal(getResult.stdout, "System.Collections.Hashtable");
+ // sample quoted test
+ res = await assertClipboard("'sample text'");
+ assert.equal(res.stdout, "sample text");
+
+ // espcaped quotes
+ value = "'; Get-ChildItem C:\; '";
+ setResult = await psCommandService.execute("setClipboard", {
+ Value: value,
+ });
+ assert.equal(setResult.stderr, "");
+ getResult = await psCommandService.execute("getClipboard", {});
+ assert.equal(getResult.stdout, "`; Get-ChildItem C:`;");
+ // reserved variable
+ var res = await assertClipboard('$true');
+ assert.equal(res.stdout, "True");
+
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+
+ return;
+ } catch (e) {
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+ throw e;
+ }
+ });
+ it("Should test value bleeding", async function () {
+ this.timeout(10000);
+ const statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "Powershell pool",
+ max: 1,
+ min: 1,
+ idleTimeoutMS: 30000,
+
+ logFunction: logFunction,
+ processCommand: "pwsh",
+ processArgs: ["-Command", "-"],
+ processRetainMaxCmdHistory: 30,
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+ initCommands: initCommands,
+ validateFunction: (processProxy) => processProxy.isValid(),
+ });
+
+ const psCommandService = new PSCommandService(
+ statefulProcessCommandProxy,
+ commandRegistry,
+ myLogFunction
+ );
+ try {
+ const newResult = await psCommandService.execute("setContent", {
+ Path: "./test.txt",
+ Value: "Test",
+ Filter: ""
+ });
+ assert.equal(newResult.command.trim(), "Set-Content -Path './test.txt' -Value 'Test'");
+ assert.equal(newResult.stderr, "");
+ const getResult = await psCommandService.execute("getContent", {
+ Path: "./test.txt",
+ });
+ assert.equal(getResult.stderr, "");
+ assert.equal(getResult.stdout, "Test");
+ } catch (e) {
+ assert.fail(e);
+ } finally {
+ await psCommandService.execute("removeItem", {
+ Path: "./test.txt",
+ });
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+ }
+ });
+ it("Should test empty value support", async function () {
+ this.timeout(10000);
+ const statefulProcessCommandProxy = new StatefulProcessCommandProxy({
+ name: "Powershell pool",
+ max: 1,
+ min: 1,
+ idleTimeoutMS: 30000,
+
+ logFunction: logFunction,
+ processCommand: "pwsh",
+ processArgs: ["-Command", "-"],
+ processRetainMaxCmdHistory: 30,
+ processCwd: null,
+ processEnvMap: null,
+ processUid: null,
+ processGid: null,
+ initCommands: initCommands,
+ validateFunction: (processProxy) => processProxy.isValid(),
+ });
+
+ const psCommandService = new PSCommandService(
+ statefulProcessCommandProxy,
+ commandRegistry,
+ myLogFunction
+ );
+ try {
+ const newResult = await psCommandService.execute("setContent", {
+ Path: "./test.txt",
+ Value: "Test",
+ Filter: ""
+ });
+ assert.equal(newResult.command.trim(), "Set-Content -Path './test.txt' -Value 'Test'");
+ assert.equal(newResult.stderr, "");
+ const getResult = await psCommandService.execute("getContent", {
+ Path: "./test.txt",
+ Filter: ""
+ });
+ assert.equal(getResult.command.trim(), "Get-Content -Path './test.txt' -Filter ''");
+ assert.equal(getResult.stderr, "");
+ assert.equal(getResult.stdout, "Test");
+ } catch (e) {
+ assert.fail(e);
+ } finally {
+ await psCommandService.execute("removeItem", {
+ Path: "./test.txt",
+ });
+ setTimeout(() => {
+ statefulProcessCommandProxy.shutdown();
+ }, 5000);
+ }
+ });
+});