diff --git a/cli-messaging/src/main/java/com/salesforce/messaging/CliMessager.java b/cli-messaging/src/main/java/com/salesforce/messaging/CliMessager.java index bf0a0a34f..b96a53aec 100644 --- a/cli-messaging/src/main/java/com/salesforce/messaging/CliMessager.java +++ b/cli-messaging/src/main/java/com/salesforce/messaging/CliMessager.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.List; +import com.google.common.collect.Lists; import com.google.gson.Gson; public class CliMessager { @@ -12,8 +13,17 @@ public class CliMessager { // The END string lets us know when a message stops, which should prevent bugs involving multi-line output. private static final String END = "SFDX-END"; + private static final String REALTIME_START = "SFCA-REALTIME-START"; + private static final String REALTIME_END = "SFCA-REALTIME-END"; + + /* Deprecated: Don't maintain state in a class that's essentially used as a utility.*/ + @Deprecated private static final List MESSAGES = new ArrayList<>(); + /** + * Deprecated - switch to static invocation of {@link #postMessage(String, EventKey, String...)} + */ + @Deprecated public static CliMessager getInstance() { return LazyHolder.INSTANCE; } @@ -26,6 +36,7 @@ public static CliMessager getInstance() { * * @param exception to send to Typescript layer */ + @Deprecated public void addMessage(MessagePassableException exception) { final EventKey eventKey = exception.getEventKey(); addMessage( @@ -43,34 +54,59 @@ public void addMessage(MessagePassableException exception) { * @param eventKey EventKey to display to user * @param args String args passed to the EventKey to make the displayed message meaningful */ + @Deprecated public void addMessage(String internalLog, EventKey eventKey, String... args) { - // Developer error if eventKey was not added to exception and we'll get a bunch of NPEs - assert (eventKey != null); - // Confirm that the correct number of arguments for the message has been provided - // If this fails, this would be a developer error - assert (eventKey.getArgCount() == args.length); - - final Message message = new Message( - eventKey.getMessageKey(), - Arrays.asList(args), - internalLog, - eventKey.getMessageType(), - eventKey.getMessageHandler(), - eventKey.isVerbose()); - MESSAGES.add(message); + final Message message = createMessage(internalLog, eventKey, args); + MESSAGES.add(message); } - /** + /** + * Publish formatted stdout message to pass onto Typescript layer. + * Make sure EventKey is updated with messages/EventKeyTemplates.json + * and has correct properties in the enum. + * + * @param internalLog Information for internal use. Will be logged but not displayed to user + * @param eventKey EventKey to display to user + * @param args String args passed to the EventKey to make the displayed message meaningful + */ + public static void postMessage(String internalLog, EventKey eventKey, String... args) { + final Message message = createMessage(internalLog, eventKey, args); + final List messages = Lists.newArrayList(message); + + final String messageAsJson = new Gson().toJson(messages); + System.out.println(REALTIME_START + messageAsJson + REALTIME_END); + } + + private static Message createMessage(String internalLog, EventKey eventKey, String[] args) { + // Developer error if eventKey was not added to exception and we'll get a bunch of NPEs + assert (eventKey != null); + // Confirm that the correct number of arguments for the message has been provided + // If this fails, this would be a developer error + assert (eventKey.getArgCount() == args.length); + + final Message message = new Message( + eventKey.getMessageKey(), + Arrays.asList(args), + internalLog, + eventKey.getMessageType(), + eventKey.getMessageHandler(), + eventKey.isVerbose()); + return message; + } + + /** * Convert all messages stored by the instance into a JSON-formatted string, enclosed in the start and end strings. * Java code can use this method to log the messages to console, and TypeScript code can seek the start and stop * strings to get an array of messages that can be deserialized. * @return */ + @Deprecated public String getAllMessagesWithFormatting() { final String messagesAsJson = getMessagesAsJson(); return START + messagesAsJson + END; } + @Deprecated private String getMessagesAsJson() { return new Gson().toJson(MESSAGES); } diff --git a/cli-messaging/src/main/java/com/salesforce/messaging/EventKey.java b/cli-messaging/src/main/java/com/salesforce/messaging/EventKey.java index a09f07269..2b3c77685 100644 --- a/cli-messaging/src/main/java/com/salesforce/messaging/EventKey.java +++ b/cli-messaging/src/main/java/com/salesforce/messaging/EventKey.java @@ -3,6 +3,8 @@ public enum EventKey { // MAKE SURE `messageKey` OF EVERY VALUE ADDED HERE HAS AN ENTRY IN 'messages/EventKeyTemplates.js'! + + /** PMD-CATALOGER RELATED **/ INFO_GENERAL_INTERNAL_LOG("info.generalInternalLog", 1, MessageType.INFO, MessageHandler.INTERNAL, true), WARNING_INVALID_CAT_SKIPPED("warning.invalidCategorySkipped", 1, MessageType.WARNING, MessageHandler.UX, true), WARNING_INVALID_RULESET_SKIPPED("warning.invalidRulesetSkipped", 1, MessageType.WARNING, MessageHandler.UX, true), @@ -20,8 +22,23 @@ public enum EventKey { ERROR_EXTERNAL_RECURSION_LIMIT("error.external.recursionLimitReached", 2, MessageType.ERROR, MessageHandler.UX, false), ERROR_EXTERNAL_XML_NOT_READABLE("error.external.xmlNotReadable", 2, MessageType.ERROR, MessageHandler.UX, false), ERROR_EXTERNAL_XML_NOT_PARSABLE("error.external.xmlNotParsable", 2, MessageType.ERROR, MessageHandler.UX, false), + + + + + /** SFGE RELATED **/ + INFO_GENERAL("info.sfgeInfoLog", 1, MessageType.INFO, MessageHandler.UX, true), + INFO_META_INFO_COLLECTED("info.sfgeMetaInfoCollected", 2, MessageType.INFO, MessageHandler.UX, true), + INFO_COMPLETED_FILE_COMPILATION("info.sfgeFinishedCompilingFiles", 1, MessageType.INFO, MessageHandler.UX_SPINNER, false), + INFO_STARTED_BUILDING_GRAPH("info.sfgeStartedBuildingGraph", 0, MessageType.INFO, MessageHandler.UX_SPINNER, false), + INFO_COMPLETED_BUILDING_GRAPH("info.sfgeFinishedBuildingGraph", 0, MessageType.INFO, MessageHandler.UX_SPINNER, false), + INFO_PATH_ENTRY_POINTS_IDENTIFIED("info.sfgePathEntryPointsIdentified", 1, MessageType.INFO, MessageHandler.UX_SPINNER, false), + INFO_PATH_ANALYSIS_PROGRESS("info.sfgeViolationsInPathProgress", 4, MessageType.INFO, MessageHandler.UX_SPINNER, false), + INFO_COMPLETED_PATH_ANALYSIS("info.sfgeCompletedPathAnalysis", 3, MessageType.INFO, MessageHandler.UX_SPINNER, false), + WARNING_GENERAL("warning.sfgeWarnLog", 1, MessageType.WARNING, MessageHandler.UX, true), WARNING_MULTIPLE_METHOD_TARGET_MATCHES("warning.multipleMethodTargetMatches", 3, MessageType.WARNING, MessageHandler.UX, false), - WARNING_NO_METHOD_TARGET_MATCHES("warning.noMethodTargetMatches", 2, MessageType.WARNING, MessageHandler.UX, false); + WARNING_NO_METHOD_TARGET_MATCHES("warning.noMethodTargetMatches", 2, MessageType.WARNING, MessageHandler.UX, false), + ERROR_GENERAL("error.internal.sfgeErrorLog", 1, MessageType.ERROR, MessageHandler.UX, false); final String messageKey; final int argCount; diff --git a/cli-messaging/src/main/java/com/salesforce/messaging/Message.java b/cli-messaging/src/main/java/com/salesforce/messaging/Message.java index 20ba93ae0..b5df0e39e 100644 --- a/cli-messaging/src/main/java/com/salesforce/messaging/Message.java +++ b/cli-messaging/src/main/java/com/salesforce/messaging/Message.java @@ -6,7 +6,7 @@ public class Message { final private String messageKey; final private List args; - final private String internalLog; + final private String internalLog; final private MessageType type; final private MessageHandler handler; final private boolean verbose; @@ -34,8 +34,25 @@ public String getInternalLog() { return internalLog; } + public MessageType getType() { + return type; + } + + public MessageHandler getHandler() { + return handler; + } + + public boolean isVerbose() { + return verbose; + } + + public long getTime() { + return time; + } + enum MessageHandler { UX, + UX_SPINNER, INTERNAL } diff --git a/messages/DefaultRuleManager.js b/messages/DefaultRuleManager.js index d1a440e3c..af170dd4e 100644 --- a/messages/DefaultRuleManager.js +++ b/messages/DefaultRuleManager.js @@ -1,6 +1,7 @@ module.exports = { "warning": { "targetSkipped": "Target: '%s' was not processed by any engines.", - "targetsSkipped": "Targets: '%s' were not processed by any engines." + "targetsSkipped": "Targets: '%s' were not processed by any engines.", + "pathsDoubleProcessed": "At least one file was processed by both ESLint and ESLint-LWC simultaneously, which could result in duplicate violations. Customize the targetPatterns property for eslint and eslint-lwc engines in %s to remove overlap on the following file(s): %s", } } diff --git a/messages/EventKeyTemplates.js b/messages/EventKeyTemplates.js index a5d0f3a48..5206ccc06 100644 --- a/messages/EventKeyTemplates.js +++ b/messages/EventKeyTemplates.js @@ -7,7 +7,15 @@ module.exports = { "customEslintHeadsUp": "About to run Eslint with custom config in %s. Please make sure your current directory has all the required NPM dependencies.", "customPmdHeadsUp": "About to run PMD with custom config in %s. Please make sure that any custom rule references have already been added to the plugin through scanner:rule:add command.", "pmdRuleSkipped": "Omitting results for PMD rule \"%s\". Reason: %s.", - "unmatchedPathExtensionCpd": "Path extensions for the following files will not be processed by CPD: %s" + "unmatchedPathExtensionCpd": "Path extensions for the following files will not be processed by CPD: %s", + "sfgeInfoLog": "%s", + "sfgeMetaInfoCollected": "Loaded %s: [ %s ]", + "sfgeFinishedCompilingFiles": "Compiled %s files.", + "sfgeStartedBuildingGraph": "Building graph.", + "sfgeFinishedBuildingGraph": "Added all compilation units to graph.", + "sfgePathEntryPointsIdentified": "Identified %s path entry point(s).", + "sfgeViolationsInPathProgress": "Detected %s violation(s) from %s path(s) on %s/%s entry point(s).", + "sfgeCompletedPathAnalysis": "Overall, analyzed %s path(s) from %s entry point(s). Detected %s violation(s)." }, "warning": { "invalidCategorySkipped": "Cataloger skipped invalid PMD Category file '%s'.", @@ -20,7 +28,8 @@ module.exports = { "unexpectedPmdNodeType": "Encountered unexpected PMD node of type '%s'", "multipleMethodTargetMatches": "Total of %s methods in file %s matched name #%s", "noMethodTargetMatches": "No methods in file %s matched name #%s()", - "pmdConfigError": "PMD failed to evaluate rule '%s'. Message: %s" + "pmdConfigError": "PMD failed to evaluate rule '%s'. Message: %s", + "sfgeWarnLog": "%s" }, "error": { "internal": { @@ -28,7 +37,8 @@ module.exports = { "mainInvalidArgument": "INTERNAL ERROR: Invalid arguments passed to Main. Details: %s. Please log an issue with us at github.com/forcedotcom/sfdx-scanner.", "jsonWriteFailed": "INTERNAL ERROR: Failed to write JSON to file: %s. Please log an issue with us at github.com/forcedotcom/sfdx-scanner.", "classpathDoesNotExist": "INTERNAL ERROR: Path does not exist: %s. Please log an issue with us at github.com/forcedotcom/sfdx-scanner.", - "xmlMissingInClasspath": "INTERNAL ERROR: XML resource [%s] found in jar, but not in Classpath. Please log an issue with us at github.com/forcedotcom/sfdx-scanner." + "xmlMissingInClasspath": "INTERNAL ERROR: XML resource [%s] found in jar, but not in Classpath. Please log an issue with us at github.com/forcedotcom/sfdx-scanner.", + "sfgeErrorLog": "%s" }, "external": { "errorMessageAbove": "Please see error details displayed above.", diff --git a/messages/common.js b/messages/common.js index be555a676..810df19b0 100644 --- a/messages/common.js +++ b/messages/common.js @@ -1,3 +1,3 @@ module.exports = { - FEEDBACK_SURVEY_BANNER: `We're constantly improving Salesforce Code Analyzer. Tell us what you think! Give feedback at https://research.net/r/SalesforceCA.` -}; + surveyRequestMessage: `We're constantly improving Salesforce Code Analyzer. Tell us what you think! Give feedback at https://research.net/r/SalesforceCA.` +}; \ No newline at end of file diff --git a/package.json b/package.json index e35f376fd..1b6d8a18e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/sfdx-scanner", "description": "Static code scanner that applies quality and security rules to Apex code, and provides feedback.", - "version": "3.3.0", + "version": "3.4.0", "author": "ISV SWAT", "bugs": "https://github.com/forcedotcom/sfdx-scanner/issues", "dependencies": { diff --git a/pmd-cataloger/build.gradle.kts b/pmd-cataloger/build.gradle.kts index 966b43f10..bc7f90997 100644 --- a/pmd-cataloger/build.gradle.kts +++ b/pmd-cataloger/build.gradle.kts @@ -9,7 +9,7 @@ group = "sfdx" version = "1.0" val distDir = "$buildDir/../../dist" -val pmdVersion = "6.47.0" +val pmdVersion = "6.48.0" val pmdFile = "pmd-bin-$pmdVersion.zip" val pmdUrl = "https://github.com/pmd/pmd/releases/download/pmd_releases%2F${pmdVersion}/${pmdFile}" val skippableJarRegexes = setOf("""^common_[\d\.-]*\.jar""".toRegex(), diff --git a/retire-js/RetireJsVulns.json b/retire-js/RetireJsVulns.json index 1c8d04ba9..5c361ad2e 100644 --- a/retire-js/RetireJsVulns.json +++ b/retire-js/RetireJsVulns.json @@ -287,10 +287,12 @@ "jquery.validat(?:ion|e)-(§§version§§)(.min)?\\.js" ], "uri": [ - "/(§§version§§)/jquery.validat(ion|e)(\\.min)?\\.js" + "/(§§version§§)/jquery.validat(ion|e)(\\.min)?\\.js", + "/jquery-validation@(§§version§§)/dist/.*\\.js" ], "filecontent": [ - "/\\*!?(?:\n \\*)? jQuery Validation Plugin v(§§version§§)" + "/\\*!?(?:\n \\*)?[\\s]*jQuery Validation Plugin -? ?v(§§version§§)", + "Original file: /npm/jquery-validation@(§§version§§)/dist/jquery.validate.js" ], "hashes": {} } @@ -444,9 +446,26 @@ "https://github.com/jquery/jquery-ui/security/advisories/GHSA-9gj3-hwp5-pmwc", "https://nvd.nist.gov/vuln/detail/CVE-2021-41182" ] + }, + { + "below": "1.13.2", + "severity": "medium", + "identifiers": { + "CVE": [ + "CVE-2022-31160" + ], + "summary": "XSS when refreshing a checkboxradio with an HTML-like initial text label " + }, + "info": [ + "https://github.com/jquery/jquery-ui/security/advisories/GHSA-h6gj-6jjq-h8g9", + "https://nvd.nist.gov/vuln/detail/CVE-2022-31160" + ] } ], "extractors": { + "uri": [ + "/(§§version§§)/jquery-ui(\\.min)?\\.js" + ], "filecontent": [ "/\\*!? jQuery UI - v(§§version§§)", "/\\*!?[\n *]+jQuery UI (§§version§§)" @@ -578,6 +597,10 @@ } ], "extractors": { + "uri": [ + "/prettyPhoto/(§§version§§)/js/jquery\\.prettyPhoto(\\.min?)\\.js", + "/prettyphoto@(§§version§§)/js/jquery\\.prettyPhoto\\.js" + ], "filecontent": [ "/\\*[\r\n -]+Class: prettyPhoto(?:.*\n){1,3}[ ]*Version: (§§version§§)", "\\.prettyPhoto[ ]?=[ ]?\\{version:[ ]?(?:'|\")(§§version§§)(?:'|\")\\}" @@ -661,8 +684,11 @@ "filename": [ "knockout-(§§version§§)(.min)?\\.js" ], + "uri": [ + "/knockout/(§§version§§)/knockout(-[a-z.]+)?\\.js" + ], "filecontent": [ - "\\* Knockout JavaScript library v(§§version§§)" + "(?:\\*|//) Knockout JavaScript library v(§§version§§)" ], "hashes": {} } @@ -850,8 +876,12 @@ } ], "extractors": { + "uri": [ + "/tinymce/(§§version§§)/tinymce(\\.min)?\\.js" + ], "filecontent": [ - "// (§§version§§) \\([0-9\\-]+\\)[\n\r]+.{0,1200}l=.tinymce/geom/Rect." + "// (§§version§§) \\([0-9\\-]+\\)[\n\r]+.{0,1200}l=.tinymce/geom/Rect.", + "/\\*\\*[\\s]*\\* TinyMCE version (§§version§§)" ], "filecontentreplace": [ "/tinyMCEPreInit.*majorVersion:.([0-9]+).,minorVersion:.([0-9.]+)./$1.$2/", @@ -1360,7 +1390,8 @@ ], "extractors": { "uri": [ - "/(?:v)?(§§version§§)/ember(\\.min)?\\.js" + "/(?:v)?(§§version§§)/ember(\\.min)?\\.js", + "/ember\\.?js/(§§version§§)/ember((\\.|-)[a-z\\-.]+)?\\.js" ], "filename": [ "ember-(§§version§§)(\\.min)?\\.js" @@ -1368,7 +1399,11 @@ "filecontent": [ "Project: Ember -(?:.*\n){9,11}// Version: v(§§version§§)", "// Version: v(§§version§§)(.*\n){10,15}(Ember Debug|@module ember|@class ember)", - "Ember.VERSION[ ]?=[ ]?(?:'|\")(§§version§§)(?:'|\")" + "Ember.VERSION[ ]?=[ ]?(?:'|\")(§§version§§)(?:'|\")", + "meta\\.revision=\"Ember@(§§version§§)\"", + "e\\(\"ember/version\",\\[\"exports\"\\],function\\(e\\)\\{\"use strict\";?[\\s]*e(?:\\.|\\[\")default(?:\"\\])?=\"(§§version§§)\"", + "\\(\"ember/version\",\\[\"exports\"\\],function\\(e\\)\\{\"use strict\";.{1,70}\\.default=\"(§§version§§)\"", + "/\\*![\\s]+\\* @overview Ember - JavaScript Application Framework[\\s\\S]{0,400}\\* @version (§§version§§)" ], "hashes": {} } @@ -1733,6 +1768,20 @@ "https://vulnerabledoma.in/ngSanitize1.6.8_bypass.html" ] }, + { + "below": "1.5.0-beta.1", + "severity": "medium", + "identifiers": { + "summary": "XSS through xlink:href attributes", + "CVE": [ + "CVE-2019-14863" + ] + }, + "info": [ + "https://github.com/angular/angular.js/blob/master/CHANGELOG.md#150-beta1-dense-dispersion-2015-09-29", + "https://github.com/advisories/GHSA-r5fx-8r73-v86c" + ] + }, { "atOrAbove": "1.3.0", "below": "1.5.0-rc2", @@ -1786,6 +1835,16 @@ "info": [ "https://github.com/angular/angular.js/commit/8f31f1ff43b673a24f84422d5c13d6312b2c4d94" ] + }, + { + "below": "1.999", + "severity": "low", + "identifiers": { + "summary": "End-of-Life: Long term support for AngularJS has been discontinued" + }, + "info": [ + "https://blog.angular.io/discontinued-long-term-support-for-angularjs-cc066b82e65a?gi=9d3103b5445c" + ] } ], "extractors": { @@ -1829,7 +1888,7 @@ ], "filecontent": [ "//[ ]+Backbone.js (§§version§§)", - "a=t.Backbone={}}a.VERSION=\"(§§version§§)\"" + "a=t.Backbone=\\{\\}\\}a.VERSION=\"(§§version§§)\"" ], "hashes": {} } @@ -2283,7 +2342,9 @@ "filecontent": [ "DOMPurify.version = '(§§version§§)';", "DOMPurify.version=\"(§§version§§)\"", - "DOMPurify=.[^\\r\\n]{10,500}\\.version=\"(§§version§§)\"" + "DOMPurify=.[^\\r\\n]{10,850}?\\.version=\"(§§version§§)\"", + "/\\*! @license DOMPurify (§§version§§)", + "var .=\"dompurify\"+.{10,550}?\\.version=\"(§§version§§)\"" ], "hashes": {} } @@ -2718,7 +2779,7 @@ "/\\*!? Bootstrap v(§§version§§)", "\\* Bootstrap v(§§version§§)", "/\\*! Bootstrap v(§§version§§)", - "this\\.close\\)};.\\.VERSION=\"(§§version§§)\"(?:,.\\.TRANSITION_DURATION=150)?,.\\.prototype\\.close" + "this\\.close\\)\\};.\\.VERSION=\"(§§version§§)\"(?:,.\\.TRANSITION_DURATION=150)?,.\\.prototype\\.close" ], "hashes": {} } @@ -2992,7 +3053,9 @@ ], "extractors": { "uri": [ - "/vue@(§§version§§)/dist/vue\\.js" + "/vue@(§§version§§)/dist/vue\\.js", + "/vue/(§§version§§)/vue\\..*\\.js", + "/npm/vue@(§§version§§)" ], "filename": [ "vue-(§§version§§)(\\.min)?\\.js" @@ -3000,7 +3063,10 @@ "filecontent": [ "/\\*!\\n \\* Vue.js v(§§version§§)", "Vue.version = '(§§version§§)';", - "'(§§version§§)'[^\\n]{0,8000}Vue compiler" + "'(§§version§§)'[^\\n]{0,8000}Vue compiler", + "\\* Original file: /npm/vue@(§§version§§)/dist/vue.(global|common).js", + "const version[ ]*=[ ]*\"(§§version§§)\";[\\s]*/\\*\\*[\\s]*\\* SSR utils for \\\\@vue/server-renderer", + "\\.__vue_app__=.{0,8000}?const [a-z]+=\"(§§version§§)\"," ] } }, @@ -3263,9 +3329,6 @@ "below": "0.7.0", "severity": "high", "identifiers": { - "CVE": [ - "CVE-XXXX-XXXX" - ], "bug": "SNYK-JS-ALASQL-1082932", "summary": "An arbitrary code execution exists as AlaSQL doesn't sanitize input when characters are placed between square brackets [] or preceded with a backtik (accent grave) ` character. Versions older that 0.7.0 were deprecated in March of 2021 and should no longer be used." }, @@ -3286,6 +3349,164 @@ ] } }, + "jquery.datatables": { + "vulnerabilities": [ + { + "below": "1.10.10", + "identifiers": { + "summary": "possible XSS" + }, + "info": [ + "https://github.com/DataTables/DataTables/commit/6f67df2d21f9858ec40a6e9565c3a653cdb691a6" + ] + }, + { + "below": "1.10.8", + "identifiers": { + "CVE": [ + "CVE-2015-6584" + ], + "summary": "XSS" + }, + "info": [ + "https://github.com/DataTables/DataTablesSrc/commit/ccf86dc5982bd8e16d", + "https://www.invicti.com/web-applications-advisories/cve-2015-6384-xss-vulnerability-identified-in-datatables/" + ] + } + ], + "extractors": { + "uri": [ + "/(§§version§§)/(js/)?jquery.dataTables(.min)?.js" + ], + "filename": [ + "jquery.dataTables-(§§version§§)(\\.min)?\\.js" + ], + "filecontent": [ + "http://www.datatables.net\n +DataTables (§§version§§)", + "/\\*! DataTables (§§version§§)", + "u.version=\"(§§version§§)\";u.settings=\\[\\];u.models=\\{\\};u.models.oSearch" + ] + } + }, + "nextjs": { + "vulnerabilities": [ + { + "atOrAbove": "10.0.0", + "below": "12.1.0", + "severity": "medium", + "identifiers": { + "summary": "Improper CSP in Image Optimization API", + "CVE": [ + "CVE-2022-23646" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-fmvm-x8mv-47mj" + ] + }, + { + "atOrAbove": "12.0.0", + "below": "12.0.9", + "severity": "medium", + "identifiers": { + "summary": "DOS Vulnerability for self-hosted next.js apps", + "CVE": [ + "CVE-2022-21721" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-wr66-vrwm-5g5x" + ] + }, + { + "below": "11.1.3", + "severity": "high", + "identifiers": { + "summary": "Unexpected server crash in Next.js versions", + "CVE": [ + "CVE-2021-43803" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-25mp-g6fv-mqxx" + ] + }, + { + "atOrAbove": "12.0.0", + "below": "12.0.5", + "severity": "high", + "identifiers": { + "summary": "Unexpected server crash in Next.js versions", + "CVE": [ + "CVE-2021-43803" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-25mp-g6fv-mqxx" + ] + }, + { + "atOrAbove": "10.0.0", + "below": "11.1.1", + "severity": "medium", + "identifiers": { + "summary": "XSS in Image Optimization API", + "CVE": [ + "CVE-2021-39178" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-9gr3-7897-pp7m" + ] + }, + { + "below": "11.1.0", + "severity": "medium", + "identifiers": { + "summary": "Open Redirect in Next.js", + "CVE": [ + "CVE-2021-37699" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-vxf5-wxwp-m7g9" + ] + }, + { + "atOrAbove": "9.5.0", + "below": "9.5.4", + "severity": "medium", + "identifiers": { + "summary": "Open Redirect in Next.js", + "CVE": [ + "CVE-2020-15242" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-x56p-c8cg-q435" + ] + }, + { + "below": "9.3.2", + "severity": "medium", + "identifiers": { + "summary": "Directory Traversal in Next.js", + "CVE": [ + "CVE-2020-5284" + ] + }, + "info": [ + "https://github.com/vercel/next.js/security/advisories/GHSA-fq77-7p7r-83rj" + ] + } + ], + "extractors": { + "filecontent": [ + "version=\"(§§version§§)\".{1,1500}document\\.getElementById\\(\"__NEXT_DATA__\"\\)\\.textContent", + "document\\.getElementById\\(\"__NEXT_DATA__\"\\)\\.textContent\\);window\\.__NEXT_DATA__=.;.\\.version=\"(§§version§§)\"" + ] + } + }, "dont check": { "extractors": { "uri": [ diff --git a/sfge/src/main/java/com/salesforce/CliMessagerAppender.java b/sfge/src/main/java/com/salesforce/CliMessagerAppender.java new file mode 100644 index 000000000..9fe793a34 --- /dev/null +++ b/sfge/src/main/java/com/salesforce/CliMessagerAppender.java @@ -0,0 +1,86 @@ +package com.salesforce; + +import com.salesforce.config.SfgeConfigProvider; +import com.salesforce.messaging.CliMessager; +import com.salesforce.messaging.EventKey; +import java.io.Serializable; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.layout.PatternLayout; + +/** + * Custom log4j2 appender to send logs as realtime events through {@link CliMessager}. This helps + * streamline logs displayed to commandline users. Invoked from log4j.xml. + */ +@Plugin( + name = "CliMessagerAppender", + category = Core.CATEGORY_NAME, + elementType = Appender.ELEMENT_TYPE) +public class CliMessagerAppender extends AbstractAppender { + + private final boolean shouldLogWarningsOnVerbose; + + @PluginFactory + public static CliMessagerAppender createAppender( + @PluginAttribute("name") String name, + @PluginElement("Layout") Layout layout, + @PluginElement("Filter") final Filter filter) { + if (name == null) { + // Assign default name to avoid complaining + name = "CliMessagerAppender"; + } + if (layout == null) { + layout = PatternLayout.createDefaultLayout(); + } + return new CliMessagerAppender(name, filter, layout, true); + } + + protected CliMessagerAppender( + String name, + Filter filter, + Layout layout, + final boolean ignoreExceptions) { + super(name, filter, layout, ignoreExceptions, null); + this.shouldLogWarningsOnVerbose = SfgeConfigProvider.get().shouldLogWarningsOnVerbose(); + } + + /** + * {@link CliMessagerAppender} decrements the log level while publishing to CLI. Warning is + * reduced to Info, Error is reduced to Warning, Fatal is reduced to Error. + * + * @param event that was published from code + */ + @Override + public void append(LogEvent event) { + Level level = event.getLevel(); + if (Level.WARN.equals(level) && this.shouldLogWarningsOnVerbose) { + CliMessager.postMessage( + "SFGE Warning as Info", EventKey.INFO_GENERAL, getEventMessage(event)); + } else if (Level.ERROR.equals(level)) { + CliMessager.postMessage( + "SFGE Error as Warning", EventKey.WARNING_GENERAL, getEventMessage(event)); + } else if (Level.FATAL.equals(level)) { + CliMessager.postMessage( + "SFGE Fatal as Error", EventKey.ERROR_GENERAL, getEventMessage(event)); + } else { + error( + String.format( + "Unable to log less than WARN level [{}]: {}", + event.getLevel(), + getEventMessage(event))); + } + } + + private String getEventMessage(LogEvent event) { + return event.getMessage().getFormattedMessage(); + } +} diff --git a/sfge/src/main/java/com/salesforce/Main.java b/sfge/src/main/java/com/salesforce/Main.java index aabe40ea9..4634f88d7 100644 --- a/sfge/src/main/java/com/salesforce/Main.java +++ b/sfge/src/main/java/com/salesforce/Main.java @@ -5,6 +5,7 @@ import com.salesforce.exception.SfgeException; import com.salesforce.exception.SfgeRuntimeException; import com.salesforce.exception.UnexpectedException; +import com.salesforce.exception.UserActionException; import com.salesforce.graph.ops.GraphUtil; import com.salesforce.messaging.CliMessager; import com.salesforce.metainfo.MetaInfoCollector; @@ -13,6 +14,7 @@ import com.salesforce.rules.RuleRunner; import com.salesforce.rules.RuleUtil; import com.salesforce.rules.Violation; +import com.salesforce.rules.ops.ProgressListenerProvider; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -135,12 +137,17 @@ private int execute(String... args) { System.err.println( "Unexpected exception while loading graph. See logs for more information."); return INTERNAL_ERROR; + } catch (UserActionException ex) { + LOGGER.error("User action expected: ", ex); + System.err.println(formatError(ex)); + return INTERNAL_ERROR; } // Run all of the rules. List allViolations; try { allViolations = new RuleRunner(g).runRules(eap.getSelectedRules(), eap.getTargets()); + ProgressListenerProvider.get().completedAnalysis(); } catch (SfgeRuntimeException ex) { LOGGER.error("Error while running rules", ex); System.err.println(formatError(ex)); @@ -167,6 +174,11 @@ private void collectMetaInfo(CliArgParser.ExecuteArgParser eap) { for (MetaInfoCollector collector : allCollectors) { collector.loadProjectFiles(eap.getProjectDirs()); + + // Let progress listener know about the meta information collected + ProgressListenerProvider.get() + .collectedMetaInfo( + collector.getMetaInfoTypeName(), collector.getMetaInfoCollected()); } } diff --git a/sfge/src/main/java/com/salesforce/apex/jorje/JorjeUtil.java b/sfge/src/main/java/com/salesforce/apex/jorje/JorjeUtil.java index 3442afd0b..d2704c808 100644 --- a/sfge/src/main/java/com/salesforce/apex/jorje/JorjeUtil.java +++ b/sfge/src/main/java/com/salesforce/apex/jorje/JorjeUtil.java @@ -1,6 +1,7 @@ package com.salesforce.apex.jorje; import apex.jorje.data.Locations; +import apex.jorje.parser.impl.BaseApexLexer; import apex.jorje.parser.impl.HiddenToken; import apex.jorje.semantic.ast.AstNode; import apex.jorje.semantic.ast.visitor.NoopScope; @@ -22,6 +23,8 @@ import java.util.Collections; import java.util.List; import java.util.NavigableMap; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.stream.Collectors; /** Converts string source code into a node that can be imported into the graph. */ @@ -29,6 +32,8 @@ public final class JorjeUtil { static { // Inform Jorje to track locations Locations.useIndexFactory(); + // Increment log level to avoid printing info lines + incrementLogLevel(); } public static AstNodeWrapper compileApexFromString(String sourceCode) { @@ -130,5 +135,11 @@ public static class JorjeCompilationException extends SfgeRuntimeException { } } + /** Increments log level of BaseApexLexer class from jorje jar to avoid printing info logs. */ + private static void incrementLogLevel() { + Logger jorjeLogger = Logger.getLogger(BaseApexLexer.class.getName()); + jorjeLogger.setLevel(Level.WARNING); + } + private JorjeUtil() {} } diff --git a/sfge/src/main/java/com/salesforce/config/EnvUtil.java b/sfge/src/main/java/com/salesforce/config/EnvUtil.java index b2b913675..afe45194f 100644 --- a/sfge/src/main/java/com/salesforce/config/EnvUtil.java +++ b/sfge/src/main/java/com/salesforce/config/EnvUtil.java @@ -9,6 +9,8 @@ public final class EnvUtil { private static final String ENV_RULE_ENABLE_WARNING_VIOLATION = "SFGE_RULE_ENABLE_WARNING_VIOLATION"; private static final String ENV_IGNORE_PARSE_ERRORS = "SFGE_IGNORE_PARSE_ERRORS"; + private static final String ENV_LOG_WARNINGS_ON_VERBOSE = "SFGE_LOG_WARNINGS_ON_VERBOSE"; + private static final String ENV_PROGRESS_INCREMENTS = "SFGE_PROGRESS_INCREMENTS"; // TODO: These should move to SfgeConfigImpl and this class should return Optionals @VisibleForTesting @@ -21,6 +23,8 @@ public final class EnvUtil { @VisibleForTesting static final boolean DEFAULT_RULE_ENABLE_WARNING_VIOLATION = true; @VisibleForTesting static final boolean DEFAULT_IGNORE_PARSE_ERRORS = false; + @VisibleForTesting static final boolean DEFAULT_LOG_WARNINGS_ON_VERBOSE = false; + @VisibleForTesting static final int DEFAULT_PROGRESS_INCREMENTS = 10; /** * Returns the value of the {@link #ENV_RULE_THREAD_COUNT} environment variable if set, else @@ -61,6 +65,27 @@ static boolean shouldIgnoreParseErrors() { return getBoolOrDefault(ENV_IGNORE_PARSE_ERRORS, DEFAULT_IGNORE_PARSE_ERRORS); } + /** + * Indicates if SFGE should log internal warnings on --verbose + * + * @return value of {@link #ENV_LOG_WARNINGS_ON_VERBOSE} env variable. If it is not set, {@link + * #DEFAULT_LOG_WARNINGS_ON_VERBOSE} + */ + static boolean shouldLogWarningsOnVerbose() { + return getBoolOrDefault(ENV_LOG_WARNINGS_ON_VERBOSE, DEFAULT_LOG_WARNINGS_ON_VERBOSE); + } + + /** + * Gets the level of increments at which path analysis progress update is provided. Applicable + * only with --verbose. + * + * @return value of {@link #ENV_PROGRESS_INCREMENTS} environment variable if set, else {@link + * #DEFAULT_PROGRESS_INCREMENTS} + */ + static int getProgressIncrements() { + return getIntOrDefault(ENV_PROGRESS_INCREMENTS, DEFAULT_PROGRESS_INCREMENTS); + } + private static int getIntOrDefault(String name, int defaultValue) { String strVal = System.getProperty(name); return strVal == null ? defaultValue : Integer.parseInt(strVal); diff --git a/sfge/src/main/java/com/salesforce/config/SfgeConfig.java b/sfge/src/main/java/com/salesforce/config/SfgeConfig.java index 1503614e9..9cbc3da47 100644 --- a/sfge/src/main/java/com/salesforce/config/SfgeConfig.java +++ b/sfge/src/main/java/com/salesforce/config/SfgeConfig.java @@ -16,4 +16,16 @@ public interface SfgeConfig { /** Indicates if a Jorje parse error causes the entire process to stop. */ boolean shouldIgnoreParseErrors(); + + /** + * Indicates if Warn level logs to log4j should be forwarded to CLI as well when verbose is + * enabled + */ + boolean shouldLogWarningsOnVerbose(); + + /** + * Should be used to set the level of increments at which path analysis progress update is + * provided + */ + int getProgressIncrements(); } diff --git a/sfge/src/main/java/com/salesforce/config/SfgeConfigImpl.java b/sfge/src/main/java/com/salesforce/config/SfgeConfigImpl.java index fd45a735e..fad94991f 100644 --- a/sfge/src/main/java/com/salesforce/config/SfgeConfigImpl.java +++ b/sfge/src/main/java/com/salesforce/config/SfgeConfigImpl.java @@ -25,6 +25,16 @@ public boolean shouldIgnoreParseErrors() { return EnvUtil.shouldIgnoreParseErrors(); } + @Override + public boolean shouldLogWarningsOnVerbose() { + return EnvUtil.shouldLogWarningsOnVerbose(); + } + + @Override + public int getProgressIncrements() { + return EnvUtil.getProgressIncrements(); + } + static SfgeConfigImpl getInstance() { return SfgeConfigImpl.LazyHolder.INSTANCE; } diff --git a/sfge/src/main/java/com/salesforce/config/UserFacingErrorMessages.java b/sfge/src/main/java/com/salesforce/config/UserFacingErrorMessages.java deleted file mode 100644 index 7d94730d4..000000000 --- a/sfge/src/main/java/com/salesforce/config/UserFacingErrorMessages.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.salesforce.config; - -/** - * Contains error message constants that will be displayed to users. TODO: move all other - * user-facing messages here. - */ -public final class UserFacingErrorMessages { - - /** UserActionException * */ - - // format: filename,defined type, line number - public static final String UNREACHABLE_CODE = - "Please remove unreachable code to proceed with analysis: %s,%s:%d"; -} diff --git a/sfge/src/main/java/com/salesforce/config/UserFacingMessages.java b/sfge/src/main/java/com/salesforce/config/UserFacingMessages.java new file mode 100644 index 000000000..ce152253f --- /dev/null +++ b/sfge/src/main/java/com/salesforce/config/UserFacingMessages.java @@ -0,0 +1,32 @@ +package com.salesforce.config; + +/** + * Contains error message constants that will be displayed to users. TODO: move all other + * user-facing messages here. + */ +public final class UserFacingMessages { + + /** UserActionException * */ + + // format: filename,defined type, line number + public static final String UNREACHABLE_CODE = + "Remove unreachable code to proceed with analysis: %s,%s:%d"; + + /** CRUD/FLS Violation messages * */ + // format: "CRUD" or "FLS", DML operation, Object type, Field information + public static final String VIOLATION_MESSAGE_TEMPLATE = + "%1$s validation is missing for [%2$s] operation on [%3$s]%4$s"; + + public static final String STRIP_INACCESSIBLE_READ_WARNING_TEMPLATE = + "For stripInaccessible checks on READ operation, " + + "SFGE doesn't have the capability to verify that only sanitized data is used after the check." + + "Please confirm that unsanitized data is discarded for [%2$s]"; + + public static final String UNRESOLVED_CRUD_FLS_TEMPLATE = + "SFGE couldn't resolve the parameter passed to [%2$s] operation%4$s. " + + "Please confirm that this operation has the necessary %1$s checks"; + + public static final String FIELDS_MESSAGE_TEMPLATE = " with field(s) [%s]"; + public static final String FIELD_HANDLING_NOTICE = + " - SFGE may not have parsed some objects/fields correctly. Please confirm that the objects/fields involved in these segments have FLS checks: [%s]"; +} diff --git a/sfge/src/main/java/com/salesforce/graph/build/MethodPathBuilderVisitor.java b/sfge/src/main/java/com/salesforce/graph/build/MethodPathBuilderVisitor.java index 247b811e5..b4f7ad05f 100644 --- a/sfge/src/main/java/com/salesforce/graph/build/MethodPathBuilderVisitor.java +++ b/sfge/src/main/java/com/salesforce/graph/build/MethodPathBuilderVisitor.java @@ -2,7 +2,7 @@ import com.google.common.collect.ImmutableSet; import com.salesforce.apex.jorje.ASTConstants.NodeType; -import com.salesforce.config.UserFacingErrorMessages; +import com.salesforce.config.UserFacingMessages; import com.salesforce.exception.TodoException; import com.salesforce.exception.UnexpectedException; import com.salesforce.exception.UserActionException; @@ -597,7 +597,7 @@ private void addEdge(String name, Vertex from, Vertex to) { // Ask user to fix unreachable code throw new UserActionException( String.format( - UserFacingErrorMessages.UNREACHABLE_CODE, + UserFacingMessages.UNREACHABLE_CODE, GremlinUtil.getFileName(g, to), to.value(Schema.DEFINING_TYPE), to.value(Schema.BEGIN_LINE))); diff --git a/sfge/src/main/java/com/salesforce/graph/ops/GraphUtil.java b/sfge/src/main/java/com/salesforce/graph/ops/GraphUtil.java index b6385ddfe..c414eb199 100644 --- a/sfge/src/main/java/com/salesforce/graph/ops/GraphUtil.java +++ b/sfge/src/main/java/com/salesforce/graph/ops/GraphUtil.java @@ -20,6 +20,8 @@ import com.salesforce.graph.vertex.BaseSFVertex; import com.salesforce.graph.vertex.SFVertexFactory; import com.salesforce.io.FileHandler; +import com.salesforce.rules.ops.ProgressListener; +import com.salesforce.rules.ops.ProgressListenerProvider; import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -154,8 +156,17 @@ public static void loadSourceFolders(GraphTraversalSource g, List source } } + final ProgressListener progressListener = ProgressListenerProvider.get(); + + // Let progress listener know that we've finished compiling all the files in project + progressListener.finishedFileCompilation(); + Util.Config config = Util.Config.Builder.get(g, comps).build(); + + // Let progress listener know what we are doing + progressListener.startedBuildingGraph(); Util.buildGraph(config); + progressListener.completedBuildingGraph(); } private static List buildFolderComps(String sourceFolder) @@ -182,6 +193,8 @@ private static List buildFolderComps(String sourceFo private static Optional loadFile(Path path) throws IOException { String pathString = path.toString(); + final ProgressListener progressListener = ProgressListenerProvider.get(); + if (!pathString.toLowerCase(Locale.ROOT).endsWith(".cls")) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skipping file. path=" + pathString); @@ -194,6 +207,8 @@ private static Optional loadFile(Path path) throws I } String sourceCode = FileHandler.getInstance().readTargetFile(pathString); AstNodeWrapper astNodeWrapper = JorjeUtil.compileApexFromString(sourceCode); + // Let progress listener know that we've compiled another file + progressListener.compiledAnotherFile(); return Optional.of(new Util.CompilationDescriptor(pathString, astNodeWrapper)); } catch (JorjeUtil.JorjeCompilationException ex) { if (SfgeConfigProvider.get().shouldIgnoreParseErrors()) { diff --git a/sfge/src/main/java/com/salesforce/graph/symbols/PathScopeVisitor.java b/sfge/src/main/java/com/salesforce/graph/symbols/PathScopeVisitor.java index 00e3d8b2e..15007bf5e 100644 --- a/sfge/src/main/java/com/salesforce/graph/symbols/PathScopeVisitor.java +++ b/sfge/src/main/java/com/salesforce/graph/symbols/PathScopeVisitor.java @@ -388,7 +388,7 @@ public Optional> getApexValue(VariableExpressionVertex var) { if (LOGGER.isWarnEnabled()) { LOGGER.warn( "TODO: PathScopeVisitor.getApexValue() can currently only support chains of length 2 or lower. keySequence=" - + keys); + + Arrays.toString(keys)); } return Optional.empty(); } @@ -600,7 +600,7 @@ public Optional getImplementingScope( if (LOGGER.isWarnEnabled()) { LOGGER.warn( "TODO: PathScopeVisitor.getApexValue() can currently only support chains of length 2 or lower. keySequence=" - + keys); + + Arrays.toString(keys)); } return Optional.empty(); } diff --git a/sfge/src/main/java/com/salesforce/graph/symbols/ScopeUtil.java b/sfge/src/main/java/com/salesforce/graph/symbols/ScopeUtil.java index 42a8cef79..f48c204a1 100644 --- a/sfge/src/main/java/com/salesforce/graph/symbols/ScopeUtil.java +++ b/sfge/src/main/java/com/salesforce/graph/symbols/ScopeUtil.java @@ -11,6 +11,7 @@ import com.salesforce.graph.vertex.LiteralExpressionVertex; import com.salesforce.graph.vertex.PrefixExpressionVertex; import com.salesforce.graph.vertex.VariableExpressionVertex; +import java.util.Arrays; import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -151,8 +152,8 @@ public static Optional> getDefaultPropertyValue( if (keys.length > 2) { if (LOGGER.isWarnEnabled()) { LOGGER.warn( - "TODO: PathScopeVisitor.getApexValue() can currently only support chains of length 2 or lower. keySequence=" - + keys); + "PathScopeVisitor.getApexValue() can currently only support chains of length 2 or lower. keySequence=" + + Arrays.toString(keys)); } } else if (result instanceof ObjectProperties) { final ObjectProperties objectProperties = (ObjectProperties) result; diff --git a/sfge/src/main/java/com/salesforce/graph/symbols/apex/ApexStringValue.java b/sfge/src/main/java/com/salesforce/graph/symbols/apex/ApexStringValue.java index 6771d8ad9..630d22f2e 100644 --- a/sfge/src/main/java/com/salesforce/graph/symbols/apex/ApexStringValue.java +++ b/sfge/src/main/java/com/salesforce/graph/symbols/apex/ApexStringValue.java @@ -46,6 +46,7 @@ public final class ApexStringValue extends ApexSimpleValue stringValue.replace("'", "\\'")), Pair.of(METHOD_NORMALIZE_SPACE, StringUtils::normalizeSpace), Pair.of(METHOD_TO_LOWER_CASE, StringUtils::toRootLowerCase), Pair.of(METHOD_TO_UPPER_CASE, StringUtils::toRootUpperCase), diff --git a/sfge/src/main/java/com/salesforce/graph/symbols/apex/system/SObjectAccessDecision.java b/sfge/src/main/java/com/salesforce/graph/symbols/apex/system/SObjectAccessDecision.java index d4404b277..f15de87c5 100644 --- a/sfge/src/main/java/com/salesforce/graph/symbols/apex/system/SObjectAccessDecision.java +++ b/sfge/src/main/java/com/salesforce/graph/symbols/apex/system/SObjectAccessDecision.java @@ -2,7 +2,6 @@ import com.salesforce.exception.UnexpectedException; import com.salesforce.exception.UnimplementedMethodException; -import com.salesforce.exception.UserActionException; import com.salesforce.graph.DeepCloneable; import com.salesforce.graph.ops.ApexStandardLibraryUtil; import com.salesforce.graph.ops.CloneUtil; @@ -171,14 +170,12 @@ private static AbstractSanitizableValue buildSanitizedValue( } else if (sanitizableValue instanceof ApexSoqlValue) { sanitizedValue = buildSanitizedValue(builder, (SoqlExpressionVertex) sanitizableValueVertex); - } else if (sanitizableValue instanceof ApexSingleValue) { + } else if (sanitizableValue instanceof ApexSingleValue + || sanitizableValue instanceof ApexCustomValue) { sanitizedValue = builder.declarationVertex(SyntheticTypedVertex.get("List")) .withStatus(ValueStatus.INDETERMINANT) .buildList(); - } else if (sanitizableValue instanceof ApexCustomValue) { - throw new UserActionException( - "Action needed: Do not use stripInaccessible() check on custom settings since custom settings expect only CRUD"); } else { throw new UnexpectedException( "ApexValue type not handled for stripInaccessible call: " + sanitizableValue); diff --git a/sfge/src/main/java/com/salesforce/metainfo/CustomSettingInfoCollector.java b/sfge/src/main/java/com/salesforce/metainfo/CustomSettingInfoCollector.java index 8231716c9..342e2a6f4 100644 --- a/sfge/src/main/java/com/salesforce/metainfo/CustomSettingInfoCollector.java +++ b/sfge/src/main/java/com/salesforce/metainfo/CustomSettingInfoCollector.java @@ -24,6 +24,7 @@ public class CustomSettingInfoCollector extends XmlMetaInfoCollector { private static final String CUSTOM_SETTING_PATTERN_STRING = "(.*)" + OBJECT_FILE_NAME_PATTERN; private static final Pattern CUSTOM_SETTING_PATTERN = Pattern.compile(CUSTOM_SETTING_PATTERN_STRING, Pattern.CASE_INSENSITIVE); + private static final String META_INFO_TYPE_NAME = "Custom Settings"; @Override HashSet getPathPatterns() { @@ -88,6 +89,11 @@ private Optional getCustomSettingName(Path path) { return Optional.empty(); } + @Override + public String getMetaInfoTypeName() { + return META_INFO_TYPE_NAME; + } + protected static final class LazyHolder { // Postpone initialization until first use. protected static final CustomSettingInfoCollector INSTANCE = diff --git a/sfge/src/main/java/com/salesforce/metainfo/MetaInfoCollector.java b/sfge/src/main/java/com/salesforce/metainfo/MetaInfoCollector.java index 5b214c2bc..7038087aa 100644 --- a/sfge/src/main/java/com/salesforce/metainfo/MetaInfoCollector.java +++ b/sfge/src/main/java/com/salesforce/metainfo/MetaInfoCollector.java @@ -19,6 +19,8 @@ public interface MetaInfoCollector { */ TreeSet getMetaInfoCollected(); + String getMetaInfoTypeName(); + /** Thrown when project files cannot be properly loaded/processed. */ final class MetaInfoLoadException extends SfgeRuntimeException { MetaInfoLoadException(String msg) { diff --git a/sfge/src/main/java/com/salesforce/metainfo/VisualForceHandlerImpl.java b/sfge/src/main/java/com/salesforce/metainfo/VisualForceHandlerImpl.java index 53def4dba..85c6b5e14 100644 --- a/sfge/src/main/java/com/salesforce/metainfo/VisualForceHandlerImpl.java +++ b/sfge/src/main/java/com/salesforce/metainfo/VisualForceHandlerImpl.java @@ -15,6 +15,7 @@ public class VisualForceHandlerImpl extends AbstractMetaInfoCollector { private static final Logger LOGGER = LogManager.getLogger(VisualForceHandlerImpl.class); + private static final String META_INFO_TYPE_NAME = "Apex Controllers"; @Override protected TreeSet getAcceptedExtensions() { @@ -213,6 +214,11 @@ private void addReferencedNames(String concatenatedNames) { } } + @Override + public String getMetaInfoTypeName() { + return META_INFO_TYPE_NAME; + } + protected static final class LazyHolder { // Postpone initialization until first use. protected static final VisualForceHandlerImpl INSTANCE = new VisualForceHandlerImpl(); diff --git a/sfge/src/main/java/com/salesforce/rules/AbstractRuleRunner.java b/sfge/src/main/java/com/salesforce/rules/AbstractRuleRunner.java index b97595a82..f1b773db9 100644 --- a/sfge/src/main/java/com/salesforce/rules/AbstractRuleRunner.java +++ b/sfge/src/main/java/com/salesforce/rules/AbstractRuleRunner.java @@ -6,6 +6,7 @@ import com.salesforce.graph.Schema; import com.salesforce.graph.build.CaseSafePropertyUtil.H; import com.salesforce.graph.vertex.MethodVertex; +import com.salesforce.rules.ops.ProgressListenerProvider; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -87,6 +88,9 @@ private Set runPathBasedRules( return new HashSet<>(); } + // Let listener know that we have finished identifying entry points in target + ProgressListenerProvider.get().pathEntryPointsIdentified(pathEntryPoints.size()); + // For each entry point, generate a submission object. List submissions = new ArrayList<>(); for (MethodVertex pathEntryPoint : pathEntryPoints) { diff --git a/sfge/src/main/java/com/salesforce/rules/PathBasedRuleRunner.java b/sfge/src/main/java/com/salesforce/rules/PathBasedRuleRunner.java index 84a1f7b18..0aa527108 100644 --- a/sfge/src/main/java/com/salesforce/rules/PathBasedRuleRunner.java +++ b/sfge/src/main/java/com/salesforce/rules/PathBasedRuleRunner.java @@ -9,7 +9,9 @@ import com.salesforce.graph.vertex.MethodVertex; import com.salesforce.graph.vertex.SFVertex; import com.salesforce.rules.fls.apex.operations.FlsViolationInfo; -import com.salesforce.rules.fls.apex.operations.FlsViolationUtils; +import com.salesforce.rules.fls.apex.operations.FlsViolationMessageUtil; +import com.salesforce.rules.ops.ProgressListener; +import com.salesforce.rules.ops.ProgressListenerProvider; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -28,12 +30,15 @@ public class PathBasedRuleRunner { /** Set that holds the violations found by this rule execution. */ private final Set violations; + private final ProgressListener progressListener; + public PathBasedRuleRunner( GraphTraversalSource g, List rules, MethodVertex methodVertex) { this.g = g; this.rules = rules; this.methodVertex = methodVertex; this.violations = new HashSet<>(); + this.progressListener = ProgressListenerProvider.get(); } /** @@ -65,6 +70,8 @@ public Set runRules() { } } + progressListener.finishedAnalyzingEntryPoint(paths, violations); + return violations; } @@ -125,10 +132,11 @@ private void convertToViolations(HashSet flsViolationInfos) { // Consolidate/regroup FLS violations across paths so that there are no // duplicates with different field sets for the same source/sink/dmlOperation final HashSet consolidatedFlsViolationInfos = - FlsViolationUtils.consolidateFlsViolations(flsViolationInfos); + FlsViolationMessageUtil.consolidateFlsViolations(flsViolationInfos); for (FlsViolationInfo flsViolationInfo : consolidatedFlsViolationInfos) { - final String violationMessage = FlsViolationUtils.constructMessage(flsViolationInfo); + final String violationMessage = + FlsViolationMessageUtil.constructMessage(flsViolationInfo); final Optional sourceVertex = flsViolationInfo.getSourceVertex(); final Optional sinkVertex = flsViolationInfo.getSinkVertex(); if (sinkVertex.isPresent()) { diff --git a/sfge/src/main/java/com/salesforce/rules/ThreadableRuleExecutor.java b/sfge/src/main/java/com/salesforce/rules/ThreadableRuleExecutor.java index 0ff506365..72724e1d5 100644 --- a/sfge/src/main/java/com/salesforce/rules/ThreadableRuleExecutor.java +++ b/sfge/src/main/java/com/salesforce/rules/ThreadableRuleExecutor.java @@ -5,6 +5,8 @@ import com.salesforce.graph.JustInTimeGraphProvider; import com.salesforce.graph.ops.LogUtil; import com.salesforce.graph.vertex.MethodVertex; +import com.salesforce.rules.ops.ProgressListener; +import com.salesforce.rules.ops.ProgressListenerProvider; import java.util.List; import java.util.Set; import java.util.Timer; @@ -78,8 +80,9 @@ private static int submitRunners( CompletionService> completionService, List submissions) { int submissionCount = 0; + final ProgressListener progressListener = ProgressListenerProvider.get(); for (ThreadableRuleSubmission submission : submissions) { - completionService.submit(new CallableExecutor(submission)); + completionService.submit(new CallableExecutor(submission, progressListener)); submissionCount += 1; } if (LOGGER.isInfoEnabled()) { @@ -113,7 +116,8 @@ private static class CallableExecutor implements Callable> { private final ThreadableRuleSubmission submission; private boolean timedOut; - public CallableExecutor(ThreadableRuleSubmission submission) { + public CallableExecutor( + ThreadableRuleSubmission submission, ProgressListener progressListener) { this.submission = submission; } diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsStripInaccessibleWarningInfo.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsStripInaccessibleWarningInfo.java index a67184927..c729a1099 100644 --- a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsStripInaccessibleWarningInfo.java +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsStripInaccessibleWarningInfo.java @@ -1,5 +1,6 @@ package com.salesforce.rules.fls.apex.operations; +import com.salesforce.config.UserFacingMessages; import java.util.TreeSet; /** @@ -22,4 +23,8 @@ public FlsStripInaccessibleWarningInfo( boolean isAllFields) { super(validationType, objectName, fields, isAllFields); } + + public String getMessageTemplate() { + return UserFacingMessages.STRIP_INACCESSIBLE_READ_WARNING_TEMPLATE; + } } diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsValidationCentral.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsValidationCentral.java index 9464c4339..f479de1cf 100644 --- a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsValidationCentral.java +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsValidationCentral.java @@ -4,7 +4,6 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.salesforce.config.SfgeConfigProvider; -import com.salesforce.exception.TodoException; import com.salesforce.exception.UnexpectedException; import com.salesforce.graph.symbols.ScopeUtil; import com.salesforce.graph.symbols.SymbolProvider; @@ -96,7 +95,8 @@ public void performStripInaccessibleValidationForRead( // Values we found in the MultiMap are the FlsValidationReps for Read operation // that have been sanitized by stripInaccessible() // 1. Create Warning violations - createWarningViolations(readValue, flsValidationRepresentationsForRead); + createStripInaccessibleWarningViolations( + readValue, flsValidationRepresentationsForRead); // 2. Remove the values from the map since they've been sanitized expectedReadValidations.removeAll(readValueWrapper); @@ -116,28 +116,14 @@ public void performStripInaccessibleValidationForRead( } /** Creates warning violations for stripInaccessible check on Read. */ - private void createWarningViolations( + private void createStripInaccessibleWarningViolations( ApexValue apexValue, Collection validationReps) { // If warnings are enabled, create stripInaccessible warnings if (IS_WARNING_VIOLATION_ENABLED) { - ChainedVertex vertex = - apexValue - .getValueVertex() - .orElse((ChainedVertex) apexValue.getInvocable().orElse(null)); - - if (vertex == null) { - throw new TodoException("No related vertex found for apex value: " + apexValue); - } - for (FlsValidationRepresentation validationRep : validationReps) { - for (FlsValidationRepresentation.Info validationInfo : - validationRep.getValidationInfo()) { - final FlsStripInaccessibleWarningInfo warningInfo = - FlsViolationUtils.getFlsStripInaccessibleWarningInfo(validationInfo); - - warningInfo.setSinkVertex(vertex); - violations.add(warningInfo); - } - } + final Set warningViolations = + FlsViolationCreatorUtil.createStripInaccessibleWarningViolations( + apexValue, validationReps); + violations.addAll(warningViolations); } } @@ -169,18 +155,31 @@ public void createExpectedValidations(DmlStatementVertex vertex, SymbolProvider final BaseSFVertex childVertex = dmlStatementVertexChildren.get(0); final ValidationConverter validationConverter = new ValidationConverter(validationType); - final Set validationReps; + final Set validationReps = new HashSet<>(); if (childVertex instanceof ChainedVertex) { final Optional> apexValue = ScopeUtil.resolveToApexValue(symbols, (ChainedVertex) childVertex); if (apexValue.isPresent()) { - validationReps = validationConverter.convertToExpectedValidations(apexValue.get()); + validationReps.addAll( + validationConverter.convertToExpectedValidations(apexValue.get())); } else { - throw new TodoException( - "Apex value not detected for dml's child vertex: " + childVertex); + if (LOGGER.isWarnEnabled()) { + LOGGER.warn( + "TODO: Apex value not detected for dml's child vertex: " + childVertex); + // TODO: add telemetry + } + violations.add( + FlsViolationCreatorUtil.createUnresolvedCrudFlsViolation( + validationType, vertex)); } } else { - throw new TodoException("Child vertex of DML is not a chained vertex: " + childVertex); + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("TODO: Child vertex of DML is not a chained vertex: " + childVertex); + // TODO: add telemetry + } + violations.add( + FlsViolationCreatorUtil.createUnresolvedCrudFlsViolation( + validationType, vertex)); } expectedValidations.addAll(validationReps); @@ -217,26 +216,39 @@ public void createExpectedValidations( ChainedVertex parameter = parameters.get(0); final ValidationConverter validationConverter = new ValidationConverter(validationType); - final ApexValue apexValue = - ScopeUtil.resolveToApexValue(symbols, parameter) - .orElseThrow( - () -> - new UnexpectedException( - "Database operation method has a child of unexpected type: " - + parameter)); - - // Add them to our set of expected validations - final Set validationReps = - validationConverter.convertToExpectedValidations(apexValue); - // Capture the vertex on which the operation is performed - // We'll need to capture accounts in: - // Database.insert(accounts); - // List accounts = Database.query('SELECT Name from Account'); - if (FlsValidationType.READ.equals(validationType)) { - addReadValidationReps(vertex, symbols, validationReps); + final Optional> apexValueOptional = + ScopeUtil.resolveToApexValue(symbols, parameter); + + if (!apexValueOptional.isPresent()) { + // TODO: add telemetry on missing parameter type that we need to handle in future + if (LOGGER.isWarnEnabled()) { + LOGGER.warn( + "Database operation method has a parameter of unexpected type: " + + parameter); + } + // Add a violation to let users know that SFGE cannot resolve the parameter in the DML + // operation + // and the onus of verifying its check is on them. + violations.add( + FlsViolationCreatorUtil.createUnresolvedCrudFlsViolation( + validationType, vertex)); + } else { - // for all other Database operations, use parameter passed as key - expectedValidations.addAll(validationReps); + final ApexValue apexValue = apexValueOptional.get(); + + // Add them to our set of expected validations + final Set validationReps = + validationConverter.convertToExpectedValidations(apexValue); + // Capture the vertex on which the operation is performed + // We'll need to capture accounts in: + // Database.insert(accounts); + // List accounts = Database.query('SELECT Name from Account'); + if (FlsValidationType.READ.equals(validationType)) { + addReadValidationReps(vertex, symbols, validationReps); + } else { + // for all other Database operations, use parameter passed as key + expectedValidations.addAll(validationReps); + } } } diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationCreatorUtil.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationCreatorUtil.java new file mode 100644 index 000000000..a5b2d04e3 --- /dev/null +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationCreatorUtil.java @@ -0,0 +1,112 @@ +package com.salesforce.rules.fls.apex.operations; + +import com.salesforce.collections.CollectionUtil; +import com.salesforce.exception.TodoException; +import com.salesforce.graph.ops.ApexValueUtil; +import com.salesforce.graph.symbols.apex.ApexValue; +import com.salesforce.graph.vertex.ChainedVertex; +import com.salesforce.graph.vertex.SFVertex; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +/** Handles creation of various types of {@link FlsViolationInfo} objects */ +public final class FlsViolationCreatorUtil { + + private FlsViolationCreatorUtil() {} + + static Set createStripInaccessibleWarningViolations( + ApexValue apexValue, Collection validationReps) { + final Set warningViolations = new HashSet<>(); + final ChainedVertex vertex = + apexValue + .getValueVertex() + .orElse((ChainedVertex) apexValue.getInvocable().orElse(null)); + + if (vertex == null) { + throw new TodoException("No related vertex found for apex value: " + apexValue); + } + for (FlsValidationRepresentation validationRep : validationReps) { + for (FlsValidationRepresentation.Info validationInfo : + validationRep.getValidationInfo()) { + final FlsStripInaccessibleWarningInfo warningInfo = + getFlsStripInaccessibleWarningInfo(validationInfo); + + warningInfo.setSinkVertex(vertex); + warningViolations.add(warningInfo); + } + } + return warningViolations; + } + + static FlsStripInaccessibleWarningInfo getFlsStripInaccessibleWarningInfo( + FlsValidationRepresentation.Info validationRepInfo) { + final String userFriendlyObjectName = + ApexValueUtil.deriveUserFriendlyDisplayName(validationRepInfo.getObjectValue()) + .orElse(validationRepInfo.getObjectName()); + final TreeSet combinedFieldNames = + combineFieldNamesAndValues( + validationRepInfo.getFieldNames(), + validationRepInfo.getFieldValues(), + validationRepInfo.getValidationType().analysisLevel); + + return new FlsStripInaccessibleWarningInfo( + validationRepInfo.getValidationType(), + userFriendlyObjectName, + combinedFieldNames, + validationRepInfo.isAllFields()); + } + + static FlsViolationInfo getFlsViolationInfo( + FlsValidationRepresentation.Info validationRepInfo, + Set missingFields, + Set> missingFieldValues) { + // Create a single set with both field names and field vertices. For field vertices, we use + // the name on the image. + final TreeSet combinedMissingFields = + combineFieldNamesAndValues( + missingFields, + missingFieldValues, + validationRepInfo.getValidationType().analysisLevel); + // Use object vertex image name when object vertex is available, else use object name. + final String userFriendlyObjectName = + ApexValueUtil.deriveUserFriendlyDisplayName(validationRepInfo.getObjectValue()) + .orElse(validationRepInfo.getObjectName()); + + final FlsViolationInfo flsViolationInfo = + new FlsViolationInfo( + validationRepInfo.getValidationType(), + userFriendlyObjectName, + combinedMissingFields, + validationRepInfo.isAllFields()); + + return flsViolationInfo; + } + + static FlsViolationInfo createUnresolvedCrudFlsViolation( + FlsConstants.FlsValidationType validationType, SFVertex sinkVertex) { + final FlsViolationInfo violationInfo = new UnresolvedCrudFlsViolationInfo(validationType); + violationInfo.setSinkVertex(sinkVertex); + + return violationInfo; + } + + private static TreeSet combineFieldNamesAndValues( + Set fieldNames, + Set> fieldValues, + FlsConstants.AnalysisLevel analysisLevel) { + final TreeSet combinedFieldNames = CollectionUtil.newTreeSet(); + + if (FlsConstants.AnalysisLevel.FIELD_LEVEL.equals(analysisLevel)) { + combinedFieldNames.addAll(fieldNames); + fieldValues.forEach( + fieldValue -> { + combinedFieldNames.add( + ApexValueUtil.deriveUserFriendlyDisplayName(fieldValue) + .orElse("UNKNOWN_FIELD")); + }); + } + return combinedFieldNames; + } +} diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationInfo.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationInfo.java index 14f6d2397..b220e070f 100644 --- a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationInfo.java +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationInfo.java @@ -2,6 +2,7 @@ import com.google.common.base.Objects; import com.salesforce.collections.CollectionUtil; +import com.salesforce.config.UserFacingMessages; import com.salesforce.graph.vertex.SFVertex; import com.salesforce.rules.AbstractRule; import com.salesforce.rules.RuleThrowable; @@ -118,6 +119,10 @@ public void setRule(AbstractRule rule) { this.rule = rule; } + public String getMessageTemplate() { + return UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtils.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationMessageUtil.java similarity index 63% rename from sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtils.java rename to sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationMessageUtil.java index 67dc0e1c8..a73414184 100644 --- a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtils.java +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/FlsViolationMessageUtil.java @@ -3,35 +3,23 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.salesforce.collections.CollectionUtil; +import com.salesforce.config.UserFacingMessages; import com.salesforce.exception.TodoException; import com.salesforce.exception.UnexpectedException; -import com.salesforce.graph.ops.ApexValueUtil; import com.salesforce.graph.ops.ObjectFieldUtil; import com.salesforce.graph.ops.SoqlParserUtil; -import com.salesforce.graph.symbols.apex.ApexValue; import com.salesforce.graph.vertex.MethodVertex; import com.salesforce.graph.vertex.SFVertex; import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; import java.util.stream.Collectors; /** Utils class that deals with FLS-specific violations */ -public final class FlsViolationUtils { - static final String VIOLATION_MESSAGE_TEMPLATE = - "%1$s validation is missing for [%2$s] operation on [%3$s]"; - private static final String FIELDS_MESSAGE_TEMPLATE = " with field(s) [%s]"; +public final class FlsViolationMessageUtil { private static final String FIELD_NAME_SEPARATOR = ","; - private static final String FIELD_HANDLING_NOTICE = - " - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [%s]"; - - private static final String STRIP_INACCESSIBLE_READ_WARNING_TEMPLATE = - "For stripInaccessible checks on READ operation, " - + "SFGE does not have the capability to verify that only sanitized data is used after the check." - + "Please ensure that unsanitized data is discarded for [%2$s]"; static final String ALL_FIELDS = "ALL_FIELDS"; @@ -55,7 +43,7 @@ private enum ViolationType { private static final Pattern RELATIONAL_PATTERN = Pattern.compile(RELATIONAL_PATTERN_STR, Pattern.CASE_INSENSITIVE); - private FlsViolationUtils() {} + private FlsViolationMessageUtil() {} /** * Consolidates a set of FlsViolationInfo to merge items that have the same source/sink/object @@ -93,27 +81,20 @@ static String constructMessageInternal(FlsViolationInfo flsViolationInfo) { final String fieldInformation = getFieldInformation(fieldNames, allFields, flsViolationInfo.getObjectName()); - final String validationInformation = - getValidationInformation(flsViolationInfo, violationType); - - // Return the full validation message - return validationInformation + fieldInformation; - } - private static String getValidationInformation( - FlsViolationInfo flsViolationInfo, ViolationType violationType) { - // TODO: consider using enums to choose message template when there's more than two options - final String messageTemplate = - flsViolationInfo instanceof FlsStripInaccessibleWarningInfo - ? STRIP_INACCESSIBLE_READ_WARNING_TEMPLATE - : VIOLATION_MESSAGE_TEMPLATE; + // Use the template that corresponds to the instance of flsViolationInfo. + // This is necessary since individual subtypes have different message templates. + final String messageTemplate = flsViolationInfo.getMessageTemplate(); final String validationInformation = String.format( messageTemplate, violationType, flsViolationInfo.getValidationType().name(), - flsViolationInfo.getObjectName()); + flsViolationInfo.getObjectName(), + fieldInformation); + + // Return the full validation message return validationInformation; } @@ -147,14 +128,15 @@ private static String getFieldInformation( // Populate field information only if we have anything if (!"".equals(fieldString)) { - fieldInformation = String.format(FIELDS_MESSAGE_TEMPLATE, fieldString); + fieldInformation = + String.format(UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, fieldString); } // Add field notice if we have segments that may not have been parsed correctly if (!complexSegments.isEmpty()) { fieldInformation += String.format( - FIELD_HANDLING_NOTICE, + UserFacingMessages.FIELD_HANDLING_NOTICE, Joiner.on(FIELD_NAME_SEPARATOR).join(complexSegments)); } return fieldInformation; @@ -228,66 +210,4 @@ private static boolean hasUnhandledSegment(String input) { return RELATIONAL_PATTERN.matcher(input).find() || SPECIAL_CHAR_PATTERN.matcher(input).find(); } - - static FlsStripInaccessibleWarningInfo getFlsStripInaccessibleWarningInfo( - FlsValidationRepresentation.Info validationRepInfo) { - final String userFriendlyObjectName = - ApexValueUtil.deriveUserFriendlyDisplayName(validationRepInfo.getObjectValue()) - .orElse(validationRepInfo.getObjectName()); - final TreeSet combinedFieldNames = - combineFieldNamesAndValues( - validationRepInfo.getFieldNames(), - validationRepInfo.getFieldValues(), - validationRepInfo.getValidationType().analysisLevel); - - return new FlsStripInaccessibleWarningInfo( - validationRepInfo.getValidationType(), - userFriendlyObjectName, - combinedFieldNames, - validationRepInfo.isAllFields()); - } - - static FlsViolationInfo getFlsViolationInfo( - FlsValidationRepresentation.Info validationRepInfo, - Set missingFields, - Set> missingFieldValues) { - // Create a single set with both field names and field vertices. For field vertices, we use - // the name on the image. - final TreeSet combinedMissingFields = - combineFieldNamesAndValues( - missingFields, - missingFieldValues, - validationRepInfo.getValidationType().analysisLevel); - // Use object vertex image name when object vertex is available, else use object name. - final String userFriendlyObjectName = - ApexValueUtil.deriveUserFriendlyDisplayName(validationRepInfo.getObjectValue()) - .orElse(validationRepInfo.getObjectName()); - - final FlsViolationInfo flsViolationInfo = - new FlsViolationInfo( - validationRepInfo.getValidationType(), - userFriendlyObjectName, - combinedMissingFields, - validationRepInfo.isAllFields()); - - return flsViolationInfo; - } - - private static TreeSet combineFieldNamesAndValues( - Set fieldNames, - Set> fieldValues, - FlsConstants.AnalysisLevel analysisLevel) { - final TreeSet combinedFieldNames = CollectionUtil.newTreeSet(); - - if (FlsConstants.AnalysisLevel.FIELD_LEVEL.equals(analysisLevel)) { - combinedFieldNames.addAll(fieldNames); - fieldValues.forEach( - fieldValue -> { - combinedFieldNames.add( - ApexValueUtil.deriveUserFriendlyDisplayName(fieldValue) - .orElse("UNKNOWN_FIELD")); - }); - } - return combinedFieldNames; - } } diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/UnresolvedCrudFlsViolationInfo.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/UnresolvedCrudFlsViolationInfo.java new file mode 100644 index 000000000..4c2f872d2 --- /dev/null +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/UnresolvedCrudFlsViolationInfo.java @@ -0,0 +1,19 @@ +package com.salesforce.rules.fls.apex.operations; + +import com.salesforce.collections.CollectionUtil; +import com.salesforce.config.UserFacingMessages; +import com.salesforce.graph.ops.SoqlParserUtil; + +/** + * FLS Violation when SFGE understands that a DML operation is happening, but it is unable to + * determine more information about the object and the fields involved. + */ +public class UnresolvedCrudFlsViolationInfo extends FlsViolationInfo { + public UnresolvedCrudFlsViolationInfo(FlsConstants.FlsValidationType validationType) { + super(validationType, SoqlParserUtil.UNKNOWN, CollectionUtil.newTreeSet(), false); + } + + public String getMessageTemplate() { + return UserFacingMessages.UNRESOLVED_CRUD_FLS_TEMPLATE; + } +} diff --git a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/ValidationVerifier.java b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/ValidationVerifier.java index bc5662c12..fe1ca0204 100644 --- a/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/ValidationVerifier.java +++ b/sfge/src/main/java/com/salesforce/rules/fls/apex/operations/ValidationVerifier.java @@ -75,7 +75,7 @@ && isFieldLevelValidation(expectedValidation) } return Optional.of( - FlsViolationUtils.getFlsViolationInfo( + FlsViolationCreatorUtil.getFlsViolationInfo( expectedValidation, missingFields, missingFieldVertices)); } diff --git a/sfge/src/main/java/com/salesforce/rules/ops/ProgressListener.java b/sfge/src/main/java/com/salesforce/rules/ops/ProgressListener.java new file mode 100644 index 000000000..2cfbd727a --- /dev/null +++ b/sfge/src/main/java/com/salesforce/rules/ops/ProgressListener.java @@ -0,0 +1,48 @@ +package com.salesforce.rules.ops; + +import com.salesforce.graph.ApexPath; +import com.salesforce.rules.Violation; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** Observer that listens to activities happening in SFGE while analyzing source code. */ +public interface ProgressListener { + /** + * Invoked when meta information about source code is collected + * + * @param metaInfoType type of information + * @param itemsCollected items that were collected + */ + void collectedMetaInfo(String metaInfoType, TreeSet itemsCollected); + + /** Invoked when a file is compiled by Jorje. */ + void compiledAnotherFile(); + + /** Invoked when compilation is completed for all files. */ + void finishedFileCompilation(); + + /** Invoked when SFGE starts building the graph with information found during compilation. */ + void startedBuildingGraph(); + + /** Invoked when SFGE completes building graph. */ + void completedBuildingGraph(); + + /** + * Invoked when entry points to analysis are identified. + * + * @param pathEntryPointsCount number of entry points identified. + */ + void pathEntryPointsIdentified(int pathEntryPointsCount); + + /** + * Invoked when all the paths originating from an entry point are analyzed. + * + * @param paths number of paths that originated from an entry point. + * @param violations number of violations detected while walking the identified paths. + */ + void finishedAnalyzingEntryPoint(List paths, Set violations); + + /** Invoked when analysis is finished. */ + void completedAnalysis(); +} diff --git a/sfge/src/main/java/com/salesforce/rules/ops/ProgressListenerImpl.java b/sfge/src/main/java/com/salesforce/rules/ops/ProgressListenerImpl.java new file mode 100644 index 000000000..a695113d1 --- /dev/null +++ b/sfge/src/main/java/com/salesforce/rules/ops/ProgressListenerImpl.java @@ -0,0 +1,153 @@ +package com.salesforce.rules.ops; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.salesforce.config.SfgeConfigProvider; +import com.salesforce.graph.ApexPath; +import com.salesforce.messaging.CliMessager; +import com.salesforce.messaging.EventKey; +import com.salesforce.rules.Violation; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** Publishes realtime information to CLI on the progress of analysis. */ +public class ProgressListenerImpl implements ProgressListener { + + @VisibleForTesting static final String NONE_FOUND = "none found"; + + private int filesCompiled = 0; + private int pathsDetected = 0; + private int lastPathCountReported = 0; + private int violationsDetected = 0; + private int entryPointsAnalyzed = 0; + private int totalEntryPoints = 0; + + private final int progressIncrements; + + static ProgressListener getInstance() { + return ProgressListenerImpl.LazyHolder.INSTANCE; + } + + private static final class LazyHolder { + private static final ProgressListenerImpl INSTANCE = new ProgressListenerImpl(); + } + + @VisibleForTesting + ProgressListenerImpl() { + progressIncrements = SfgeConfigProvider.get().getProgressIncrements(); + } + + @Override + public void collectedMetaInfo(String metaInfoType, TreeSet itemsCollected) { + final String items = stringify(itemsCollected); + CliMessager.postMessage( + "Meta information collected", + EventKey.INFO_META_INFO_COLLECTED, + metaInfoType, + items); + } + + @Override + public void compiledAnotherFile() { + filesCompiled++; + } + + @Override + public void finishedFileCompilation() { + CliMessager.postMessage( + "Finished compiling files", + EventKey.INFO_COMPLETED_FILE_COMPILATION, + String.valueOf(filesCompiled)); + } + + @Override + public void startedBuildingGraph() { + CliMessager.postMessage("Started building graph", EventKey.INFO_STARTED_BUILDING_GRAPH); + } + + @Override + public void completedBuildingGraph() { + CliMessager.postMessage("Finished building graph", EventKey.INFO_COMPLETED_BUILDING_GRAPH); + } + + @Override + public void pathEntryPointsIdentified(int pathEntryPointsCount) { + totalEntryPoints = pathEntryPointsCount; + CliMessager.postMessage( + "Path entry points identified", + EventKey.INFO_PATH_ENTRY_POINTS_IDENTIFIED, + String.valueOf(totalEntryPoints)); + } + + @Override + public void finishedAnalyzingEntryPoint(List paths, Set violations) { + pathsDetected += paths.size(); + violationsDetected += violations.size(); + entryPointsAnalyzed++; + + // Make a post only if we have more paths detected than the progress increments + // since the last time we posted. + if (pathsDetected - lastPathCountReported >= progressIncrements) { + CliMessager.postMessage( + "Count of violations in paths, entry points", + EventKey.INFO_PATH_ANALYSIS_PROGRESS, + String.valueOf(violationsDetected), + String.valueOf(pathsDetected), + String.valueOf(entryPointsAnalyzed), + String.valueOf(totalEntryPoints)); + + lastPathCountReported = pathsDetected; + } + } + + @Override + public void completedAnalysis() { + CliMessager.postMessage( + "Completed analysis stats", + EventKey.INFO_COMPLETED_PATH_ANALYSIS, + String.valueOf(pathsDetected), + String.valueOf(entryPointsAnalyzed), + String.valueOf(violationsDetected)); + } + + @VisibleForTesting + String stringify(Collection items) { + return (items.isEmpty()) ? NONE_FOUND : Joiner.on(',').join(items); + } + + @VisibleForTesting + void reset() { + filesCompiled = 0; + pathsDetected = 0; + lastPathCountReported = 0; + violationsDetected = 0; + entryPointsAnalyzed = 0; + } + + @VisibleForTesting + int getFilesCompiled() { + return filesCompiled; + } + + @VisibleForTesting + int getPathsDetected() { + return pathsDetected; + } + + @VisibleForTesting + int getLastPathCountReported() { + return lastPathCountReported; + } + + @VisibleForTesting + int getViolationsDetected() { + return violationsDetected; + } + + @VisibleForTesting + int getEntryPointsAnalyzed() { + return entryPointsAnalyzed; + } +} diff --git a/sfge/src/main/java/com/salesforce/rules/ops/ProgressListenerProvider.java b/sfge/src/main/java/com/salesforce/rules/ops/ProgressListenerProvider.java new file mode 100644 index 000000000..e50cd0868 --- /dev/null +++ b/sfge/src/main/java/com/salesforce/rules/ops/ProgressListenerProvider.java @@ -0,0 +1,17 @@ +package com.salesforce.rules.ops; + +import com.google.common.annotations.VisibleForTesting; + +/** Provides the singleton instance of {@link ProgressListener} in a thread-safe way. */ +public class ProgressListenerProvider { + @VisibleForTesting + static final ThreadLocal PROGRESS_LISTENER = + ThreadLocal.withInitial(() -> ProgressListenerImpl.getInstance()); + + /** Get the ProgressListener for the current thread */ + public static ProgressListener get() { + return PROGRESS_LISTENER.get(); + } + + private ProgressListenerProvider() {} +} diff --git a/sfge/src/main/resources/log4j2.xml b/sfge/src/main/resources/log4j2.xml index cd26f44bb..16650b917 100644 --- a/sfge/src/main/resources/log4j2.xml +++ b/sfge/src/main/resources/log4j2.xml @@ -1,7 +1,10 @@ - + + + + @@ -16,38 +19,36 @@ - + - + + + + - - - - - - + diff --git a/sfge/src/test/java/com/salesforce/SfgeTestExtension.java b/sfge/src/test/java/com/salesforce/SfgeTestExtension.java index 36cf3450c..de875ad2a 100644 --- a/sfge/src/test/java/com/salesforce/SfgeTestExtension.java +++ b/sfge/src/test/java/com/salesforce/SfgeTestExtension.java @@ -4,6 +4,7 @@ import com.salesforce.graph.TestMetadataInfo; import com.salesforce.graph.cache.VertexCacheTestProvider; import com.salesforce.metainfo.MetaInfoCollectorTestProvider; +import com.salesforce.rules.ops.ProgressListenerTestProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; @@ -31,6 +32,7 @@ public void beforeTestExecution(ExtensionContext context) { MetadataInfoTestProvider.initializeForTest(); VertexCacheTestProvider.initializeForTest(); MetaInfoCollectorTestProvider.initializeForTest(); + ProgressListenerTestProvider.initializeForTest(); } /** @@ -45,5 +47,6 @@ public void preDestroyTestInstance(ExtensionContext context) { MetadataInfoTestProvider.remove(); VertexCacheTestProvider.remove(); MetaInfoCollectorTestProvider.remove(); + ProgressListenerTestProvider.remove(); } } diff --git a/sfge/src/test/java/com/salesforce/config/SfgeConfigProviderTest.java b/sfge/src/test/java/com/salesforce/config/SfgeConfigProviderTest.java index 0afed7b93..189d20a92 100644 --- a/sfge/src/test/java/com/salesforce/config/SfgeConfigProviderTest.java +++ b/sfge/src/test/java/com/salesforce/config/SfgeConfigProviderTest.java @@ -41,6 +41,16 @@ public boolean isWarningViolationEnabled() { public boolean shouldIgnoreParseErrors() { return !EnvUtil.DEFAULT_IGNORE_PARSE_ERRORS; } + + @Override + public boolean shouldLogWarningsOnVerbose() { + return !EnvUtil.DEFAULT_LOG_WARNINGS_ON_VERBOSE; + } + + @Override + public int getProgressIncrements() { + return -1 * EnvUtil.DEFAULT_PROGRESS_INCREMENTS; + } }); final SfgeConfig sfgeConfig = SfgeConfigProvider.get(); @@ -57,6 +67,12 @@ public boolean shouldIgnoreParseErrors() { MatcherAssert.assertThat( sfgeConfig.shouldIgnoreParseErrors(), equalTo(!EnvUtil.DEFAULT_IGNORE_PARSE_ERRORS)); + MatcherAssert.assertThat( + sfgeConfig.shouldLogWarningsOnVerbose(), + equalTo(!EnvUtil.DEFAULT_LOG_WARNINGS_ON_VERBOSE)); + MatcherAssert.assertThat( + sfgeConfig.getProgressIncrements(), + equalTo(-1 * EnvUtil.getProgressIncrements())); } finally { SfgeConfigTestProvider.remove(); } @@ -80,5 +96,10 @@ private void assertDefaultImplementation(SfgeConfig sfgeConfig) { equalTo(EnvUtil.DEFAULT_RULE_ENABLE_WARNING_VIOLATION)); MatcherAssert.assertThat( sfgeConfig.shouldIgnoreParseErrors(), equalTo(EnvUtil.DEFAULT_IGNORE_PARSE_ERRORS)); + MatcherAssert.assertThat( + sfgeConfig.shouldLogWarningsOnVerbose(), + equalTo(EnvUtil.DEFAULT_LOG_WARNINGS_ON_VERBOSE)); + MatcherAssert.assertThat( + sfgeConfig.getProgressIncrements(), equalTo(EnvUtil.DEFAULT_PROGRESS_INCREMENTS)); } } diff --git a/sfge/src/test/java/com/salesforce/config/TestSfgeConfig.java b/sfge/src/test/java/com/salesforce/config/TestSfgeConfig.java index 13fe0adeb..c6eecb136 100644 --- a/sfge/src/test/java/com/salesforce/config/TestSfgeConfig.java +++ b/sfge/src/test/java/com/salesforce/config/TestSfgeConfig.java @@ -24,4 +24,14 @@ public boolean isWarningViolationEnabled() { public boolean shouldIgnoreParseErrors() { return SfgeConfigImpl.getInstance().shouldIgnoreParseErrors(); } + + @Override + public boolean shouldLogWarningsOnVerbose() { + return SfgeConfigImpl.getInstance().shouldLogWarningsOnVerbose(); + } + + @Override + public int getProgressIncrements() { + return SfgeConfigImpl.getInstance().getProgressIncrements(); + } } diff --git a/sfge/src/test/java/com/salesforce/graph/ops/expander/ApexPathExpanderTest.java b/sfge/src/test/java/com/salesforce/graph/ops/expander/ApexPathExpanderTest.java index 20719f3f1..617db60b8 100644 --- a/sfge/src/test/java/com/salesforce/graph/ops/expander/ApexPathExpanderTest.java +++ b/sfge/src/test/java/com/salesforce/graph/ops/expander/ApexPathExpanderTest.java @@ -598,4 +598,28 @@ public void testSwitchStatementIndeterminantValue() { TestRunnerListMatcher.hasValuesAnyOrder( "address or currency", "anytype", "unknown")); } + + @Test + public void testInlineResolvableTwoLevelMethodCall() { + String[] sourceCode = { + "public class MyClass {\n" + + " void doSomething() {\n" + + " System.debug(Factory.createInstance().secondLevel());\n" + + " }\n" + + "}\n", + "public class Factory {\n" + + " public static Factory createInstance() {\n" + + " return new Factory();\n" + + " }\n" + + " public String secondLevel() {\n" + + " return 'hello';\n" + + " }\n" + + "}\n" + }; + + List> results = + TestRunner.walkPaths(g, sourceCode); + MatcherAssert.assertThat(results, hasSize(equalTo(1))); + MatcherAssert.assertThat(results, TestRunnerListMatcher.hasValuesAnyOrder("hello")); + } } diff --git a/sfge/src/test/java/com/salesforce/graph/symbols/apex/ApexStringValueTest.java b/sfge/src/test/java/com/salesforce/graph/symbols/apex/ApexStringValueTest.java index eec4f173b..306c672fb 100644 --- a/sfge/src/test/java/com/salesforce/graph/symbols/apex/ApexStringValueTest.java +++ b/sfge/src/test/java/com/salesforce/graph/symbols/apex/ApexStringValueTest.java @@ -490,6 +490,7 @@ public static Stream stringReturnNoParameter() { Arguments.of(ApexStringValue.METHOD_ESCAPE_ECMA_SCRIPT), Arguments.of(ApexStringValue.METHOD_ESCAPE_HTML_4), Arguments.of(ApexStringValue.METHOD_ESCAPE_JAVA), + Arguments.of(ApexStringValue.METHOD_ESCAPE_SINGLE_QUOTES), Arguments.of(ApexStringValue.METHOD_NORMALIZE_SPACE), Arguments.of(ApexStringValue.METHOD_TO_LOWER_CASE), Arguments.of(ApexStringValue.METHOD_TO_UPPER_CASE), @@ -826,7 +827,15 @@ public static Stream testEscape() { Arguments.of( "escapeJava", "Company: \"Salesforce.com\"", - "Company: \\\"Salesforce.com\\\"")); + "Company: \\\"Salesforce.com\\\""), + Arguments.of( + "escapeSingleQuotes", + "\\'Salesforce.com\\'", // During assignment in apex, this would fit in as + // String s = '\'Salesforce.com\''; + "\\'Salesforce.com\\'") // Actual value printed should've undergone + // transformation such that it prints the escaped + // value + ); } @MethodSource diff --git a/sfge/src/test/java/com/salesforce/graph/symbols/apex/SObjectAccessDecisionTest.java b/sfge/src/test/java/com/salesforce/graph/symbols/apex/SObjectAccessDecisionTest.java index 45ad90dc9..e339bb315 100644 --- a/sfge/src/test/java/com/salesforce/graph/symbols/apex/SObjectAccessDecisionTest.java +++ b/sfge/src/test/java/com/salesforce/graph/symbols/apex/SObjectAccessDecisionTest.java @@ -58,6 +58,30 @@ public void testAccessDecisionValueCreation() { firstItem.getTypeVertex().get().getCanonicalType(), equalToIgnoringCase("Account")); } + @Test + public void testCustomSettings() { + String[] sourceCode = { + "public class MyClass {\n" + + " public static void doSomething() {\n" + + " MySettings__c ms = MySettings__c.getOrgDefaults();\n" + + " SObjectAccessDecision sd = Security.stripInaccessible(AccessType.UPDATABLE, ms);\n" + + " System.debug(sd.getRecords());\n" + + " }\n" + + "}\n" + }; + + TestRunner.Result result = TestRunner.walkPath(g, sourceCode); + SystemDebugAccumulator visitor = result.getVisitor(); + + final ApexListValue outputListValue = visitor.getSingletonResult(); + assertThat( + outputListValue.isSanitized( + MethodBasedSanitization.SanitizerMechanism.STRIP_INACCESSIBLE, + FlsConstants.StripInaccessibleAccessType.UPDATABLE), + equalTo(true)); + assertThat(outputListValue.isIndeterminant(), equalTo(true)); + } + @Test public void testAccessDecisionValueIncorrectAccessType() { String[] sourceCode = { diff --git a/sfge/src/test/java/com/salesforce/rules/fls/apex/ReadFlsScenariosTest.java b/sfge/src/test/java/com/salesforce/rules/fls/apex/ReadFlsScenariosTest.java index a2d5ba804..131ebb13d 100644 --- a/sfge/src/test/java/com/salesforce/rules/fls/apex/ReadFlsScenariosTest.java +++ b/sfge/src/test/java/com/salesforce/rules/fls/apex/ReadFlsScenariosTest.java @@ -4,7 +4,6 @@ import com.salesforce.rules.fls.apex.operations.FlsConstants; import com.salesforce.testutils.BaseFlsTest; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class ReadFlsScenariosTest extends BaseFlsTest { @@ -266,25 +265,6 @@ public void testUnsafeDatabaseQueryCall() { expect(4, FlsConstants.FlsValidationType.READ, "Contact").withField("FirstName")); } - @Test - @Disabled // TODO: handle Binary expressions with indeterminant parts - public void testUnsafeDatabaseQueryWithBinaryExpression() { - String sourceCode = - "public class MyClass {\n" - + " public void foo(String fields, String objectName) {\n" - + " List contacts = Database.query('SELECT ' + \n" - + "fields +\n" - + "'FROM ' + \n" - + "objectName);\n" - + " }\n" - + "}\n"; - - assertViolations( - rule, - sourceCode, - expect(3, FlsConstants.FlsValidationType.READ, "Contact").withField("FirstName")); - } - @Test public void testUnsafeDbQueryAsReturn() { String sourceCode = @@ -516,4 +496,23 @@ public void testComplexWhereClause() { .withField("contact_id__c") .withField("Name")); } + + @Test + public void testWithEscapeSingleQuotes() { + String sourceCode = + "public class MyClass {\n" + + " public void foo() {\n" + + " String queryStr = 'SELECT Name FROM Account';\n" + // TODO: this invocation is incorrect. The correct way would be: + // String.escapeSingleQuotes(queryStr); + // Created a new work item to track this issue. + + " Database.query(queryStr.escapeSingleQuotes());\n" + + " }\n" + + "}\n"; + + assertViolations( + rule, + sourceCode, + expect(4, FlsConstants.FlsValidationType.READ, "Account").withField("Name")); + } } diff --git a/sfge/src/test/java/com/salesforce/rules/fls/apex/StripInaccessibleCommonScenariosTest.java b/sfge/src/test/java/com/salesforce/rules/fls/apex/StripInaccessibleCommonScenariosTest.java index 7b645146a..4c296f321 100644 --- a/sfge/src/test/java/com/salesforce/rules/fls/apex/StripInaccessibleCommonScenariosTest.java +++ b/sfge/src/test/java/com/salesforce/rules/fls/apex/StripInaccessibleCommonScenariosTest.java @@ -1,9 +1,7 @@ package com.salesforce.rules.fls.apex; -import com.salesforce.exception.UserActionException; import com.salesforce.rules.ApexFlsViolationRule; import com.salesforce.testutils.BaseFlsTest; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,20 +30,4 @@ public void testValidCase() { assertNoViolation(rule, sourceCode); } - - @Test - public void testRejectCustomSettingFlsCheck() { - String[] sourceCode = { - "public class MyClass {\n" - + " public static void foo() {\n" - + " MySettings__c ms = MySettings__c.getOrgDefaults();\n" - + " SObjectAccessDecision sd = Security.stripInaccessible(AccessType.UPDATABLE, ms);\n" - + " update sd.getRecords();\n" - + " }\n" - + "}\n" - }; - - Assertions.assertThrows( - UserActionException.class, () -> assertNoViolation(rule, sourceCode)); - } } diff --git a/sfge/src/test/java/com/salesforce/rules/fls/apex/UnresolvedCrudFlsTest.java b/sfge/src/test/java/com/salesforce/rules/fls/apex/UnresolvedCrudFlsTest.java new file mode 100644 index 000000000..29038d326 --- /dev/null +++ b/sfge/src/test/java/com/salesforce/rules/fls/apex/UnresolvedCrudFlsTest.java @@ -0,0 +1,222 @@ +package com.salesforce.rules.fls.apex; + +import com.salesforce.rules.ApexFlsViolationRule; +import com.salesforce.rules.fls.apex.operations.FlsConstants; +import com.salesforce.testutils.BaseFlsTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class UnresolvedCrudFlsTest extends BaseFlsTest { + + @Test + public void testReadFirstLevelMethodCallValue() { + String sourceCode = + "public class MyClass {\n" + + " public void foo() {\n" + + " Database.query(getQuery());\n" + + " }\n" + + " String getQuery() {\n" + + " return 'SELECT Name from Account';" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(3, FlsConstants.FlsValidationType.READ, "Account").withField("Name")); + } + + @Test + public void testReadSecondLevelMethodCallValue() { + String[] sourceCode = { + "public class MyClass {\n" + + " public void foo() {\n" + + " Database.query(QueryBuilder.createQueryBuilder().setLimit(10).getQuery());\n" + + " }\n" + + "}\n", + "public class QueryBuilder {\n" + + " static QueryBuilder createQueryBuilder() {\n" + + " return new QueryBuilder();\n" + + " }\n" + + " QueryBuilder setLimit(Integer l) {\n" + + " return this;" + + " }\n" + + " String getQuery() {\n" + + " return 'SELECT Name from Account';" + + " }\n" + + "}\n" + }; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(3, FlsConstants.FlsValidationType.READ, "Account").withField("Name")); + } + + @Test + public void testInsertFirstLevelMethodCallValue() { + String sourceCode = + "public class MyClass {\n" + + " public void foo() {\n" + + " Database.insert(getAccounts());\n" + + " }\n" + + " String getAccounts() {\n" + + " List accounts = new List();\n" + + " accounts.add(new Account(Name = 'Acme Inc'));\n" + + " return accounts;" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(3, FlsConstants.FlsValidationType.INSERT, "Account").withField("Name")); + } + + @Test + public void testInsertSecondLevelMethodCallValue() { + String[] sourceCode = { + "public class MyClass {\n" + + " public void foo() {\n" + + " Database.insert(QueryBuilder.createQueryBuilder().getAccounts());\n" + + " }\n" + + "}\n", + "public class QueryBuilder {\n" + + " static QueryBuilder createQueryBuilder() {\n" + + " return new QueryBuilder();\n" + + " }\n" + + " String getAccounts() {\n" + + " List accounts = new List();\n" + + " accounts.add(new Account(Name = 'Acme Inc'));\n" + + " return accounts;" + + " }\n" + + "}\n" + }; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(3, FlsConstants.FlsValidationType.INSERT, "Account").withField("Name")); + } + + @EnumSource( + value = FlsConstants.FlsValidationType.class, + names = "MERGE", // Other than Merge operation, all methods take a parameter + mode = EnumSource.Mode.EXCLUDE) + @ParameterizedTest() + public void testUnresolvedMethodCallValue(FlsConstants.FlsValidationType validationType) { + String sourceCode = + "public class MyClass {\n" + + " public void foo() {\n" + + " " + + validationType.databaseOperationMethod + + "(MyBuilder.someMethod());\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expectUnresolvedCrudFls(3, validationType)); + } + + @Test + public void testReadWithUnresolvedBinaryExpression() { + String sourceCode = + "public class MyClass {\n" + + " public void foo(String fields, String objectName) {\n" + + " List contacts = Database.query('SELECT ' + \n" + + "fields +\n" + + "'FROM ' + \n" + + "objectName);\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expectUnresolvedCrudFls(3, FlsConstants.FlsValidationType.READ)); + } + + @Test + public void testDmlOnSecondLevelObject() { + String sourceCode = + "public class MyClass {\n" + + " public void foo(My_Obj__c myObj) {\n" + + " myObj.Custom_Field__c.Name = 'Acme Inc.';\n" + + " update myObj.Custom_Field__c;\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(4, FlsConstants.FlsValidationType.UPDATE, "Custom_Field__c")); + } + + @Test + public void testDmlOnReferenceObject() { + String sourceCode = + "public class MyClass {\n" + + " public void foo(My_Obj__c myObj) {\n" + + " myObj.Custom_Field__c.Reference__r.Name = 'Acme Inc.';\n" + + " update myObj.Custom_Field__c.Reference__r;\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expectUnresolvedCrudFls(4, FlsConstants.FlsValidationType.UPDATE)); + } + + @Test + public void testDmlOnMethodReturnValue() { + String sourceCode = + "public class MyClass {\n" + + " public void foo() {\n" + + " /* sfge-disable-next-line ApexFlsViolationRule */\n" + + " List accounts = [SELECT Id, Name FROM Account WHERE Type='something'];\n" + + " Map> listByType = new Map>();\n" + + " listByType.put('Account', accounts);\n" + + " delete listByType.get('Account');\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(7, FlsConstants.FlsValidationType.DELETE, "Account")); + } + + @Test + public void testDmlOnMethodReturnValueIndeterminant() { + String sourceCode = + "public class MyClass {\n" + + " public void foo(Map> listByType) {\n" + + " delete listByType.get('Account');\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(3, FlsConstants.FlsValidationType.DELETE, "SObject")); + } + + @Test + public void testDmlOnMethodInvokedbyMethod() { + String sourceCode = + "public class MyClass {\n" + + " public void foo(Map> listByType, Schema.SObjectType sObjectType) {\n" + + " delete listByType.get(sObjectType.getDescribe().getName());\n" + + " }\n" + + "}\n"; + + assertViolations( + ApexFlsViolationRule.getInstance(), + sourceCode, + expect(3, FlsConstants.FlsValidationType.DELETE, "SObject")); + } +} diff --git a/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsValidationRepresentationTest.java b/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsValidationRepresentationTest.java index 5c95276b2..fb7cc342a 100644 --- a/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsValidationRepresentationTest.java +++ b/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsValidationRepresentationTest.java @@ -49,7 +49,7 @@ public void testEqualityInMultiset() { } /** Tests for setting values */ - private static final String ALL_FIELDS = FlsViolationUtils.ALL_FIELDS; + private static final String ALL_FIELDS = FlsViolationMessageUtil.ALL_FIELDS; @Test public void testValidationWithFieldsShouldNotAllowObjectLevelType() { diff --git a/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtilsTest.java b/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtilsTest.java index 8d8e88e3d..63337b60f 100644 --- a/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtilsTest.java +++ b/sfge/src/test/java/com/salesforce/rules/fls/apex/operations/FlsViolationUtilsTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.equalToIgnoringCase; import com.salesforce.collections.CollectionUtil; +import com.salesforce.config.UserFacingMessages; import java.util.TreeSet; import org.junit.jupiter.api.Test; @@ -16,7 +17,7 @@ public class FlsViolationUtilsTest { @Test public void testMessageWithNoFields() { final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo( VALIDATION_TYPE, OBJECT_NAME, EMPTY_FIELD_LIST, false)); @@ -29,7 +30,7 @@ public void testMessageWithNoFields() { @Test public void testMessageWithAllFields() { final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, EMPTY_FIELD_LIST, true)); assertThat( @@ -42,7 +43,7 @@ public void testMessageWithAllFields() { public void testMessageWithSelectedFields() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, false)); assertThat( @@ -55,7 +56,7 @@ public void testMessageWithSelectedFields() { public void testMessageWithSelectedFieldsAndAllFieldsFlag() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, true)); assertThat( @@ -70,13 +71,23 @@ public void testMessageWithRelationalField() { CollectionUtil.newTreeSetOf( "Name", "Status__c", "Relational_Field__r.Another_field__c"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, false)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [My_Obj__c] with field(s) [Name,Status__c] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [Relational_Field__r.Another_field__c]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "My_Obj__c", + String.format( + UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, + "Name,Status__c")) + + String.format( + UserFacingMessages.FIELD_HANDLING_NOTICE, + "Relational_Field__r.Another_field__c"))); } @Test @@ -84,14 +95,24 @@ public void testMessageWithRelationalObject() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c"); final String relationalObjectName = "My_Relational_Obj__r"; final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo( VALIDATION_TYPE, relationalObjectName, fieldList, false)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [My_Relational_Obj__r] with field(s) [Name,Status__c] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [My_Relational_Obj__r]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "My_Relational_Obj__r", + String.format( + UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, + "Name,Status__c")) + + String.format( + UserFacingMessages.FIELD_HANDLING_NOTICE, + "My_Relational_Obj__r"))); } @Test @@ -99,13 +120,22 @@ public void testMessageWithIllegibleField() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c", "{1}{2}{3}"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, false)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [My_Obj__c] with field(s) [Name,Status__c] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [{1}{2}{3}]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "My_Obj__c", + String.format( + UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, + "Name,Status__c")) + + String.format( + UserFacingMessages.FIELD_HANDLING_NOTICE, "{1}{2}{3}"))); } @Test @@ -113,59 +143,92 @@ public void testMessageWithIllegibleFieldWithAllFields() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c", "{1}{2}{3}"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, true)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [My_Obj__c] with field(s) [ALL_FIELDS] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [{1}{2}{3}]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "My_Obj__c", + String.format( + UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, + "ALL_FIELDS")) + + String.format( + UserFacingMessages.FIELD_HANDLING_NOTICE, "{1}{2}{3}"))); } @Test public void testMessageWithOnlyIllegibleFields() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("{1}{2}{3}"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, false)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [My_Obj__c] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [{1}{2}{3}]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "My_Obj__c", + "") + + String.format( + UserFacingMessages.FIELD_HANDLING_NOTICE, "{1}{2}{3}"))); } @Test public void testMessageWithOnlyIllegibleFieldsWithAllFields() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("{1}{2}{3}"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, OBJECT_NAME, fieldList, true)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [My_Obj__c] with field(s) [ALL_FIELDS] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [{1}{2}{3}]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "My_Obj__c", + String.format( + UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, + "ALL_FIELDS")) + + String.format( + UserFacingMessages.FIELD_HANDLING_NOTICE, "{1}{2}{3}"))); } @Test public void testMessageWithIllegibleObject() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo(VALIDATION_TYPE, "{1}", fieldList, false)); assertThat( message, equalToIgnoringCase( - "FLS validation is missing for [UPDATE] operation on [{1}] with field(s) [Name,Status__c] - SFGE may not have parsed some objects/fields correctly. Please confirm if the objects/fields involved in these segments have FLS checks: [{1}]")); + String.format( + UserFacingMessages.VIOLATION_MESSAGE_TEMPLATE, + "FLS", + "UPDATE", + "{1}", + String.format( + UserFacingMessages.FIELDS_MESSAGE_TEMPLATE, + "Name,Status__c")) + + String.format(UserFacingMessages.FIELD_HANDLING_NOTICE, "{1}"))); } @Test public void testMessageWithValidCustomObject() { final TreeSet fieldList = CollectionUtil.newTreeSetOf("Name", "Status__c"); final String message = - FlsViolationUtils.constructMessageInternal( + FlsViolationMessageUtil.constructMessageInternal( new FlsViolationInfo( VALIDATION_TYPE, "namespace__Random_object__c", fieldList, false)); @@ -187,7 +250,7 @@ public void testFlsMessageIsIndependentOfFields() { FlsViolationInfo violationInfo = new FlsViolationInfo(validationType, objectName, fieldList, false); - final String message = FlsViolationUtils.constructMessage(violationInfo); + final String message = FlsViolationMessageUtil.constructMessage(violationInfo); assertThat( message, equalToIgnoringCase( @@ -208,7 +271,7 @@ public void testFlsMessageIsIndependentOfFields_allFields() { FlsViolationInfo violationInfo = new FlsViolationInfo(validationType, objectName, fieldList, allFields); - final String message = FlsViolationUtils.constructMessage(violationInfo); + final String message = FlsViolationMessageUtil.constructMessage(violationInfo); assertThat( message, equalToIgnoringCase( @@ -227,7 +290,7 @@ public void testCrudMessageIsIndependentOfFields() { FlsViolationInfo violationInfo = new FlsViolationInfo(validationType, objectName, fieldList, false); - final String message = FlsViolationUtils.constructMessage(violationInfo); + final String message = FlsViolationMessageUtil.constructMessage(violationInfo); assertThat( message, equalToIgnoringCase( @@ -248,7 +311,7 @@ public void testCrudMessageIsIndependentOfFields_allFields() { FlsViolationInfo violationInfo = new FlsViolationInfo(validationType, objectName, fieldList, allFields); - final String message = FlsViolationUtils.constructMessage(violationInfo); + final String message = FlsViolationMessageUtil.constructMessage(violationInfo); assertThat( message, equalToIgnoringCase( diff --git a/sfge/src/test/java/com/salesforce/rules/ops/ProgressListenerImplTest.java b/sfge/src/test/java/com/salesforce/rules/ops/ProgressListenerImplTest.java new file mode 100644 index 000000000..5f5dff5e9 --- /dev/null +++ b/sfge/src/test/java/com/salesforce/rules/ops/ProgressListenerImplTest.java @@ -0,0 +1,92 @@ +package com.salesforce.rules.ops; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.salesforce.config.SfgeConfigTestProvider; +import com.salesforce.config.TestSfgeConfig; +import com.salesforce.graph.ApexPath; +import com.salesforce.rules.Violation; +import com.salesforce.testutils.DummyVertex; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ProgressListenerImplTest { + + final ProgressListenerImpl progressListener; + + public ProgressListenerImplTest() { + SfgeConfigTestProvider.set( + new TestSfgeConfig() { + @Override + public int getProgressIncrements() { + return 3; + } + }); + + // Creating an instance directly to avoid conflicts with other tests + progressListener = new ProgressListenerImpl(); + } + + @BeforeEach + public void beforeEach() { + progressListener.reset(); + } + + @Test + public void testCompiledAnotherFile() { + progressListener.compiledAnotherFile(); + assertThat(progressListener.getFilesCompiled(), equalTo(1)); + } + + @Test + public void testFinishedAnalyzingEntryPoint() { + List paths = Lists.newArrayList(new ApexPath(null), new ApexPath(null)); + Set violations = + Sets.newHashSet( + new Violation.InternalErrorViolation("details", new DummyVertex("label"))); + + progressListener.finishedAnalyzingEntryPoint(paths, violations); + assertThat(progressListener.getPathsDetected(), equalTo(2)); + assertThat(progressListener.getViolationsDetected(), equalTo(1)); + assertThat(progressListener.getEntryPointsAnalyzed(), equalTo(1)); + assertThat(progressListener.getLastPathCountReported(), equalTo(0)); + } + + @Test + public void testFinishedAnalyzingEntryPoint_progressIncrement() { + List paths = Lists.newArrayList(new ApexPath(null), new ApexPath(null)); + Set violations = + Sets.newHashSet( + new Violation.InternalErrorViolation("details", new DummyVertex("label"))); + + progressListener.finishedAnalyzingEntryPoint(paths, violations); + assertThat(progressListener.getPathsDetected(), equalTo(2)); + assertThat(progressListener.getViolationsDetected(), equalTo(1)); + assertThat(progressListener.getEntryPointsAnalyzed(), equalTo(1)); + assertThat(progressListener.getLastPathCountReported(), equalTo(0)); + + progressListener.finishedAnalyzingEntryPoint(paths, violations); + assertThat(progressListener.getPathsDetected(), equalTo(4)); + assertThat(progressListener.getViolationsDetected(), equalTo(2)); + assertThat(progressListener.getEntryPointsAnalyzed(), equalTo(2)); + assertThat(progressListener.getLastPathCountReported(), equalTo(4)); + } + + @Test + public void testStringify() { + final List items = Lists.newArrayList("one", "two"); + assertThat(progressListener.stringify(items), equalTo("one,two")); + } + + @Test + public void testStringifyEmpty() { + final List items = new ArrayList<>(); + assertThat(progressListener.stringify(items), equalTo(ProgressListenerImpl.NONE_FOUND)); + } +} diff --git a/sfge/src/test/java/com/salesforce/rules/ops/ProgressListenerTestProvider.java b/sfge/src/test/java/com/salesforce/rules/ops/ProgressListenerTestProvider.java new file mode 100644 index 000000000..c378e58c3 --- /dev/null +++ b/sfge/src/test/java/com/salesforce/rules/ops/ProgressListenerTestProvider.java @@ -0,0 +1,26 @@ +package com.salesforce.rules.ops; + +import com.salesforce.SfgeTestExtension; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * Test implementation that overrides behavior of {@link ProgressListenerProvider}. Initializes a + * test with a unique {@link ProgressListener} implementation for the current thread. + */ +public class ProgressListenerTestProvider { + /** + * Create a new ProgressListenerImpl for the current thread. This is invoked from {@link + * SfgeTestExtension#beforeTestExecution(ExtensionContext)} for all tests. + */ + public static void initializeForTest() { + ProgressListenerProvider.PROGRESS_LISTENER.set(new TestProgressListenerImpl()); + } + + /** + * Remove the ProgressListener from the current thread. This is invoked from {@link + * SfgeTestExtension#preDestroyTestInstance(ExtensionContext)} for all tests. + */ + public static void remove() { + ProgressListenerProvider.PROGRESS_LISTENER.remove(); + } +} diff --git a/sfge/src/test/java/com/salesforce/rules/ops/TestProgressListenerImpl.java b/sfge/src/test/java/com/salesforce/rules/ops/TestProgressListenerImpl.java new file mode 100644 index 000000000..b0ca29ca5 --- /dev/null +++ b/sfge/src/test/java/com/salesforce/rules/ops/TestProgressListenerImpl.java @@ -0,0 +1,50 @@ +package com.salesforce.rules.ops; + +import com.salesforce.graph.ApexPath; +import com.salesforce.rules.Violation; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** Test implementation of {@link ProgressListener} to use as a non-singleton with tests */ +public class TestProgressListenerImpl implements ProgressListener { + @Override + public void collectedMetaInfo(String metaInfoType, TreeSet itemsCollected) { + // do nothing + } + + @Override + public void compiledAnotherFile() { + // do nothing + } + + @Override + public void finishedFileCompilation() { + // do nothing + } + + @Override + public void startedBuildingGraph() { + // do nothing + } + + @Override + public void completedBuildingGraph() { + // do nothing + } + + @Override + public void pathEntryPointsIdentified(int pathEntryPointsCount) { + // do nothing + } + + @Override + public void finishedAnalyzingEntryPoint(List paths, Set violations) { + // do nothing + } + + @Override + public void completedAnalysis() { + // do nothing + } +} diff --git a/sfge/src/test/java/com/salesforce/testutils/BaseFlsTest.java b/sfge/src/test/java/com/salesforce/testutils/BaseFlsTest.java index 4c92c3b8d..6dccf8956 100644 --- a/sfge/src/test/java/com/salesforce/testutils/BaseFlsTest.java +++ b/sfge/src/test/java/com/salesforce/testutils/BaseFlsTest.java @@ -177,7 +177,13 @@ protected ViolationWrapper.FlsViolationBuilder expect( protected ViolationWrapper.FlsViolationBuilder expectStripInaccWarning( int line, FlsConstants.FlsValidationType validationType, String objectName) { return ViolationWrapper.FlsViolationBuilder.get(line, validationType, objectName) - .forStripInaccWarning(); + .forViolationType(ViolationWrapper.FlsViolationType.STRIP_INACCESSIBLE_WARNING); + } + + protected ViolationWrapper.FlsViolationBuilder expectUnresolvedCrudFls( + int line, FlsConstants.FlsValidationType validationType) { + return ViolationWrapper.FlsViolationBuilder.get(line, validationType) + .forViolationType(ViolationWrapper.FlsViolationType.UNRESOLVED_CRUD_FLS); } protected ViolationWrapper.MessageBuilder expect(int line, String message) { diff --git a/sfge/src/test/java/com/salesforce/testutils/ViolationWrapper.java b/sfge/src/test/java/com/salesforce/testutils/ViolationWrapper.java index 915cad5cf..2984a6110 100644 --- a/sfge/src/test/java/com/salesforce/testutils/ViolationWrapper.java +++ b/sfge/src/test/java/com/salesforce/testutils/ViolationWrapper.java @@ -2,15 +2,48 @@ import com.google.common.base.Objects; import com.salesforce.collections.CollectionUtil; +import com.salesforce.graph.ops.SoqlParserUtil; import com.salesforce.rules.fls.apex.operations.FlsConstants; import com.salesforce.rules.fls.apex.operations.FlsStripInaccessibleWarningInfo; import com.salesforce.rules.fls.apex.operations.FlsViolationInfo; -import com.salesforce.rules.fls.apex.operations.FlsViolationUtils; +import com.salesforce.rules.fls.apex.operations.FlsViolationMessageUtil; +import com.salesforce.rules.fls.apex.operations.UnresolvedCrudFlsViolationInfo; import java.util.Arrays; import java.util.TreeSet; +import java.util.function.Function; /** Wrapper around Violation to help comparing only the violation message and line numbers */ public class ViolationWrapper { + public enum FlsViolationType { + STANDARD( + (builder) -> + new FlsViolationInfo( + builder.validationType, + builder.objectName, + builder.fieldNames, + builder.allFields)), + STRIP_INACCESSIBLE_WARNING( + (builder) -> + new FlsStripInaccessibleWarningInfo( + builder.validationType, + builder.objectName, + builder.fieldNames, + builder.allFields)), + UNRESOLVED_CRUD_FLS( + (builder) -> new UnresolvedCrudFlsViolationInfo(builder.validationType)); + + Function instanceSupplier; + + FlsViolationType( + Function instanceSupplier) { + this.instanceSupplier = instanceSupplier; + } + + FlsViolationInfo createInstance(FlsViolationBuilder builder) { + return this.instanceSupplier.apply(builder); + } + } + final int line; final String violationMsg; @@ -21,26 +54,9 @@ private ViolationWrapper(MessageBuilder builder) { private ViolationWrapper(FlsViolationBuilder builder) { this.line = builder.line; - // A more graceful way would've been to pass the builder to FlsViolationInfo. But since this - // is builder - // is available only in test code, it is not visible in production code. - // Also, moving the builder to production feels like an overkill since definingType, - // sourceLine, etc - // will not be available where FlsViolationInfo is initialized. - final FlsViolationInfo violationInfo = - builder.stripInaccWarning - ? new FlsStripInaccessibleWarningInfo( - builder.validationType, - builder.objectName, - builder.fieldNames, - builder.allFields) - : new FlsViolationInfo( - builder.validationType, - builder.objectName, - builder.fieldNames, - builder.allFields); - - this.violationMsg = FlsViolationUtils.constructMessage(violationInfo); + // Create new instance of FlsViolationInfo based on the type of violation + final FlsViolationInfo violationInfo = builder.violationType.createInstance(builder); + this.violationMsg = FlsViolationMessageUtil.constructMessage(violationInfo); } @Override @@ -80,7 +96,7 @@ public static class FlsViolationBuilder { private String definingType; private String definingMethod; - private boolean stripInaccWarning; + private FlsViolationType violationType; private FlsViolationBuilder( int line, FlsConstants.FlsValidationType validationType, String objectName) { @@ -89,7 +105,7 @@ private FlsViolationBuilder( this.objectName = objectName; this.fieldNames = CollectionUtil.newTreeSet(); this.allFields = false; - this.stripInaccWarning = false; + this.violationType = FlsViolationType.STANDARD; } public static FlsViolationBuilder get( @@ -97,6 +113,11 @@ public static FlsViolationBuilder get( return new FlsViolationBuilder(line, validationType, objectName); } + public static FlsViolationBuilder get( + int line, FlsConstants.FlsValidationType validationType) { + return get(line, validationType, SoqlParserUtil.UNKNOWN); + } + public FlsViolationBuilder withField(String field) { this.fieldNames.add(field); return this; @@ -132,8 +153,8 @@ public FlsViolationBuilder withDefiningMethod(String definingMethod) { return this; } - public FlsViolationBuilder forStripInaccWarning() { - this.stripInaccWarning = true; + public FlsViolationBuilder forViolationType(FlsViolationType violationType) { + this.violationType = violationType; return this; } diff --git a/src/Constants.ts b/src/Constants.ts index 2481a2b4a..e4538ae52 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -1,7 +1,7 @@ import os = require('os'); import path = require('path'); -export const PMD_VERSION = '6.47.0'; +export const PMD_VERSION = '6.48.0'; export const SFGE_VERSION = '1.0.1-pilot'; export const CATALOG_FILE = 'Catalog.json'; export const CUSTOM_PATHS_FILE = 'CustomPaths.json'; diff --git a/src/commands/scanner/rule/add.ts b/src/commands/scanner/rule/add.ts index b4e34515b..65ddee5e6 100644 --- a/src/commands/scanner/rule/add.ts +++ b/src/commands/scanner/rule/add.ts @@ -1,10 +1,11 @@ -import {flags, SfdxCommand} from '@salesforce/command'; +import {flags} from '@salesforce/command'; import {Messages, SfdxError} from '@salesforce/core'; import {AnyJson} from '@salesforce/ts-types'; import {Controller} from '../../../Controller'; import {stringArrayTypeGuard} from '../../../lib/util/Utils'; import path = require('path'); import untildify = require('untildify'); +import { ScannerCommand } from '../../../lib/ScannerCommand'; // Initialize Messages with the current plugin directory @@ -13,9 +14,8 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'add'); -const commonMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'common'); -export default class Add extends SfdxCommand { +export default class Add extends ScannerCommand { public static description = messages.getMessage('commandDescription'); public static longDescription = messages.getMessage('commandDescriptionLong'); @@ -39,9 +39,8 @@ export default class Add extends SfdxCommand { }) }; - public async run(): Promise { + async runInternal(): Promise { this.validateFlags(); - this.ux.styledHeader(commonMessages.getMessage('FEEDBACK_SURVEY_BANNER')); const language = this.flags.language as string; const paths = this.resolvePaths(); diff --git a/src/commands/scanner/rule/describe.ts b/src/commands/scanner/rule/describe.ts index c5dd74f6a..9dffe7fa0 100644 --- a/src/commands/scanner/rule/describe.ts +++ b/src/commands/scanner/rule/describe.ts @@ -12,7 +12,6 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'describe'); -const commonMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'common'); type DescribeStyledRule = Rule & { enabled: boolean; @@ -41,8 +40,7 @@ export default class Describe extends ScannerCommand { verbose: flags.builtin() }; - public async run(): Promise { - this.ux.styledHeader(commonMessages.getMessage('FEEDBACK_SURVEY_BANNER')); + async runInternal(): Promise { const ruleFilters = this.buildRuleFilters(); // It's possible for this line to throw an error, but that's fine because the error will be an SfdxError that we can // allow to boil over. diff --git a/src/commands/scanner/rule/list.ts b/src/commands/scanner/rule/list.ts index ba5406632..77ae897a9 100644 --- a/src/commands/scanner/rule/list.ts +++ b/src/commands/scanner/rule/list.ts @@ -13,7 +13,6 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'list'); -const commonMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'common'); const columns = [messages.getMessage('columnNames.name'), messages.getMessage('columnNames.languages'), messages.getMessage('columnNames.categories'), @@ -64,8 +63,7 @@ export default class List extends ScannerCommand { // END: Flags consumed by ScannerCommand#buildRuleFilters }; - public async run(): Promise { - this.ux.styledHeader(commonMessages.getMessage('FEEDBACK_SURVEY_BANNER')); + async runInternal(): Promise { const ruleFilters = this.buildRuleFilters(); // It's possible for this line to throw an error, but that's fine because the error will be an SfdxError that we can // allow to boil over. diff --git a/src/commands/scanner/rule/remove.ts b/src/commands/scanner/rule/remove.ts index f121782de..26bbe40a9 100644 --- a/src/commands/scanner/rule/remove.ts +++ b/src/commands/scanner/rule/remove.ts @@ -15,7 +15,6 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'remove'); -const commonMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'common'); export default class Remove extends ScannerCommand { // These determine what's displayed when the --help/-h flag is supplied. @@ -44,8 +43,7 @@ export default class Remove extends ScannerCommand { }) }; - public async run(): Promise { - this.ux.styledHeader(commonMessages.getMessage('FEEDBACK_SURVEY_BANNER')); + async runInternal(): Promise { // Step 1: Validate our input. this.validateFlags(); diff --git a/src/lib/DefaultRuleManager.ts b/src/lib/DefaultRuleManager.ts index b9ee1c31e..e4c706ce7 100644 --- a/src/lib/DefaultRuleManager.ts +++ b/src/lib/DefaultRuleManager.ts @@ -14,7 +14,7 @@ import {Controller} from '../Controller'; import globby = require('globby'); import path = require('path'); import {uxEvents, EVENTS} from './ScannerEvents'; -import {CUSTOM_CONFIG} from '../Constants'; +import {CUSTOM_CONFIG, ENGINE, CONFIG_PILOT_FILE} from '../Constants'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'DefaultRuleManager'); @@ -80,6 +80,13 @@ export class DefaultRuleManager implements RuleManager { const runDescriptorList: RunDescriptor[] = []; const engines: RuleEngine[] = await this.resolveEngineFilters(filters, engineOptions); const matchedTargets: Set = new Set(); + + // Storing the paths from eslint and eslint-lwc to track if any are processed by both. + const paths: Map = new Map([ + [ENGINE.ESLINT, [] as string[]], + [ENGINE.ESLINT_LWC, [] as string[]], + ]); + for (const e of engines) { // For each engine, filter for the appropriate groups and rules and targets, and pass // them all in. Note that some engines (pmd) need groups while others (eslint) need the rules. @@ -107,8 +114,27 @@ export class DefaultRuleManager implements RuleManager { this.logger.trace(`${e.getName()} is not eligible to execute this time.`); } + if (paths.has(e.getName())) { + for (const t of engineTargets) { + for (const p of t.paths) { + paths.get(e.getName()).push(p); + } + } + } } + // Checking if any file paths were processed by eslint and eslint-lwc, which may cause duplicate violations. + const pathsDoubleProcessed = paths.get(ENGINE.ESLINT).filter(path => paths.get(ENGINE.ESLINT_LWC).includes(path)); + if (pathsDoubleProcessed.length > 0) { + const numFilesShown = 3; + const filesToDisplay = pathsDoubleProcessed.slice(0, numFilesShown); + if (pathsDoubleProcessed.length > numFilesShown) { + filesToDisplay.push(`and ${pathsDoubleProcessed.length - numFilesShown} more`) + } + uxEvents.emit(EVENTS.WARNING_ALWAYS, messages.getMessage('warning.pathsDoubleProcessed', [`${Controller.getSfdxScannerPath()}/${CONFIG_PILOT_FILE}`, `${filesToDisplay.join(', ')}`])); + } + + this.validateRunDescriptors(runDescriptorList); await this.emitRunTelemetry(runDescriptorList, runOptions.sfdxVersion); // Warn the user if any positive targets were skipped diff --git a/src/lib/ScannerCommand.ts b/src/lib/ScannerCommand.ts index 664373831..73008d17f 100644 --- a/src/lib/ScannerCommand.ts +++ b/src/lib/ScannerCommand.ts @@ -2,9 +2,35 @@ import {SfdxCommand} from '@salesforce/command'; import {CategoryFilter, LanguageFilter, RuleFilter, RulesetFilter, RulenameFilter, EngineFilter} from './RuleFilter'; import {uxEvents, EVENTS} from './ScannerEvents'; import {stringArrayTypeGuard} from './util/Utils'; +import {AnyJson} from '@salesforce/ts-types'; + +import {Messages} from '@salesforce/core'; + +// Initialize Messages with the current plugin directory +Messages.importMessagesDirectory(__dirname); +const commonMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'common'); + export abstract class ScannerCommand extends SfdxCommand { + public async run(): Promise { + this.runCommonSteps(); + return await this.runInternal(); + } + + /** + * Command's should implement this method to add their + * working steps. + */ + abstract runInternal(): Promise; + + /** + * Common steps that should be run before every command + */ + protected runCommonSteps(): void { + this.ux.warn(commonMessages.getMessage('surveyRequestMessage')); + } + protected buildRuleFilters(): RuleFilter[] { const filters: RuleFilter[] = []; // Create a filter for any provided categories. @@ -60,6 +86,13 @@ export abstract class ScannerCommand extends SfdxCommand { this.ux.setSpinnerStatus(msg); } + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + protected waitOnSpinner(msg: string): void { + // msg variable is thrown away - please don't send anything here. + const currentStatus = this.ux.getSpinnerStatus(); + this.ux.setSpinnerStatus(currentStatus + ' .'); + } + protected stopSpinner(msg: string): void { this.ux.stopSpinner(msg); } @@ -78,6 +111,7 @@ export abstract class ScannerCommand extends SfdxCommand { uxEvents.on(EVENTS.ERROR_VERBOSE, (msg: string) => this.displayError(msg)); uxEvents.on(EVENTS.START_SPINNER, (msg: string, status: string) => this.startSpinner(msg, status)); uxEvents.on(EVENTS.UPDATE_SPINNER, (msg: string) => this.updateSpinner(msg)); + uxEvents.on(EVENTS.WAIT_ON_SPINNER, (msg: string) => this.waitOnSpinner(msg)); uxEvents.on(EVENTS.STOP_SPINNER, (msg: string) => this.stopSpinner(msg)); } } diff --git a/src/lib/ScannerEvents.ts b/src/lib/ScannerEvents.ts index 1ddb1355f..4d5bbd36d 100644 --- a/src/lib/ScannerEvents.ts +++ b/src/lib/ScannerEvents.ts @@ -14,5 +14,6 @@ export enum EVENTS { // and `ux.stopSpinner()`, which are both no-ops if there's not an active spinner. START_SPINNER = 'start-spinner', UPDATE_SPINNER = 'update-spinner', + WAIT_ON_SPINNER = 'wait-on-spinner', STOP_SPINNER = 'stop-spinner' } diff --git a/src/lib/ScannerRunCommand.ts b/src/lib/ScannerRunCommand.ts index 6cc092fee..40586d20f 100644 --- a/src/lib/ScannerRunCommand.ts +++ b/src/lib/ScannerRunCommand.ts @@ -14,16 +14,14 @@ Messages.importMessagesDirectory(__dirname); // Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core, // or any library that is using the messages framework can also be loaded this way. const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'run'); -const commonMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'common'); // This code is used for internal errors. export const INTERNAL_ERROR_CODE = 1; export abstract class ScannerRunCommand extends ScannerCommand { - public async run(): Promise { + async runInternal(): Promise { // First, do any validations that can't be handled with out-of-the-box stuff. await this.validateFlags(); - this.ux.styledHeader(commonMessages.getMessage('FEEDBACK_SURVEY_BANNER')); // If severity-threshold is used, that implicitly normalizes the severity. const normalizeSeverity: boolean = (this.flags['normalize-severity'] || this.flags['severity-threshold']) as boolean; diff --git a/src/lib/cpd/CpdEngine.ts b/src/lib/cpd/CpdEngine.ts index ac7c11b69..3cecab2b2 100644 --- a/src/lib/cpd/CpdEngine.ts +++ b/src/lib/cpd/CpdEngine.ts @@ -189,8 +189,8 @@ export class CpdEngine extends AbstractRuleEngine { if (xmlStart != -1 && xmlEnd != -1) { const cpdXml = stdout.slice(xmlStart, xmlEnd + cpdEnd.length); const cpdJson: Element = xml2js(cpdXml, {compact: false, ignoreDeclaration: true}) as Element; - - const duplications = cpdJson.elements[0].elements; + // Not all of the elements in this list will be duplication tags. We only want the ones that are. + const duplications = (cpdJson.elements[0].elements || []).filter(element => element.name === "duplication"); if (duplications) { ruleResults = this.jsonToRuleResults(duplications); } diff --git a/src/lib/eslint/JavascriptEslintStrategy.ts b/src/lib/eslint/JavascriptEslintStrategy.ts index 8673d4ff3..732132c38 100644 --- a/src/lib/eslint/JavascriptEslintStrategy.ts +++ b/src/lib/eslint/JavascriptEslintStrategy.ts @@ -9,15 +9,22 @@ import {ESLint} from 'eslint'; const ES_CONFIG: ESLint.Options = { "baseConfig": {}, "overrideConfig": { + "parser": "@babel/eslint-parser", "parserOptions": { - "sourceType": "module", - "ecmaVersion": 2018, + "requireConfigFile": false, + "babelOptions": { + "parserOpts": { + "plugins": ["classProperties", ["decorators", {"decoratorsBeforeExport": false}]] + } + } }, "ignorePatterns": [ "node_modules/!**" ] }, - "useEslintrc": false // Will not use an external config + "useEslintrc": false, // Will not use an external config + "resolvePluginsRelativeTo": __dirname, // Use the plugins found in the sfdx scanner installation directory + "cwd": __dirname // Use the parser found in the sfdx scanner installation }; export class JavascriptEslintStrategy implements EslintStrategy { diff --git a/src/lib/services/CommandLineSupport.ts b/src/lib/services/CommandLineSupport.ts index 4ad203958..63a912f71 100644 --- a/src/lib/services/CommandLineSupport.ts +++ b/src/lib/services/CommandLineSupport.ts @@ -77,9 +77,11 @@ export abstract class CommandLineSupport extends AsyncCreatable { // When data is passed back up to us, pop it onto the appropriate string. cp.stdout.on('data', data => { + this.outputProcessor.processRealtimeOutput(String(data)); stdout += data; }); cp.stderr.on('data', data => { + this.outputProcessor.processRealtimeOutput(String(data)); stderr += data; }); diff --git a/src/lib/services/OutputProcessor.ts b/src/lib/services/OutputProcessor.ts index bde7eba52..cf13229c2 100644 --- a/src/lib/services/OutputProcessor.ts +++ b/src/lib/services/OutputProcessor.ts @@ -1,13 +1,18 @@ import {Logger, LoggerLevel, Messages} from '@salesforce/core'; import {AsyncCreatable} from '@salesforce/kit'; import {RuleEvent} from '../../types'; -import {uxEvents} from '../ScannerEvents'; +import {EVENTS, uxEvents} from '../ScannerEvents'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'EventKeyTemplates'); const genericMessageKey = 'error.external.genericErrorMessage'; +const MESSAGE_START_TAG = 'SFDX-START'; +const MESSAGE_END_TAG = 'SFDX-END'; +const REALTIME_MESSAGE_START_TAG = 'SFCA-REALTIME-START'; +const REALTIME_MESSAGE_END_TAG = 'SFCA-REALTIME-END'; + /** * Helps with processing output from PmdCatalog java module and converting messages into usable events */ @@ -30,42 +35,64 @@ export class OutputProcessor extends AsyncCreatable { this.initialized = true; } + public isRealtimeOutput(out: string): boolean { + return out.startsWith(REALTIME_MESSAGE_START_TAG); + } + + public processOutput(out: string): boolean { + return this.processAllOutput(out, MESSAGE_START_TAG, MESSAGE_END_TAG); + } + + public processRealtimeOutput(out: string): boolean { + return this.processAllOutput(out, REALTIME_MESSAGE_START_TAG, REALTIME_MESSAGE_END_TAG); + } + // We want to find any events that were dumped into stdout or stderr and turn them back into events that can be thrown. // As per the convention outlined in SfdxMessager.java, SFDX-relevant messages will be stored in the outputs as JSONs - // sandwiched between 'SFDX-START' and 'SFDX-END'. So we'll find all instances of that. - public processOutput(out: string): void { + // sandwiched between a given start tag and end tag. So we'll find all instances of that. + private processAllOutput(out: string, startTag: string, endTag: string): boolean { this.logger.trace(`stdout: ${out}`); if (!out) { // Nothing to do here - return; + return false; } - const outEvents: RuleEvent[] = this.getEventsFromString(out); + const outEvents: RuleEvent[] = this.getEventsFromString(out, startTag, endTag); this.logger.trace(`Total count of events found: ${outEvents.length}`); - this.emitEvents(outEvents); + return this.emitEvents(outEvents); } // TODO: consider moving all message creation logic to a separate place and making this method private - public emitEvents(outEvents: RuleEvent[]): void { + public emitEvents(outEvents: RuleEvent[]): boolean { this.logger.trace('About to order and emit'); // If list is empty, we can just be done now. if (outEvents.length == 0) { - return; + return false; } // Iterate over all of the events and throw them as appropriate. outEvents.forEach((event) => { this.logEvent(event); - if (event.handler === 'UX' || (event.handler === 'INTERNAL' && event.type === 'ERROR')) { - const eventType = `${event.type.toLowerCase()}-${event.verbose ? 'verbose' : 'always'}`; - this.emitUxEvent(eventType, event.messageKey, event.args); + let eventType = ''; + if (event.handler === 'UX_SPINNER') { + eventType = EVENTS.UPDATE_SPINNER.toString(); + } else if (event.handler === 'UX' || (event.handler === 'INTERNAL' && event.type === 'ERROR')) { + eventType = `${event.type.toLowerCase()}-${event.verbose ? 'verbose' : 'always'}`; } + this.emitUxEvent(eventType, event.messageKey, event.args); }); + return true; } private emitUxEvent(eventType: string, messageKey: string, args: string[]): void { + if (eventType === '') { + this.logger.trace(`No event type requested for message ${messageKey}`); + return; + } + + this.logger.trace(`Sending new event of type ${eventType} and message ${messageKey}`); let constructedMessage: string = null; try { @@ -79,12 +106,12 @@ export class OutputProcessor extends AsyncCreatable { uxEvents.emit(eventType, constructedMessage); } - private getEventsFromString(str: string): RuleEvent[] { + private getEventsFromString(str: string, startTag: string, endTag: string): RuleEvent[] { const events: RuleEvent[] = []; - const regex = /SFDX-START(.*)SFDX-END/g; - const headerLength = 'SFDX-START'.length; - const tailLength = 'SFDX-END'.length; + const regex = new RegExp(`^${startTag}(.*)${endTag}`, 'g'); + const headerLength = startTag.length; + const tailLength = endTag.length; const regexMatch = str.match(regex); if (!regexMatch || regexMatch.length < 1) { this.logger.trace(`No events to log`); diff --git a/src/lib/sfge/SfgeWrapper.ts b/src/lib/sfge/SfgeWrapper.ts index 723516852..bf488d97c 100644 --- a/src/lib/sfge/SfgeWrapper.ts +++ b/src/lib/sfge/SfgeWrapper.ts @@ -63,12 +63,11 @@ class SfgeSpinnerManager extends AsyncCreatable implements SpinnerManager { } public startSpinner(): void { - uxEvents.emit(EVENTS.START_SPINNER, `Evaluating SFGE rules. See ${this.logFilePath} for logs`, "Please wait"); + uxEvents.emit(EVENTS.START_SPINNER, `Analyzing with SFGE. See ${this.logFilePath} for details.`, "Please wait"); - let intervalCount = 0; + // TODO: This timer logic should ideally live inside waitOnSpinner() this.intervalId = setInterval(() => { - uxEvents.emit(EVENTS.UPDATE_SPINNER, "Please wait." + ".".repeat(intervalCount)); - intervalCount += 1; + uxEvents.emit(EVENTS.WAIT_ON_SPINNER, 'message is unused'); }, 30000); } diff --git a/test/TestUtils.ts b/test/TestUtils.ts index 06da598c4..b50b3770d 100644 --- a/test/TestUtils.ts +++ b/test/TestUtils.ts @@ -1,7 +1,6 @@ import fs = require('fs'); import path = require('path'); -import { test, expect } from '@salesforce/command/lib/test'; -import * as CommonMessages from '../messages/common.js'; +import { test } from '@salesforce/command/lib/test'; import * as TestOverrides from './test-related-lib/TestOverrides'; import Sinon = require('sinon'); import LocalCatalog from '../src/lib/services/LocalCatalog'; @@ -16,12 +15,6 @@ export function prettyPrint(obj: any): string { return JSON.stringify(obj, null, 2); } -export function stripExtraneousOutput(stdout: string): string { - const splitOutput = stdout.split('\n'); - expect(splitOutput[0]).to.equal('=== ' + CommonMessages.FEEDBACK_SURVEY_BANNER); - return splitOutput.slice(1).join('\n'); -} - export function stubCatalogFixture(): void { // Make sure all catalogs exist where they're supposed to. if (!fs.existsSync(CATALOG_FIXTURE_PATH)) { diff --git a/test/commands/scanner/rule/describe.test.ts b/test/commands/scanner/rule/describe.test.ts index fc70f9d91..6e992dc3a 100644 --- a/test/commands/scanner/rule/describe.test.ts +++ b/test/commands/scanner/rule/describe.test.ts @@ -20,8 +20,8 @@ describe('scanner:rule:describe', () => { .it('--json flag yields correct results', ctx => { const ctxJson = JSON.parse(ctx.stdout); expect(ctxJson.result.length).to.equal(0, 'Should be no results'); - expect(ctxJson.warnings.length).to.equal(1, 'Should be one warning'); - expect(ctxJson.warnings[0]).to.equal(formattedWarning, 'Warning message should match'); + expect(ctxJson.warnings.length).to.equal(2, 'Incorrect warning count'); + expect(ctxJson.warnings[1]).to.equal(formattedWarning, 'Warning message should match'); }); }); diff --git a/test/commands/scanner/rule/list.test.ts b/test/commands/scanner/rule/list.test.ts index 973782dfd..3bfc3ef50 100644 --- a/test/commands/scanner/rule/list.test.ts +++ b/test/commands/scanner/rule/list.test.ts @@ -1,5 +1,5 @@ import {expect} from '@salesforce/command/lib/test'; -import {setupCommandTest, stripExtraneousOutput} from '../../../TestUtils'; +import {setupCommandTest} from '../../../TestUtils'; import {Rule} from '../../../../src/types'; import {CATALOG_FILE, ENGINE} from '../../../../src/Constants'; import fs = require('fs'); @@ -56,8 +56,7 @@ describe('scanner:rule:list', () => { // Split the output table by newline and throw out the first two rows, since they just contain header information. That // should leave us with the actual data. - const output = stripExtraneousOutput(ctx.stdout); - const rows = output.trim().split('\n'); + const rows = ctx.stdout.trim().split('\n'); rows.shift(); rows.shift(); expect(rows).to.have.lengthOf(totalRuleCount, 'All rules should have been returned'); @@ -307,8 +306,7 @@ describe('scanner:rule:list', () => { .command(['scanner:rule:list', '--category', 'Beebleborp']) .it('Without --json flag, an empty table is printed', ctx => { // Split the result by newline, and make sure there are two rows. - const output = stripExtraneousOutput(ctx.stdout); - const rows = output.trim().split('\n'); + const rows = ctx.stdout.trim().split('\n'); expect(rows).to.have.lengthOf(2, 'Only the header rows should have been printed'); }); diff --git a/test/commands/scanner/run.dfa.test.ts b/test/commands/scanner/run.dfa.test.ts new file mode 100644 index 000000000..e51826c8c --- /dev/null +++ b/test/commands/scanner/run.dfa.test.ts @@ -0,0 +1,95 @@ +import {expect} from '@salesforce/command/lib/test'; +import {setupCommandTest} from '../../TestUtils'; +import {Messages} from '@salesforce/core'; +import * as path from 'path'; +import Dfa from '../../../src/commands/scanner/run/dfa'; +import * as sinon from 'sinon'; + + +Messages.importMessagesDirectory(__dirname); +const sfgeMessages = Messages.loadMessages('@salesforce/sfdx-scanner', 'EventKeyTemplates'); + +const dfaTarget = path.join('test', 'code-fixtures', 'projects', 'sfge-smoke-app', 'src'); +const projectdir = path.join('test', 'code-fixtures', 'projects', 'sfge-smoke-app', 'src'); + +const apexControllerStr = 'UnsafeVfController'; +const customSettingsStr = 'none found'; +const fileCount = '7'; +const entryPointCount = '5'; +const pathCount = '6'; +const violationCount = '2'; + +const customSettingsMessage = sfgeMessages.getMessage('info.sfgeMetaInfoCollected', ['Custom Settings', customSettingsStr]); +const apexControllerMessage = sfgeMessages.getMessage('info.sfgeMetaInfoCollected', ['Apex Controllers', apexControllerStr]); +const compiledMessage = sfgeMessages.getMessage('info.sfgeFinishedCompilingFiles', [fileCount]); +const startGraphBuildMessage = sfgeMessages.getMessage('info.sfgeStartedBuildingGraph'); +const endGraphBuildMessage = sfgeMessages.getMessage('info.sfgeFinishedBuildingGraph'); +const identifiedEntryMessage = sfgeMessages.getMessage('info.sfgePathEntryPointsIdentified', [entryPointCount]); +const completedAnalysisMessage = sfgeMessages.getMessage('info.sfgeCompletedPathAnalysis', [pathCount, entryPointCount, violationCount]); + +function isSubstr(output: string, substring: string): boolean { + const updatedSubstr = substring.replace('[', '\\['); + const regex = new RegExp(`^${updatedSubstr}`, 'gm'); + const regexMatch = output.match(regex); + return regexMatch != null && regexMatch.length >= 1; +} + + +function verifyNotContains(output: string, substring: string): void { + expect(isSubstr(output, substring), `Output "${output}" should not contain substring "${substring}"`).is.false; +} + +describe('scanner:run:dfa', function () { + this.timeout(20000); // TODO why do we get timeouts at the default of 5000? What is so expensive here? + + describe('End to end', () => { + + describe('Output consistency', () => { + describe('run with --format json', () => { + setupCommandTest + .command(['scanner:run:dfa', + '--target', dfaTarget, + '--projectdir', projectdir, + '--format', 'json' + ]) + .it('provides only json in stdout', ctx => { + try { + JSON.parse(ctx.stdout); + } catch (error) { + expect.fail("dummy", "another dummy", "Invalid JSON output from --format json: " + ctx.stdout + ", error = " + error); + } + + }); + }); + }); + + describe('Progress output', () => { + let sandbox; + let spy: sinon.SinonSpy; + before(() => { + sandbox = sinon.createSandbox(); + spy = sandbox.spy(Dfa.prototype, "updateSpinner"); + }); + + after(() => { + spy.restore(); + sinon.restore(); + }); + + setupCommandTest + .command(['scanner:run:dfa', + '--target', dfaTarget, + '--projectdir', projectdir, + '--format', 'json' + ]) + .it('contains valid information when --verbose is not used', ctx => { + const output = ctx.stdout; + verifyNotContains(output, customSettingsMessage); + verifyNotContains(output, apexControllerMessage); + expect(spy.calledWith(compiledMessage, startGraphBuildMessage, endGraphBuildMessage, identifiedEntryMessage, completedAnalysisMessage)); + }); + + }); + + }); +}); \ No newline at end of file diff --git a/test/commands/scanner/run.severity.test.ts b/test/commands/scanner/run.severity.test.ts index 4e411acff..6c9384dc4 100644 --- a/test/commands/scanner/run.severity.test.ts +++ b/test/commands/scanner/run.severity.test.ts @@ -1,5 +1,5 @@ import {expect} from '@salesforce/command/lib/test'; -import {setupCommandTest, stripExtraneousOutput} from '../../TestUtils'; +import {setupCommandTest} from '../../TestUtils'; import {Messages} from '@salesforce/core'; import path = require('path'); @@ -31,12 +31,12 @@ describe('scanner:run', function () { '--severity-threshold', '1' ]) .it('When no violations are found equal to or greater than flag value, no error is thrown', ctx => { - const output = stripExtraneousOutput(ctx.stdout); - const outputJson = JSON.parse(output); + + const output = JSON.parse(ctx.stdout); // check that test file still has severities of 3 - for (let i=0; i { - const output = stripExtraneousOutput(ctx.stdout); - const outputJson = JSON.parse(output); + + const output = JSON.parse(ctx.stdout); // check that test file still has severities of 3 - for (let i=0; i { - const output = stripExtraneousOutput(ctx.stdout); - const outputJson = JSON.parse(output); - - for (let i=0; i { - const output = stripExtraneousOutput(ctx.stdout); - const outputJson = JSON.parse(output); + const output = JSON.parse(ctx.stdout); - for (let i=0; i { - const output = stripExtraneousOutput(ctx.stdout); - validateCsvOutput(output, false); + // Split the output by newline characters and throw away the first entry, so we're left with just the rows. + validateCsvOutput(ctx.stdout, false); }); setupCommandTest @@ -640,7 +640,6 @@ describe('scanner:run', function () { .command(['scanner:run', '--target', '**/*.js,**/*.cls', '--format', 'json']) .finally(() => process.chdir("../../../..")) .it('Polyglot project triggers pmd and eslint rules', ctx => { - expect(ctx.stderr, ctx.stdout).to.be.empty; const results = JSON.parse(ctx.stdout.substring(ctx.stdout.indexOf("[{"), ctx.stdout.lastIndexOf("}]") + 2)); // Look through all of the results and gather a set of unique engines const uniqueEngines = new Set(results.map(r => { return r.engine })); @@ -694,6 +693,22 @@ describe('scanner:run', function () { }); }); + describe('run with format --json', () => { + setupCommandTest + .command(['scanner:run', + '--target', path.join('test', 'code-fixtures', 'apex', 'AnotherTestClass.cls'), + '--format', 'json' + ]) + .it('provides only json in stdout', ctx => { + try { + JSON.parse(ctx.stdout); + } catch (error) { + expect.fail("Invalid JSON output from --format json: " + ctx.stdout, error); + } + + }); + }); + describe('Validation on custom config flags', () => { setupCommandTest .command(['scanner:run', diff --git a/test/lib/RuleManager.test.ts b/test/lib/RuleManager.test.ts index c805cd6a9..3e736d5ee 100644 --- a/test/lib/RuleManager.test.ts +++ b/test/lib/RuleManager.test.ts @@ -4,7 +4,7 @@ import {Lifecycle} from '@salesforce/core'; import {Controller} from '../../src/Controller'; import {Rule, RuleGroup, RuleTarget, TelemetryData} from '../../src/types'; -import {ENGINE} from '../../src/Constants'; +import {ENGINE, CONFIG_PILOT_FILE} from '../../src/Constants'; import {CategoryFilter, EngineFilter, LanguageFilter, RuleFilter, RulesetFilter} from '../../src/lib/RuleFilter'; import {DefaultRuleManager} from '../../src/lib/DefaultRuleManager'; @@ -22,6 +22,10 @@ import * as TestUtils from '../TestUtils'; import path = require('path'); import Sinon = require('sinon'); +import {Messages} from '@salesforce/core'; +Messages.importMessagesDirectory(__dirname); +const messages = Messages.loadMessages('@salesforce/sfdx-scanner', 'DefaultRuleManager'); + TestOverrides.initializeTestSetup(); let ruleManager: RuleManager = null; @@ -272,6 +276,35 @@ describe('RuleManager', () => { Sinon.assert.calledWith(uxSpy, EVENTS.WARNING_ALWAYS, `Target: '${invalidTarget.join(', ')}' was not processed by any engines.`); Sinon.assert.callCount(telemetrySpy, 1); }); + + it('Warns correctly if eslint and eslint-lwc have one duplicate path to process', async () => { + const targets = ['../invalid-lwc']; + const filters: EngineFilter[] = [new EngineFilter(["eslint-lwc", "eslint", "pmd"])]; + + await ruleManager.runRulesMatchingCriteria(filters, targets, runOptions, EMPTY_ENGINE_OPTIONS); + + const filename = path.join(__dirname, '..','code-fixtures', 'invalid-lwc', 'invalidApiDecorator', 'noLeadingUpperCase.js') + const warningMessage = messages.getMessage('warning.pathsDoubleProcessed', [`${Controller.getSfdxScannerPath()}/${CONFIG_PILOT_FILE}`, `${filename}`]) + + Sinon.assert.calledWith(uxSpy, EVENTS.WARNING_ALWAYS, warningMessage); + Sinon.assert.callCount(telemetrySpy, 1); + }); + + it('Warns correctly if eslint and eslint-lwc have one duplicate path to process', async () => { + const targets = ["js", "../invalid-lwc"]; + const filters: EngineFilter[] = [new EngineFilter(["eslint-lwc", "eslint", "pmd"])]; + + await ruleManager.runRulesMatchingCriteria(filters, targets, runOptions, EMPTY_ENGINE_OPTIONS); + + const baseConfigEnv = path.join(__dirname, '..','code-fixtures', 'projects', 'js', 'src', 'baseConfigEnv.js') + const fileThatUsesQUnit = path.join(__dirname, '..','code-fixtures', 'projects', 'js', 'src', 'fileThatUsesQUnit.js') + const simpleYetWrong = path.join(__dirname, '..','code-fixtures', 'projects', 'js', 'src', 'simpleYetWrong.js') + const warningMessage = messages.getMessage('warning.pathsDoubleProcessed', [`${Controller.getSfdxScannerPath()}/${CONFIG_PILOT_FILE}`, `${baseConfigEnv}, ${fileThatUsesQUnit}, ${simpleYetWrong}, and 1 more`]) + + Sinon.assert.calledWith(uxSpy, EVENTS.WARNING_ALWAYS, warningMessage); + Sinon.assert.callCount(telemetrySpy, 1); + }); + }); describe('Test Case: Run by category', () => { diff --git a/yarn.lock b/yarn.lock index 51150a5ac..0e89a1812 100644 --- a/yarn.lock +++ b/yarn.lock @@ -427,6 +427,40 @@ is-wsl "^2.1.1" tslib "^2.3.1" +"@oclif/core@^1.3.6", "@oclif/core@^1.6.4": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.14.0.tgz#b967b1d187d8118e6fffb0347d494ad458831b94" + integrity sha512-dqFIlxt9dBzoaQnm9mrGyEvIPs1/F1RRbftnlnyQoUF/UxKW2s5i6EXyzGomxaXLlDOA+tHlNimpVb2rKDTlHA== + dependencies: + "@oclif/linewrap" "^1.0.0" + "@oclif/screen" "^3.0.2" + ansi-escapes "^4.3.2" + ansi-styles "^4.3.0" + cardinal "^2.1.1" + chalk "^4.1.2" + clean-stack "^3.0.1" + cli-progress "^3.10.0" + debug "^4.3.4" + ejs "^3.1.6" + fs-extra "^9.1.0" + get-package-type "^0.1.0" + globby "^11.1.0" + hyperlinker "^1.0.0" + indent-string "^4.0.0" + is-wsl "^2.2.0" + js-yaml "^3.14.1" + natural-orderby "^2.0.3" + object-treeify "^1.1.33" + password-prompt "^1.1.2" + semver "^7.3.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + supports-color "^8.1.1" + supports-hyperlinks "^2.2.0" + tslib "^2.3.1" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + "@oclif/dev-cli@^1": version "1.26.10" resolved "https://registry.yarnpkg.com/@oclif/dev-cli/-/dev-cli-1.26.10.tgz#d8df3a79009b68552f5e7f249d1d19ca52278382" @@ -528,35 +562,38 @@ widest-line "^2.0.1" wrap-ansi "^4.0.0" -"@oclif/plugin-help@^3": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-3.3.1.tgz#36adb4e0173f741df409bb4b69036d24a53bfb24" - integrity sha512-QuSiseNRJygaqAdABYFWn/H1CwIZCp9zp/PLid6yXvy6VcQV7OenEFF5XuYaCvSARe2Tg9r8Jqls5+fw1A9CbQ== +"@oclif/plugin-help@^5": + version "5.1.12" + resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-5.1.12.tgz#24a18631eb9b22cb55e1a3b8e4f6039fd42727e6" + integrity sha512-HvH/RubJxqCinP0vUWQLTOboT+SfjfL8h40s+PymkWaldIcXlpoRaJX50vz+SjZIs7uewZwEk8fzLqpF/BWXlg== dependencies: - "@oclif/command" "^1.8.15" - "@oclif/config" "1.18.2" - "@oclif/errors" "1.3.5" - "@oclif/help" "^1.0.1" - chalk "^4.1.2" - indent-string "^4.0.0" - lodash "^4.17.21" - string-width "^4.2.0" - strip-ansi "^6.0.0" - widest-line "^3.1.0" - wrap-ansi "^6.2.0" + "@oclif/core" "^1.3.6" "@oclif/screen@^1.0.3", "@oclif/screen@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== -"@oclif/test@^1", "@oclif/test@^1.2.4": +"@oclif/screen@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-3.0.2.tgz#969054308fe98d130c02844a45cc792199b75670" + integrity sha512-S/SF/XYJeevwIgHFmVDAFRUvM3m+OjhvCAYMk78ZJQCYCQ5wS7j+LTt1ZEv2jpEEGg2tx/F6TYYWxddNAYHrFQ== + +"@oclif/test@^1.2.4": version "1.2.9" resolved "https://registry.yarnpkg.com/@oclif/test/-/test-1.2.9.tgz#bfd0bd3de6d2309f779b8f445a163db2fd48e72b" integrity sha512-op+ak0NTyeBKqjLVH1jfPCRGWK5befIoQpCL/xwekjucUEmMfCbUpV1Sa60f9rU8X58HEqrclwWbAH+DtQR6FQ== dependencies: fancy-test "^1.4.10" +"@oclif/test@^2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@oclif/test/-/test-2.1.1.tgz#140ed05ece651cdf427306b5c878826e273a1722" + integrity sha512-vuBDXnXbnU073xNUqj+V4Fbrp+6Xhs1sgeWUIMj69SP9qC3pUBPfEPzIoK00ga6/WxZ+2qUgqCBAOPGz3nmTEg== + dependencies: + "@oclif/core" "^1.6.4" + fancy-test "^2.0.0" + "@salesforce/bunyan@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@salesforce/bunyan/-/bunyan-2.0.0.tgz#8dbe377f2cf7d35348a23260416fee15adba5f97" @@ -609,14 +646,10 @@ semver "^7.3.5" ts-retry-promise "^0.6.0" -"@salesforce/dev-config@^1.5.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@salesforce/dev-config/-/dev-config-1.6.0.tgz#f2d2c279b998c6d837b05ee6ac086890f2e8ea84" - integrity sha512-YRwB8gsNgKUw+1SO61qqKDhp4ntMMur5V3Waa5w5cESJnJshe/kZfVSWlBki9dqYxIOl6iw1+8VEjv60wF0ATQ== - dependencies: - tslint "^5.18.0" - tslint-microsoft-contrib "^5.2.1" - typescript "~3.7.4" +"@salesforce/dev-config@^3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@salesforce/dev-config/-/dev-config-3.1.0.tgz#8eb5b35860ff60d1c1dc3fd9329b01a28475d5b9" + integrity sha512-cPph7ibj3DeSzWDFLcLtxOh5fmUlDUY2Ezq43n0V6auVP+l8orxRHjCExHS86SB3QKVgXkC8yYhryXiS8KF7Zw== "@salesforce/eslint-config-lwc@^3.2.1": version "3.2.4" @@ -663,17 +696,17 @@ dependencies: tslib "^2.2.0" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@>=5", "@sinonjs/fake-timers@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" + integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== dependencies: "@sinonjs/commons" "^1.7.0" @@ -701,10 +734,10 @@ array-from "^2.1.1" lodash "^4.17.15" -"@sinonjs/samsam@^5.3.1": - version "5.3.1" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" - integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== +"@sinonjs/samsam@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.1.1.tgz#627f7f4cbdb56e6419fa2c1a3e4751ce4f6a00b1" + integrity sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA== dependencies: "@sinonjs/commons" "^1.6.0" lodash.get "^4.4.2" @@ -715,6 +748,11 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -819,10 +857,10 @@ dependencies: "@types/node" "*" -"@types/mocha@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" - integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w== +"@types/mocha@^9": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== "@types/mustache@^4.0.1": version "4.1.2" @@ -834,10 +872,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== -"@types/node@^13.11.1": - version "13.13.52" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.52.tgz#03c13be70b9031baaed79481c0c0cfb0045e53f7" - integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ== +"@types/node@^17": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== "@types/normalize-path@^3.0.0": version "3.0.0" @@ -1012,7 +1050,7 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.1.1: +acorn-walk@^8.1.1, acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -1022,7 +1060,7 @@ acorn@^8.4.1, acorn@^8.7.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== -agent-base@6: +agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -1052,12 +1090,17 @@ ansi-colors@4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-escapes@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.3.0: +ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1086,7 +1129,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0, ansi-styles@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -1098,7 +1141,7 @@ ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= -anymatch@~3.1.1: +anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -1216,16 +1259,33 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +ast-types@^0.13.2: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + async@^3.2.0: version "3.2.3" resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1280,6 +1340,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -1321,10 +1388,10 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== caching-transform@^4.0.0: version "4.0.0" @@ -1390,7 +1457,7 @@ chai@^4: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1399,7 +1466,7 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1417,20 +1484,20 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.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" optionalDependencies: - fsevents "~2.3.1" + fsevents "~2.3.2" chownr@^1.1.1: version "1.1.4" @@ -1442,13 +1509,20 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -clean-stack@^3.0.0: +clean-stack@^3.0.0, clean-stack@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-3.0.1.tgz#155bf0b2221bf5f4fba89528d24c5953f17fe3a8" integrity sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg== dependencies: escape-string-regexp "4.0.0" +cli-progress@^3.10.0: + version "3.11.2" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.11.2.tgz#f8c89bd157e74f3f2c43bcfb3505670b4d48fc77" + integrity sha512-lCPoS6ncgX4+rJu5bS3F/iCz17kZ9MPZ6dpuTtI0KXKABkhyXIdYB3Inby1OpaGti3YlI3EeEkM9AuWpelJrVA== + dependencies: + string-width "^4.2.3" + cli-progress@^3.4.0: version "3.10.0" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.10.0.tgz#63fd9d6343c598c93542fdfa3563a8b59887d78a" @@ -1569,11 +1643,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colors@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -1586,7 +1655,7 @@ commander@2.5.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.5.1.tgz#23c61f6e47be143cc02e7ad4bb1c47f5cd5a2883" integrity sha1-I8Yfbke+FDzALnrUuxxH9c1aKIM= -commander@^2.12.1, commander@^2.9.0: +commander@^2.9.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -1710,6 +1779,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" + integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== + dayjs-plugin-utc@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz#48e552407024143922d6499a40f6c765f8c93d03" @@ -1720,20 +1794,13 @@ dayjs@^1.8.16: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.8.tgz#267df4bc6276fcb33c04a6735287e3f429abec41" integrity sha512-wbNwDfBHHur9UOzNUjeKUOJ0fCb0a52Wx0xInmQ7Y8FstyajiV1NmK1e00cxsr9YrE9r7yAChE0VvpuY5Rnlow== -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: +debug@4, debug@4.3.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - debug@^2.2.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1748,6 +1815,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -1770,7 +1844,7 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" -deep-is@^0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -1789,11 +1863,26 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +degenerator@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-3.0.2.tgz#6a61fcc42a702d6e50ff6023fe17bff435f68235" + integrity sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ== + dependencies: + ast-types "^0.13.2" + escodegen "^1.8.1" + esprima "^4.0.0" + vm2 "^3.9.8" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -1817,11 +1906,16 @@ diff@^3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@^4.0.1, diff@^4.0.2: +diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -1865,6 +1959,13 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" +ejs@^3.1.6: + version "3.1.8" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" + integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.76: version "1.4.80" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.80.tgz#299a1ea3e32810934b4e3c2e4d4cb53136fdab3f" @@ -1949,6 +2050,18 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escodegen@^1.8.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-import-resolver-node@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" @@ -2079,7 +2192,7 @@ espree@^9.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -2098,7 +2211,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -2170,6 +2283,20 @@ fancy-test@^1.4.10: nock "^13.0.0" stdout-stderr "^0.1.9" +fancy-test@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.0.tgz#f1477ae4190820318816914aabe273c0a0dbd597" + integrity sha512-SFb2D/VX9SV+wNYXO1IIh1wyxUC1GS0mYCFJOWD1ia7MPj9yE2G8jaPkw4t/pg0Sj7/YJP56OzMY4nAuJSOugQ== + dependencies: + "@types/chai" "*" + "@types/lodash" "*" + "@types/node" "*" + "@types/sinon" "*" + lodash "^4.17.13" + mock-stdin "^1.0.0" + nock "^13.0.0" + stdout-stderr "^0.1.9" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2191,7 +2318,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -2229,6 +2356,18 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-uri-to-path@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" + integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== + +filelist@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -2351,7 +2490,7 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^8.1: +fs-extra@^8.1, fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== @@ -2360,16 +2499,34 @@ fs-extra@^8.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@~2.3.1: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +ftp@^0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ== + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2429,6 +2586,18 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-uri@3: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" + integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== + dependencies: + "@tootallnate/once" "1" + data-uri-to-buffer "3" + debug "4" + file-uri-to-path "2" + fs-extra "^8.1.0" + ftp "^0.3.10" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2441,17 +2610,17 @@ github-slugger@^1.2.1: resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== -glob-parent@^5.1.2, glob-parent@^6.0.1, glob-parent@~5.1.0: +glob-parent@^5.1.2, glob-parent@^6.0.1, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@7.2.0, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2471,18 +2640,6 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - 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" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -2509,7 +2666,7 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^11.0.0, globby@^11.0.1, globby@^11.0.4: +globby@^11.0.0, globby@^11.0.1, globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -2625,11 +2782,31 @@ http-call@^5.1.2: parse-json "^4.0.0" tunnel-agent "^0.6.0" +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + http-parser-js@>=0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.6.tgz#2e02406ab2df8af8a7abfba62e0da01c62b95afd" integrity sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA== +http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2639,6 +2816,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +https-proxy-agent@5: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -2652,6 +2837,13 @@ hyperlinker@^1.0.0: resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -2693,7 +2885,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2712,6 +2904,16 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +ip@^1.1.5: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" + integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2859,6 +3061,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakref@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -2898,10 +3105,10 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isbinaryfile@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" - integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== +isbinaryfile@^5: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.0.tgz#034b7e54989dab8986598cbcea41f66663c65234" + integrity sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg== isexe@^2.0.0: version "2.0.0" @@ -2974,19 +3181,29 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -js-yaml@^3.13.1: +js-yaml@^3.13.1, js-yaml@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -2994,13 +3211,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - js2xmlparser@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.2.tgz#2a1fdf01e90585ef2ae872a01bc169c6a8d5e60a" @@ -3095,6 +3305,15 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonwebtoken@8.5.0: version "8.5.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#ebd0ca2a69797816e1c5af65b6c759787252947e" @@ -3163,6 +3382,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -3300,12 +3527,13 @@ lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^4.0.0" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" lolex@^2.4.2: version "2.7.5" @@ -3326,6 +3554,13 @@ loupe@^2.3.1: dependencies: get-func-name "^2.0.0" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3386,13 +3621,20 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: brace-expansion "^1.1.7" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -3408,7 +3650,7 @@ mkdirp@1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -3426,33 +3668,32 @@ mocha-junit-reporter@^2.0.0: strip-ansi "^6.0.1" xml "^1.0.0" -mocha@^8.1.1: - version "8.4.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff" - integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ== +mocha@^9: + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" + chokidar "3.5.3" + debug "4.3.3" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.6" + glob "7.2.0" growl "1.10.5" he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "4.2.1" ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" + nanoid "3.3.1" + serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" + workerpool "6.2.0" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -3504,17 +3745,17 @@ nan@^2.0.8: resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -natural-orderby@^2.0.1: +natural-orderby@^2.0.1, natural-orderby@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== @@ -3524,6 +3765,11 @@ ncp@~2.0.0: resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -3540,13 +3786,13 @@ nise@^1.3.3: lolex "^5.0.1" path-to-regexp "^1.7.0" -nise@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6" - integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA== +nise@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3" + integrity sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A== dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/commons" "^1.8.3" + "@sinonjs/fake-timers" ">=5" "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" path-to-regexp "^1.7.0" @@ -3663,7 +3909,7 @@ object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-treeify@^1.1.4: +object-treeify@^1.1.33, object-treeify@^1.1.4: version "1.1.33" resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== @@ -3701,6 +3947,18 @@ opn@^5.3.0: dependencies: is-wsl "^1.1.0" +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -3777,6 +4035,30 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz#b718f76475a6a5415c2efbe256c1c971c84f635e" + integrity sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + get-uri "3" + http-proxy-agent "^4.0.1" + https-proxy-agent "5" + pac-resolver "^5.0.0" + raw-body "^2.2.0" + socks-proxy-agent "5" + +pac-resolver@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-5.0.1.tgz#c91efa3a9af9f669104fa2f51102839d01cde8e7" + integrity sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q== + dependencies: + degenerator "^3.0.2" + ip "^1.1.5" + netmask "^2.0.2" + package-hash@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" @@ -3894,6 +4176,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + printj@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb" @@ -3923,6 +4210,25 @@ propagate@^2.0.0: resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== +proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-5.0.0.tgz#d31405c10d6e8431fde96cba7a0c027ce01d633b" + integrity sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g== + dependencies: + agent-base "^6.0.0" + debug "4" + http-proxy-agent "^4.0.0" + https-proxy-agent "^5.0.0" + lru-cache "^5.1.1" + pac-proxy-agent "^5.0.0" + proxy-from-env "^1.0.0" + socks-proxy-agent "^5.0.0" + +proxy-from-env@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -3977,6 +4283,16 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +raw-body@^2.2.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + read-installed@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" @@ -4001,6 +4317,16 @@ read-package-json@^2.0.0: normalize-package-data "^2.0.0" npm-normalize-package-bin "^1.0.0" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.1.0: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" @@ -4040,10 +4366,10 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" @@ -4124,7 +4450,7 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -4133,14 +4459,14 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.3.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -retire@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/retire/-/retire-2.2.5.tgz#a5d095d1803f31a0d73be5f6ebb59c3236349fa8" - integrity sha512-j4xoxGz/rse6ZZS5KKYwSXw/jB0bwgp4IFAkgqbMq+/G34EAxx2ttYJS5h3Uuzm/z4mGGMDJlFaR+CdwRv0w9A== +retire@^3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/retire/-/retire-3.0.7.tgz#1cf60b47060d0c95b8afcc0dc72a424ff0f2dd76" + integrity sha512-Qf5rcB+LUoo8A5y9hppigdNOGRolE/zEdqucq0OKQnFSX3aphHOYr9ustG4aW5oITjD4lowgTI+X1iQdZ5yseg== dependencies: - colors "^1.1.2" + ansi-colors "^4.1.1" commander "2.5.x" - https-proxy-agent "^5.0.0" + proxy-agent "^5.0.0" read-installed "^4.0.3" walkdir "0.4.1" @@ -4192,7 +4518,7 @@ safe-json-stringify@~1: resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -4207,7 +4533,7 @@ sax@>=0.6.0, sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -4224,15 +4550,22 @@ semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: dependencies: lru-cache "^6.0.0" +semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + sequin@*: version "0.1.1" resolved "https://registry.yarnpkg.com/sequin/-/sequin-0.1.1.tgz#5c2d389d66a383734eaafbc45edeb2c1cb1be701" integrity sha1-XC04nWajg3NOqvvEXt6ywcsb5wE= -serialize-javascript@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" @@ -4241,6 +4574,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -4296,6 +4634,18 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +sinon@^13: + version "13.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-13.0.2.tgz#c6a8ddd655dc1415bbdc5ebf0e5b287806850c3a" + integrity sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA== + dependencies: + "@sinonjs/commons" "^1.8.3" + "@sinonjs/fake-timers" "^9.1.2" + "@sinonjs/samsam" "^6.1.1" + diff "^5.0.0" + nise "^5.1.1" + supports-color "^7.2.0" + sinon@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-5.1.1.tgz#19c59810ffb733ea6e76a28b94a71fc4c2f523b8" @@ -4309,18 +4659,6 @@ sinon@^5.1.1: supports-color "^5.4.0" type-detect "^4.0.8" -sinon@^9.0.0: - version "9.2.4" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" - integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== - dependencies: - "@sinonjs/commons" "^1.8.1" - "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/samsam" "^5.3.1" - diff "^4.0.2" - nise "^4.0.4" - supports-color "^7.1.0" - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -4331,6 +4669,28 @@ slide@~1.1.3: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +socks-proxy-agent@5, socks-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== + dependencies: + agent-base "^6.0.2" + debug "4" + socks "^2.3.3" + +socks@^2.3.3: + version "2.7.0" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.0.tgz#f9225acdb841e874dca25f870e9130990f3913d0" + integrity sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + sort-keys@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" @@ -4343,7 +4703,7 @@ source-map@^0.5.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.1: +source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -4406,6 +4766,11 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + stdout-stderr@^0.1.9: version "0.1.13" resolved "https://registry.yarnpkg.com/stdout-stderr/-/stdout-stderr-0.1.13.tgz#54e3450f3d4c54086a49c0c7f8786a44d1844b6f" @@ -4414,7 +4779,7 @@ stdout-stderr@^0.1.9: debug "^4.1.1" strip-ansi "^6.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.1: +string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -4431,7 +4796,7 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4463,6 +4828,11 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4511,7 +4881,7 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1. resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@8.1.1, supports-color@^8.1.0: +supports-color@8.1.1, supports-color@^8.1.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -4525,7 +4895,7 @@ supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-co dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -4540,7 +4910,7 @@ supports-hyperlinks@^1.0.1: has-flag "^2.0.0" supports-color "^5.0.0" -supports-hyperlinks@^2.1.0: +supports-hyperlinks@^2.1.0, supports-hyperlinks@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== @@ -4619,6 +4989,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tough-cookie@*: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -4682,7 +5057,7 @@ tsconfig-paths@^3.12.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.3: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -4692,45 +5067,10 @@ tslib@^2, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.2.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslint-microsoft-contrib@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz#a6286839f800e2591d041ea2800c77487844ad81" - integrity sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA== - dependencies: - tsutils "^2.27.2 <2.29.0" - -tslint@^5.18.0: - version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" - integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.1" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.8.0" - tsutils "^2.29.0" - -"tsutils@^2.27.2 <2.29.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.28.0.tgz#6bd71e160828f9d019b6f4e844742228f85169a1" - integrity sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA== - dependencies: - tslib "^1.8.1" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" +tslib@^2.0.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== tsutils@^3.21.0: version "3.21.0" @@ -4765,6 +5105,13 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -4802,11 +5149,6 @@ typescript@^4.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== -typescript@~3.7.4: - version "3.7.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.7.tgz#c931733e2ec10dda56b855b379cc488a72a81199" - integrity sha512-MmQdgo/XenfZPvVLtKZOq9jQQvzaUAUpcKW8Z43x9B2fOm4S5g//tPtMweZUIP+SoBqrVPEIm+dJeQ9dfO0QdA== - unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" @@ -4822,6 +5164,16 @@ universalify@^0.1.0, universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -4876,6 +5228,14 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vm2@^3.9.8: + version "3.9.10" + resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.10.tgz#c66543096b5c44c8861a6465805c23c7cc996a44" + integrity sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ== + dependencies: + acorn "^8.7.0" + acorn-walk "^8.2.0" + walkdir@0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" @@ -4930,13 +5290,6 @@ which@~1.0.5: resolved "https://registry.yarnpkg.com/which/-/which-1.0.9.tgz#460c1da0f810103d0321a9b633af9e575e64486f" integrity sha1-RgwdoPgQED0DIam2M6+eV15kSG8= -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - widest-line@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" @@ -4956,15 +5309,15 @@ winreg@~1.2.2: resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b" integrity sha1-ugZWKbepJRMOFXeRCM9UCZDpjRs= -word-wrap@^1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== wrap-ansi@^4.0.0: version "4.0.0" @@ -5050,6 +5403,11 @@ xmlcreate@^2.0.4: resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be" integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg== +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -5060,6 +5418,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"