Repository: chef/automate
Pull request #4443 (2020-11-17 19:35:22)

Elasticsearch Publishers Full warning
### :nut_and_bolt: Description: What code changed, and why?

When all the Elasticsearch publishers are full the log message should be an error and not a warning.

### :chains: Related Resources

https://github.com/chef/automate/issues/3152

### :+1: Definition of Done

The log message when all the Elasticsearch publishers are full is an error, not a warning. 

### :white_check_mark: Checklist

- [x] I have read the [CONTRIBUTING document](https://github.com/chef/automate/blob/master/CONTRIBUTING.md).
- [ ] Tests added/updated?
- [ ] Docs added/updated?
- [x] All commits have been signed-off for the [Developer Certification of Origin](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco).


netlify[bot]: Deploy preview for *chef-automate* processing.

Building with commit 7824dc2a12e09b7a0a4f485995f806fb9119f6d1

https://app.netlify.com/sites/chef-automate/deploys/5fb425fbf744c10008a1844c

Patch:
diff --git a/.expeditor/export_semgrep_token.sh b/.expeditor/export_semgrep_token.sh
new file mode 100755
index 000000000..1d61686de
--- /dev/null
+++ b/.expeditor/export_semgrep_token.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -eou pipefail
+
+SEMGREP_TOKEN=$(vault kv get -field token secret/semgrep)
+SEMGREP_ID=$(vault kv get -field id secret/semgrep)
+
+export SEMGREP_TOKEN
+export SEMGREP_ID
\ No newline at end of file
diff --git a/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml
index e03fb5587..db2888c3b 100644
--- a/.expeditor/verify.pipeline.yml
+++ b/.expeditor/verify.pipeline.yml
@@ -61,37 +61,6 @@ steps:
             - HAB_STUDIO_SUP=false
             - HAB_NONINTERACTIVE=true
 
-  - label: ":semgrep: Custom"
-    expeditor:
-      executor:
-        docker:
-          image: returntocorp/semgrep:0.29.0
-          entrypoint: semgrep
-          command: [
-            "--error",
-            "--exclude", "*.spec.ts",
-            "--config", "/go/src/github.com/chef/automate/semgrep",
-            "/go/src/github.com/chef/automate"
-          ]
-
-  - label: ":semgrep: Published"
-    expeditor:
-      executor:
-        docker:
-          image: returntocorp/semgrep:latest
-          entrypoint: semgrep
-          command: [
-            "--error",
-            "--exclude", "third_party",
-            "--exclude", "*_test.go",
-            "--exclude", "*.pb.go",
-            "--exclude", "*.bindata.go",
-            "--exclude", "*.spec.ts",
-            "--timeout", "120",
-            "--config", "https://semgrep.dev/p/r2c-ci",
-            "/go/src/github.com/chef/automate"
-          ]
-
   #
   # Static & Unit tests
   #
diff --git a/.expeditor/verify_private.pipeline.yml b/.expeditor/verify_private.pipeline.yml
index 519fb90b6..eddba64bd 100644
--- a/.expeditor/verify_private.pipeline.yml
+++ b/.expeditor/verify_private.pipeline.yml
@@ -101,6 +101,42 @@ steps:
       executor:
         docker:
 
+  - label: ":semgrep: Custom"
+    expeditor:
+      executor:
+        docker:
+          image: returntocorp/semgrep:0.32.0
+          entrypoint: semgrep
+          command: [
+            "--error",
+            "--exclude", "*.spec.ts",
+            "--config", "/go/src/github.com/chef/automate/.semgrep",
+            "/go/src/github.com/chef/automate"
+          ]
+
+  - label: ":semgrep: Published"
+    command:
+    - echo "running in $(pwd)"
+    - export SEMGREP_JOB_URL=$BUILDKITE_BUILD_URL
+    - export SEMGREP_BRANCH=$BUILDKITE_BRANCH
+    - python -m semgrep_agent --publish-token "\$SEMGREP_TOKEN" --publish-deployment \$SEMGREP_ID
+    timeout_in_minutes: 20
+    expeditor:
+      secrets: true
+    plugins:
+      # Temporary workaround per @tduffield; do not propagate this solution too much!
+      - chef/step-hook#v0.1.1:
+          pre-command:
+            - .expeditor/export_semgrep_token.sh
+      - docker#v3.7.0:
+          image: returntocorp/semgrep-agent:v1
+          propogate-environment: true
+          workdir: /go/src/github.com/chef/automate
+          environment:
+            - SEMGREP_TOKEN
+            - SEMGREP_ID
+    soft_fail: true
+
   # Wait for the build to complete before starting anything below this
   # directive. All tests below this wait either require build assets
   # or take a long time.
diff --git a/semgrep/general.yaml b/.semgrep/general.yaml
similarity index 100%
rename from semgrep/general.yaml
rename to .semgrep/general.yaml
diff --git a/semgrep/rxjs-subscription.yaml b/.semgrep/rxjs-subscription.yaml
similarity index 100%
rename from semgrep/rxjs-subscription.yaml
rename to .semgrep/rxjs-subscription.yaml
diff --git a/.semgrep/rxjs-syntax.yaml b/.semgrep/rxjs-syntax.yaml
new file mode 100644
index 000000000..0dcef19c6
--- /dev/null
+++ b/.semgrep/rxjs-syntax.yaml
@@ -0,0 +1,24 @@
+rules:
+
+  - id: combineLatest-not-combining
+    pattern: combineLatest([$SINGLE_EXPRESSION])
+    fix:  $SINGLE_EXPRESSION
+    message: combineLatest is not needed with a single argument
+    languages: [ts]
+    severity: WARNING
+
+  - id: observable-not-ending-with-dollar-sign
+    patterns:
+    - pattern-either:
+      - pattern: $VAR = $EXPR.select(...);
+      - pattern: $VAR = $EXPR.pipe(...);
+      - pattern: $VAR = observableOf(...)
+      - pattern: $VAR = of(...)
+    - pattern-not: $VAR = d3.select(...);
+    - metavariable-regex:
+        metavariable: '$VAR'
+        regex: '[^$]+(?!\$)$'
+    message: |
+      Observable variable ($VAR) should end with a dollar sign.
+    languages: [ts]
+    severity: ERROR
diff --git a/.semgrepignore b/.semgrepignore
new file mode 100644
index 000000000..bb654c7a4
--- /dev/null
+++ b/.semgrepignore
@@ -0,0 +1,32 @@
+# Copied from https://github.com/returntocorp/semgrep-action/blob/develop/src/semgrep_agent/templates/.semgrepignore
+# Note that these are for semgrep-agent ONLY; command line semgrep does NOT use this file.
+
+# Ignore git items
+.gitignore
+.git/
+:include .gitignore
+
+# Common large directories
+node_modules/
+build/
+dist/
+vendor/
+env/
+.env/
+venv/
+.venv/
+*.min.js
+
+# Common test directories
+test/
+tests/
+
+# Semgrep rules folder
+.semgrep
+
+# Chef customizations
+third_party/
+*_test.go
+*.pb.go
+*.bindata.go
+*.spec.ts
diff --git a/Makefile.common_go b/Makefile.common_go
index 418d6acaf..eaf29aeec 100644
--- a/Makefile.common_go
+++ b/Makefile.common_go
@@ -20,6 +20,10 @@ GOLANGCILINTTARBALL:=golangci-lint-$(GOLANGCILINTVERSION)-$(PLATFORM).tar.gz
 
 LINTERARGS?=./...
 
+# Semgrep by default respects .gitignore; these are additive:
+SEMGREP_IGNORE := --exclude third_party --exclude *_test.go --exclude *.pb.go --exclude *.bindata.go
+SEMGREP_CONFIG := https://semgrep.dev/p/r2c-ci
+
 $(REPOROOT)/cache/$(GOLANGCILINTTARBALL):
 	curl --output $(REPOROOT)/cache/$(GOLANGCILINTTARBALL) -L https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCILINTVERSION)/$(GOLANGCILINTTARBALL)
 
@@ -50,13 +54,13 @@ spell:
 # NB: "third_party" only exists for automate-gateway, but no harm having it for other dirs here.
 semgrep:
 # uncomment if custom rules beyond automate-ui ever get added
-#	semgrep --config $(REPOROOT)/semgrep --exclude third_party --exclude *_test.go --exclude *.pb.go --exclude *.bindata.go
-	semgrep --config https://semgrep.dev/p/r2c-ci --exclude third_party --exclude *_test.go --exclude *.pb.go --exclude *.bindata.go
+# semgrep --config $(REPOROOT)/.semgrep $(SEMGREP_IGNORE)
+	semgrep --config $(SEMGREP_CONFIG) $(SEMGREP_IGNORE)
 
 #: Security validation via semgrep; autofix where possible
 semgrep-and-fix:
 # uncomment if custom rules beyond automate-ui ever get added
-#	semgrep --config $(REPOROOT)/semgrep --exclude third_party --exclude *_test.go --exclude *.pb.go --exclude *.bindata.go --autofix
-	semgrep --config https://semgrep.dev/p/r2c-ci --exclude third_party --exclude *_test.go --exclude *.pb.go --exclude *.bindata.go --autofix
+# semgrep --config $(REPOROOT)/.semgrep $(SEMGREP_IGNORE) --autofix
+	semgrep --config $(SEMGREP_CONFIG) $(SEMGREP_IGNORE) --autofix
 
 .PHONY: lint fmt fmt-check golang_version_check semgrep semgrep-and-fix
diff --git a/components/automate-cs-bookshelf/habitat/config/sys.config b/components/automate-cs-bookshelf/habitat/config/sys.config
index 8f79e5c53..c5a7b06ff 100644
--- a/components/automate-cs-bookshelf/habitat/config/sys.config
+++ b/components/automate-cs-bookshelf/habitat/config/sys.config
@@ -76,9 +76,7 @@
                                      {ecdhe_ecdsa,aes_128_gcm,null,sha256},
                                      {ecdhe_ecdsa,aes_256_gcm,null,sha384},
                                      {dhe_rsa,aes_128_gcm,null,sha256},
-                                     {dhe_rsa,aes_256_gcm,null,sha384},
-                                     {rsa,aes_128_gcm,null,sha256},
-                                     {rsa,aes_256_gcm,null,sha384}]},
+                                     {dhe_rsa,aes_256_gcm,null,sha384}]},
                           {eccs, [secp256r1,brainpoolP256r1,secp256k1,brainpoolP384r1,
                                   secp384r1,brainpoolP512r1,sect571r1,sect571k1,secp521r1,
                                   sect409k1,sect409r1,sect283k1,sect283r1,sect239k1,sect233k1,
diff --git a/components/automate-cs-oc-bifrost/habitat/config/sys.config b/components/automate-cs-oc-bifrost/habitat/config/sys.config
index f43b179e7..b89edb708 100644
--- a/components/automate-cs-oc-bifrost/habitat/config/sys.config
+++ b/components/automate-cs-oc-bifrost/habitat/config/sys.config
@@ -112,9 +112,7 @@
                                     {ecdhe_ecdsa,aes_128_gcm,null,sha256},
                                     {ecdhe_ecdsa,aes_256_gcm,null,sha384},
                                     {dhe_rsa,aes_128_gcm,null,sha256},
-                                    {dhe_rsa,aes_256_gcm,null,sha384},
-                                    {rsa,aes_128_gcm,null,sha256},
-                                    {rsa,aes_256_gcm,null,sha384}]},
+                                    {dhe_rsa,aes_256_gcm,null,sha384}]},
                          {eccs, [secp256r1,brainpoolP256r1,secp256k1,brainpoolP384r1,
                                  secp384r1,brainpoolP512r1,sect571r1,sect571k1,secp521r1,
                                  sect409k1,sect409r1,sect283k1,sect283r1,sect239k1,sect233k1,
diff --git a/components/automate-cs-oc-erchef/habitat/config/sys.config b/components/automate-cs-oc-erchef/habitat/config/sys.config
index f1fdce01a..9cb6e63a1 100644
--- a/components/automate-cs-oc-erchef/habitat/config/sys.config
+++ b/components/automate-cs-oc-erchef/habitat/config/sys.config
@@ -69,9 +69,7 @@
                                {ecdhe_ecdsa,aes_128_gcm,null,sha256},
                                {ecdhe_ecdsa,aes_256_gcm,null,sha384},
                                {dhe_rsa,aes_128_gcm,null,sha256},
-                               {dhe_rsa,aes_256_gcm,null,sha384},
-                               {rsa,aes_128_gcm,null,sha256},
-                               {rsa,aes_256_gcm,null,sha384}]},
+                               {dhe_rsa,aes_256_gcm,null,sha384}]},
                     {eccs, [secp256r1,brainpoolP256r1,secp256k1,brainpoolP384r1,
                             secp384r1,brainpoolP512r1,sect571r1,sect571k1,secp521r1,
                             sect409k1,sect409r1,sect283k1,sect283r1,sect239k1,sect233k1,
diff --git a/components/automate-ui/Makefile b/components/automate-ui/Makefile
index 233b5b817..28e906376 100644
--- a/components/automate-ui/Makefile
+++ b/components/automate-ui/Makefile
@@ -10,6 +10,11 @@ DEMO_FLAGS := --spec false
 NG_CMD := npm run ng --
 REPOROOT=../..
 
+# Semgrep by default respects .gitignore; these are additive:
+SEMGREP_IGNORE := --exclude *.spec.ts
+SEMGREP_CONFIG := https://semgrep.dev/p/r2c-ci
+
+
 install:
 	npm install
 	npm run install:ui-library
@@ -40,14 +45,18 @@ lint-typescript:
 	npm run lint
 
 #: Security validation via semgrep
-semgrep:
-	semgrep --config $(REPOROOT)/semgrep --exclude *.spec.ts
-	semgrep --config https://semgrep.dev/p/r2c-ci
+semgrep: semgrep-custom semgrep-published
+
+semgrep-custom:
+	semgrep --config $(REPOROOT)/.semgrep $(SEMGREP_IGNORE)
+
+semgrep-published:
+	semgrep --config $(SEMGREP_CONFIG) $(SEMGREP_IGNORE)
 
 #: Security validation via semgrep; autofix where possible
 semgrep-and-fix:
-	semgrep --config $(REPOROOT)/semgrep --exclude *.spec.ts --autofix
-	semgrep --config https://semgrep.dev/p/r2c-ci --autofix
+	semgrep --config $(REPOROOT)/.semgrep $(SEMGREP_IGNORE) --autofix
+	semgrep --config $(SEMGREP_CONFIG) $(SEMGREP_IGNORE) --autofix
 
 pr-ready: unit-all-browsers lint e2e
 
@@ -103,4 +112,4 @@ spell:
 	popd > /dev/null; \
 	exit $$EXIT_CODE
 
-.PHONY: build install test lint lint-html lint-sass lint-typescript semgrep semgrep-and-fix unit e2e license_scout peer-dependencies e2e-aot unit-all-browsers pr-ready start
+.PHONY: build install test lint lint-html lint-sass lint-typescript semgrep semgrep-and-fix semgrep-custom semgrep-published unit e2e license_scout peer-dependencies e2e-aot unit-all-browsers pr-ready start
diff --git a/components/automate-ui/src/app/components/page-picker/page-picker.component.html b/components/automate-ui/src/app/components/page-picker/page-picker.component.html
index 0a1e5e3dd..cc61e31f0 100644
--- a/components/automate-ui/src/app/components/page-picker/page-picker.component.html
+++ b/components/automate-ui/src/app/components/page-picker/page-picker.component.html
@@ -1,4 +1,4 @@
-<ng-container *ngIf="selectablePages.length > 1">
+<ng-container *ngIf="selectablePages.length > 1 || last > 1">
   <ng-container *ngIf="!forDesktop">
     <chef-button secondary
       class="page-picker-item page-picker-first"
@@ -17,7 +17,7 @@
       class="page-picker-item"
       [attr.primary]="item === page"
       [attr.secondary]="item !== page"
-      [attr.disabled]="selectablePages.length <= 1"
+      [attr.disabled]="selectablePages.length <= 1 && last <= 1"
       (click)="onItemTap(item)">
       {{ item }}
     </chef-button>
diff --git a/components/automate-ui/src/app/components/page-picker/page-picker.component.spec.ts b/components/automate-ui/src/app/components/page-picker/page-picker.component.spec.ts
index 4eb315211..66cee695c 100644
--- a/components/automate-ui/src/app/components/page-picker/page-picker.component.spec.ts
+++ b/components/automate-ui/src/app/components/page-picker/page-picker.component.spec.ts
@@ -107,6 +107,51 @@ describe('PagePickerComponent', () => {
         });
       });
     });
+
+    describe('page set calculations', () => {
+
+      using([
+        [ 1, 1, 1, [1], 'just one item and one allowed page'],
+        [ 1, 3, 1, [1], 'just one item with multiple allowed pages'],
+        [10, 3, 1, [1], 'one full page of items'],
+        [25, 5, 1, [1, 2, 3], 'multiple pages less than one full view'],
+        [46, 5, 1, [1, 2, 3, 4, 5],
+          'multiple pages of exactly one full view but last page not full'],
+        [50, 5, 1, [1, 2, 3, 4, 5],
+          'multiple pages of exactly one full view and last page full'],
+        [46, 5, 3, [1, 2, 3, 4, 5],
+          'multiple pages of exactly one full view, on middle page'],
+        [46, 5, 5, [1, 2, 3, 4, 5],
+          'multiple pages of exactly one full view, on last page'],
+        [105, 5, 6, [6, 7, 8, 9, 10],
+          'visit first page beyond the first full view'],
+        [105, 5, 11, [11],
+          'visit last view'],
+        [105, 3, 1, [1, 2, 3],
+          'different pages per view, first view'],
+        [105, 3, 4, [4, 5, 6],
+          'different pages per view, second view'],
+        [105, 3, 10, [10, 11],
+          'different pages per view, last view']
+      ], function (
+        totalItems: number,
+        pagesPerView: number,
+        currentPage: number,
+        expected: number[],
+        description: string
+        ) {
+        it(`with ${description}, expect ${expected}`, () => {
+          component.perPage = 10; // just easier for mentally munging the data above
+          component.total = totalItems;
+          component.maxSelectablePages = pagesPerView;
+          component.page = currentPage;
+
+          component.ngOnChanges();
+
+          expect(component.selectablePages).toEqual(expected);
+        });
+      });
+    });
   });
 
 });
diff --git a/components/automate-ui/src/app/components/page-picker/page-picker.component.ts b/components/automate-ui/src/app/components/page-picker/page-picker.component.ts
index ce4a36aa2..4dfeef6b3 100644
--- a/components/automate-ui/src/app/components/page-picker/page-picker.component.ts
+++ b/components/automate-ui/src/app/components/page-picker/page-picker.component.ts
@@ -13,7 +13,7 @@ export class PagePickerComponent implements OnChanges {
   @Input() total: number;
   @Input() perPage: number;
   @Input() page: number;
-  @Input() maxPageItems = 10;
+  @Input() maxSelectablePages = 10;
   @Input() @HostBinding('class.forDesktop') forDesktop = false;
   @Input() @HostBinding('class.fullScreened') fullScreened = false;
 
@@ -54,24 +54,17 @@ export class PagePickerComponent implements OnChanges {
 
   ngOnChanges() {
     this.last = Math.ceil(this.total / this.perPage) || 1;
+    this.page = Math.min(this.page, this.last);
     this.prev = (this.page === this.first) ? null : this.page - 1;
     this.next = (this.page === this.last) ? null : this.page + 1;
 
-    const pages = [];
-    const selectedIndex = ((this.page - 1) % this.maxPageItems);
+    const pages: number[] = [];
+    const selectedIndex = ((this.page - 1) % this.maxSelectablePages);
 
-    for (let pageIndex = selectedIndex; pageIndex >= 0; --pageIndex) {
-      const prev = this.page + (pageIndex - selectedIndex);
-      pages[pageIndex] = prev;
-    }
-
-    pages[selectedIndex] = this.page;
-
-    for (let pageIndex = selectedIndex; pageIndex < this.maxPageItems; ++pageIndex) {
-      const next = this.page + (pageIndex - selectedIndex);
-      if (next <= this.last) {
-        pages[pageIndex] = next;
-      }
+    for (let pageIndex = 0, pg = this.page - selectedIndex;
+      pageIndex < this.maxSelectablePages && pg <= this.last;
+      ++pageIndex, pg = this.page + pageIndex - selectedIndex) {
+      pages[pageIndex] = pg;
     }
     this.selectablePages = pages;
 
diff --git a/components/automate-ui/src/app/page-components/run-history/run-history.component.html b/components/automate-ui/src/app/page-components/run-history/run-history.component.html
index 0aff66af8..705ff61c2 100644
--- a/components/automate-ui/src/app/page-components/run-history/run-history.component.html
+++ b/components/automate-ui/src/app/page-components/run-history/run-history.component.html
@@ -66,7 +66,7 @@
         [total]="stats(selected)"
         [perPage]="pageSize"
         [page]="currentPage"
-        [maxPageItems]="3"
+        [maxSelectablePages]="3"
         (pageChanged)="updatePageNumber($event)">
       </app-page-picker>
     </div>
diff --git a/components/automate-ui/src/app/page-components/run-history/run-history.component.scss b/components/automate-ui/src/app/page-components/run-history/run-history.component.scss
index 0fb250c75..e2cf032f5 100644
--- a/components/automate-ui/src/app/page-components/run-history/run-history.component.scss
+++ b/components/automate-ui/src/app/page-components/run-history/run-history.component.scss
@@ -180,6 +180,5 @@ app-date-selector {
 }
 
 .history-pagination {
-  margin-bottom: 1em;
-  justify-content: center;
+  padding: 1em;
 }
diff --git a/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-overview/overview-trend/overview-trend.component.ts b/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-overview/overview-trend/overview-trend.component.ts
index ce2e42f22..a6c5f3faa 100644
--- a/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-overview/overview-trend/overview-trend.component.ts
+++ b/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-overview/overview-trend/overview-trend.component.ts
@@ -157,7 +157,7 @@ export class OverviewTrendComponent implements OnChanges, OnDestroy  {
         return [
           moment.utc(this.domainX[0]).format(DateTime.CHEF_DATE_TIME),
           moment.utc(this.domainX[1]).format(DateTime.CHEF_DATE_TIME)
-        ].join(' - ');
+        ].join(' - ') + ' (UTC)';
       });
 
     this.axisXSelection.select('.domain')
@@ -285,7 +285,7 @@ export class OverviewTrendComponent implements OnChanges, OnDestroy  {
     update
       .attr('for', (_d, i) => `dot-group-bg-${i}`);
     update.select('.tip-name')
-      .text(d => d3.timeFormat('%a %B %e %Y')(d.report_time));
+      .text(d => d3.timeFormat('%a %B %e %Y (UTC)')(d.report_time));
 
     statuses.forEach(status => {
       update.select(`.tip-legend-item.${status}`)
diff --git a/components/automate-ui/src/app/pages/+compliance/+reporting/reporting-searchbar/reporting-searchbar.component.html b/components/automate-ui/src/app/pages/+compliance/+reporting/reporting-searchbar/reporting-searchbar.component.html
index 400ff4830..d8835aabd 100644
--- a/components/automate-ui/src/app/pages/+compliance/+reporting/reporting-searchbar/reporting-searchbar.component.html
+++ b/components/automate-ui/src/app/pages/+compliance/+reporting/reporting-searchbar/reporting-searchbar.component.html
@@ -70,7 +70,7 @@
       <span *ngIf="last24h">Last 24 hours</span>
       <ng-container *ngIf="!last24h">
         <chef-icon>date_range</chef-icon>
-        <span>{{ date | datetime: CHEF_SHORT_DATE }}</span>
+        <span>{{ date | datetime: CHEF_SHORT_DATE }} (UTC)</span>
       </ng-container>
     </chef-button>
     <chef-dropdown class="calendar-menu-dropdown" [attr.visible]="calendarMenuVisible">
@@ -80,7 +80,7 @@
         </chef-button>
         <chef-button class="calendar-btn" secondary (click)="showCalendar()">
           <chef-icon>date_range</chef-icon>
-          <span>{{ date | datetime: CHEF_SHORT_DATE }}</span>
+          <span>Choose Date (UTC)</span>
         </chef-button>
       </chef-click-outside>
     </chef-dropdown>
diff --git a/components/automate-ui/src/app/pages/+compliance/+scanner/containers/nodes-edit/nodes-edit.component.ts b/components/automate-ui/src/app/pages/+compliance/+scanner/containers/nodes-edit/nodes-edit.component.ts
index 965c72fde..a608ed917 100644
--- a/components/automate-ui/src/app/pages/+compliance/+scanner/containers/nodes-edit/nodes-edit.component.ts
+++ b/components/automate-ui/src/app/pages/+compliance/+scanner/containers/nodes-edit/nodes-edit.component.ts
@@ -24,7 +24,7 @@ export class NodesEditComponent implements OnInit {
 
   form: FormGroup;
   backendControl: FormGroup;
-  backendValue: Observable<string>;
+  backendValue$: Observable<string>;
   // Array of secrets available for user to select
   // TODO make ngrx/store selection
   secrets$: Observable<any[]>;
@@ -56,10 +56,10 @@ export class NodesEditComponent implements OnInit {
 
         // Swap fields based on selected "backend" value (ssh, winrm)
         this.backendControl = this.form.get('target_config').get('backend') as FormGroup;
-        this.backendValue = this.backendControl
+        this.backendValue$ = this.backendControl
           .valueChanges
           .pipe(startWith(this.backendControl.value));
-        this.backendValue.subscribe(backend => {
+        this.backendValue$.subscribe(backend => {
           const step = this.form.get('target_config') as FormGroup;
           // step.get('secrets').setValue([]);
           switch (backend) {
@@ -78,7 +78,7 @@ export class NodesEditComponent implements OnInit {
 
         this.secrets$ = combineLatest([
           this.fetchSecrets(),
-          this.backendValue
+          this.backendValue$
         ]).pipe(
           map(([secrets, backend]: [any[], string]) =>
             secrets.filter(s => s.type === backend || s.type === 'sudo'))
diff --git a/components/compliance-service/docs/kt.md b/components/compliance-service/docs/kt.md
new file mode 100644
index 000000000..501489d35
--- /dev/null
+++ b/components/compliance-service/docs/kt.md
@@ -0,0 +1,270 @@
+# Automate Compliance (and dependencies) Knowledge Transfer notes
+
+## Overview
+
+Compliance in Automate helps customer manage security and compliance risk of:
+ * Servers (Windows, Linux, etc)
+ * Cloud Accounts (AWS, Azure, GCP, Digital Ocean, etc)
+ * Network devices
+ * Containers
+ * Anything else that has an API or CLI if a custom plugin is created. Not necessary security or compliance related. Used frequently for common IT automated testing because of it's versatility.
+
+Automate ships with 399 compliance profiles, majority of them automatically converted from industry leading compliance benchmarks like **CIS** and **STIG**.
+ * `Automate UI > Compliance > Profiles > Available tab` also called in the code as the *market* profiles
+ * Once imported with the `Get` button, they can be used to trigger scans
+
+Chef has certified a lot of these profiles with **CIS**: https://www.cisecurity.org/partner/chef/
+
+The profiles are executed a [Ruby](http://www.ruby-lang.org) based tool called **InSpec**. The profile execution generates a report, which is then used by Automate for the Compliance reporting data.
+InSpec is owned by Chef/Progress, with the source code being available in this public repository:
+ - https://github.com/inspec/inspec
+
+InSpec uses a ruby gem called [train](https://github.com/inspec/train) to communicate with the targets.
+
+Starting with InSpec v4 (released in April 2019), packages of InSpec and Chef Infra require a Chef license acceptance. For commercial use, a paid subscription.
+
+Independent of Chef, **[CINC group](https://cinc.sh)** releases open source builds of *Chef Infra* and *Chef InSpec* at.
+
+---
+
+**Show slides of Infra vs InSpec and basic operation of InSpec**
+
+---
+
+## How InSpec reports make it to Automate:
+
+1. **[audit cookbook](https://github.com/chef-cookbooks/audit)**, executed by Chef Infra. The most common execution modes:
+
+ - **Chef Infra** (runs **audit cookbook** [runs **InSpec**]) -----> **Chef Server** -----> **Chef Automate**
+ - **Chef Infra** (runs **audit cookbook** [runs **InSpec**]) ---*data collection token*---> **Chef Automate**
+
+
+2. **Habitat Effortless package** (with bundled InSpec) ---*data collection token*---> **Chef Automate**
+
+3. **Automate** (with bundled InSpec) executes remote scans of VMs, cloud accounts(AWS, Azure, GCP, SSL certificates)
+
+4. **InSpec** (with the `automate` reporter) executed manually or from Ansible/Puppet/Cron job. In this mode, InSpec posts the report to an Automate URL with a **data collection token**
+
+Example InSpec exec and json report:
+```
+$ inspec exec ~/git/mycompliance-profile/mylinux --reporter json | jq .
+{
+  "platform": {
+    "name": "debian",
+    "release": "bullseye/sid"
+  },
+  "profiles": [
+    {
+      "name": "mylinux",
+      "version": "0.1.4",
+      "sha256": "1a319ce266574c9a6f121245be8e0cf355ee6c498d556e0173207e8a71fbe908",
+      "title": "My Demo Linux Profile",
+...
+```
+
+## Audit cookbook
+
+Main driver of InSpec report data that is sent to Automate.
+Executed by Chef Infra client with attributes: profiles, inputs, report target.
+
+Provided over the years a stop gap, a place to quick fix things or make sure InSpec and Automate stay "friends".
+
+Example: InSpec generates a report of **100 MB** based on the profile being executed and the target node. Automate has a hard limit of **4 MB** inherited from GRPC. Audit cookbook takes the **100 MB** report, reduces profile metadata (if Automate already has it), truncates results, and send to Automate an acceptable report of **1 MB** :tada: :tada: :tada:
+
+## Ingestion
+
+The compliance code responsible for processing the InSpec reports sent to Automate can be found at this location in the repository:
+```
+/components/compliance-service/ingest/
+```
+
+Data is stored in ElasticSearch in 3 index types:
+
+ * Daily summary index (e.g. `comp-7-s-2020.11.07`), one for each day
+ * Daily reports index (e.g. `comp-7-r-2020.11.07`), one for each day
+ * Profiles metadata index (e.g. `comp-3-profiles`), just one
+
+^ `comp` is the prefix for `compliance` data, 7 and 3 are the versions of these indices. These numbers increase with index migrations.
+
+Used to normalize data, for deduplication reasons and to avoid unnecessary ingestion load.
+Calculate report stats for performant and rich data aggregations.
+
+## Compliance profiles
+
+These are the artifacts, the content that InSpec needs to execute in order to deliver a scan report.
+
+You can find the backend code for this functionality at:
+```
+components/compliance-service/api/profiles/server/pgserver.go
+components/compliance-service/dao/pgdb/jobs.go
+```
+
+The profile data is stored in Postgresql and the profile metadata is also cached in ElasticSearch in the `comp-*-profiles` index.
+
+You can interact with the service from this Automate page:
+`Automate UI > Compliance > Profiles`
+
+## Secrets service
+
+Is a service that deals with credentials of nodes or integrations. For example:
+ * SSH username & passwords or RSA private keys,
+ * WinRM username & passwords
+ * AWS ACCESS KEY ID & SECRET ACCESS KEY
+ * ServiceNow credentials
+
+The backend code for this service is located in:
+```
+components/secrets-service
+```
+
+These are two of the Automate pages where you can manually interact with the service:
+```
+Automate UI > Settings > Node Credentials > Create Credential
+Automate UI > Settings > Node Integrations > Create Integration
+```
+
+The data is stored in Postgresql symmetrically encrypted.
+
+
+## Node manager service
+
+The node manager keeps track of the nodes in Automate. Either manually added or synched from Chef Server or infrastructure clouds (AWS, Azure, GCP)
+
+The backend code for this service is located at:
+```
+components/nodemanager-service
+```
+
+You can interact with the service from this Automate page:
+`Automate UI > Compliance > Scan Jobs > Nodes Added tab > Add Nodes`
+
+
+## Scan jobs
+
+These are two types of scan jobs:
+ * single run job
+ * multiple runs job, recurrent with or without end time limit
+
+You can find the backend code for this functionality at:
+```
+components/compliance-service/api/jobs/server/server.go
+components/compliance-service/dao/pgdb/jobs.go
+```
+
+The data is stored in Postgresql.
+
+You can interact with the service from this Automate page:
+`Automate UI > Compliance > Scan Jobs > Scan Jobs tab`
+
+## Last 24h API / UI improvements
+
+On November 11, 2020 an improvement has been merged in master to show in the UI by default Compliance data for the last 24 hours, relative to the time of the user.
+Before we were showing based on the current UTC date, regardless of the user's timezone.
+
+---
+
+## Reporting with extensive filtering and deep filtering
+Surface level filters:
+- environment
+- organization
+- chef_server
+- chef_tags
+- policy_group
+- policy_name
+- status
+- node_name
+- platform
+- platform_with_version
+- role
+- recipe
+- inspec_version
+- ipaddress
+- node_id 
+- start_time 
+- end_time
+- job_id
+
+Nested filters:
+- control
+- control_tag
+- profile_id
+
+Example of some filters used across most reporting apis
+```json
+{
+  "filters": [
+    {
+      "type": "start_time",
+      "values": [
+        "2019-01-26T00:00:00Z"
+      ]
+    },
+    {
+      "type": "end_time",
+      "values": [
+        "2019-02-05T23:59:59Z"
+      ]
+    },
+    {
+      "type": "profile_id",
+      "values": [
+        "f42d2f48c9acd48f52324d52ec575ca9028e405eb303f69cb34d79eb0e588b5c"
+      ]
+    },
+    {
+      "type": "control",
+      "values": [
+        "sshd-02"
+      ]
+    },
+    {
+      "type": "platform",
+      "values": [
+        "centos"
+      ]
+    },
+    {
+      "type": "environment",
+      "values": [
+        "DevSec Prod Zeta"
+      ]
+    }
+  ]
+}
+```
+
+Deep filtering allows us to see how our infra is doing with respect to the overall node, a specific profile, or a specific control
+ 
+
+## The four states of a control / profile / node:
+ * passed 
+ * skipped
+ * failed
+ * waived
+```$go
+func computeStatus(failed int32, passed int32, skipped int32, waived int32) string {
+	if failed > 0 {
+		return "failed"
+	} else if passed == 0 && skipped == 0 && waived > 0 {
+		return "waived"
+	} else if passed > 0 || skipped == 0 {
+		return "passed"
+	} else if passed == 0 && skipped > 0 {
+		return "skipped"
+	}
+	return "unknown"
+}
+``` 
+  
+## Suggestions
+Reports contain certain fields that we use to filter on
+the suggestions api allows us to list close matches as we type for more efficient selection
+The reporting data is stored in ES in summary and also in detailed form.. 
+Currently the summary is only used for surface level suggestions
+
+
+## ES Migrations
+* index versioning is separate for report indices and profiles indices which minimizes migrations 
+* the migration flow chart shows the process by which we migrate time series for maximum up time
+* the interface driven approach to migrations (esMigratable interface)
+
diff --git a/components/event-gateway/pkg/nats/server.go b/components/event-gateway/pkg/nats/server.go
index 7c93d3c83..3e1e9d8f1 100644
--- a/components/event-gateway/pkg/nats/server.go
+++ b/components/event-gateway/pkg/nats/server.go
@@ -204,6 +204,7 @@ func mTLSConf(c *config.EventGatewayConfig) (*tls.Config, error) {
 		KeyFile:  c.TLSConfig.KeyPath,
 		CaFile:   c.TLSConfig.RootCACertPath,
 		Verify:   true,
+		Ciphers:  secureconn.DefaultCipherSuites(),
 	}
 
 	natsTLSConf, err := natsd.GenTLSConfig(natsTLSOpts)
diff --git a/components/event-service/nats/server.go b/components/event-service/nats/server.go
index 8e69ae70f..b9fec1632 100644
--- a/components/event-service/nats/server.go
+++ b/components/event-service/nats/server.go
@@ -6,14 +6,15 @@ import (
 	"sync"
 	"time"
 
-	"github.com/chef/automate/components/event-service/config"
-
 	natsd "github.com/nats-io/gnatsd/server"
 	streamd "github.com/nats-io/nats-streaming-server/server" // nolint: misspell
 	stores "github.com/nats-io/nats-streaming-server/stores"
 	nats "github.com/nats-io/nats.go"
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
+
+	"github.com/chef/automate/components/event-service/config"
+	"github.com/chef/automate/lib/grpc/secureconn"
 )
 
 type shutdowner interface {
@@ -71,6 +72,7 @@ func mTLSConfFor(c *config.EventConfig, service string) *tls.Config {
 		KeyFile:  c.TLSConfig.KeyPath,
 		CaFile:   c.TLSConfig.RootCACertPath,
 		Verify:   true,
+		Ciphers:  secureconn.DefaultCipherSuites(),
 	}
 
 	natsTLSConf, err := natsd.GenTLSConfig(natsTLSOpts)
diff --git a/components/ingest-service/pipeline/publisher/msg_distributor.go b/components/ingest-service/pipeline/publisher/msg_distributor.go
index bd2a0a410..2ee65807d 100644
--- a/components/ingest-service/pipeline/publisher/msg_distributor.go
+++ b/components/ingest-service/pipeline/publisher/msg_distributor.go
@@ -54,7 +54,8 @@ func sendMessage(pipeInChannels []chan message.ChefRun, msg message.ChefRun) {
 
 		log.WithFields(log.Fields{
 			"number_of_publishers": len(pipeInChannels),
-		}).Warn("All elasticsearch publishers are full")
+		}).Error("All elasticsearch publishers are full. " +
+			"Increase the max number of bundled messages and/or number of publishers")
 		time.Sleep(time.Millisecond * 10)
 	}
 }
diff --git a/lib/config/resolve_config_parser.go b/lib/config/resolve_config_parser.go
new file mode 100644
index 000000000..03b129c2c
--- /dev/null
+++ b/lib/config/resolve_config_parser.go
@@ -0,0 +1,68 @@
+package config
+
+import (
+	"bytes"
+	"io/ioutil"
+	"net"
+	"regexp"
+)
+
+var (
+	defaultResolvConf = "/etc/resolv.conf"
+	nameServerRegex   = regexp.MustCompile(`^\s*nameserver\s*(.+?)\s*$`)
+)
+
+// GetNameServersFromResolveConfig parses a file in the format of /etc/resolv.conf
+// The resolve.conf format is described in resolv.conf(5):
+//
+//    https://man7.org/linux/man-pages/man5/resolv.conf.5.html
+//
+// This function looks up at the lines starting with `nameserver`.
+// There can be a single nameserver each line and maximum 3 (defined
+// my MAXNS in resolv.h) in the file.
+func GetNameServersFromResolveConfig(filePath string) ([]string, error) {
+	content, err := ioutil.ReadFile(filePath)
+	if err != nil {
+		return nil, err
+	}
+
+	nameservers := ParseNameServers(content)
+	return nameservers, nil
+}
+
+// ParseNameServers parses an array of bytes and returns the nameservers
+// The content should be of format of /etc/resolv.conf i.e.
+// nameserver 12.0.0.3
+func ParseNameServers(fileContent []byte) []string {
+	fileLines := bytes.Split(fileContent, []byte("\n"))
+	var nameservers []string
+	for _, currentLine := range fileLines {
+		var contentToParse = currentLine
+		var commentIndicatorIndex = bytes.Index(currentLine, []byte("#"))
+		if commentIndicatorIndex != -1 {
+			// Only check the content before the comment section
+			contentToParse = currentLine[:commentIndicatorIndex]
+		}
+
+		server := nameServerRegex.FindSubmatch(contentToParse)
+		if len(server) == 2 {
+			address := string(server[1])
+			if net.ParseIP(address) != nil {
+				nameservers = append(nameservers, address)
+			}
+		}
+	}
+
+	return nameservers
+}
+
+// GetSystemResolvers returns resolvers discovered via
+// /etc/resolv.conf. If not valid resolvers are found, an empty array
+// is returned.
+func GetSystemResolvers() []string {
+	resolvers, err := GetNameServersFromResolveConfig(defaultResolvConf)
+	if err != nil {
+		return []string{}
+	}
+	return resolvers
+}
diff --git a/lib/grpc/secureconn/factory.go b/lib/grpc/secureconn/factory.go
index c001583c8..118e11206 100644
--- a/lib/grpc/secureconn/factory.go
+++ b/lib/grpc/secureconn/factory.go
@@ -136,10 +136,9 @@ func (f *Factory) DialOptions(serviceName string) []grpc.DialOption {
 }
 
 // The following is a re-implementation of some of what is in the
-// Golang standard library to construct the default cipher suite list
-// with minor modifications.
+// Golang standard library to construct the default cipher suite list.
 //
-// We re-implement it here to disable 3DES cipher suites while still
+// We re-implement it here to disable many cipher suites while still
 // getting the hardware-capability-based cipher ordering selection.
 var (
 	once                sync.Once
@@ -155,12 +154,38 @@ func DefaultCipherSuites() []uint16 {
 // defaultCipherSuites are ordered by preference. We attempt to detect
 // the presence of CPU features that make AES-GCM ciphers faster and
 // prefer those if present.
+//
+// This configuration is currently in line with the "Intermediate"
+// advice from Mozilla.
+//
+// These ciphers only impact TLS1.2 connection. TLS1.3 does not allow
+// for configurable cipher suites and most of our Go-to-Go
+// interactions are likely using TLS1.3.
+//
+// HTTP/2 imposes additional cipher-suite restrictions which would
+// have disallowed the majority of the cipher suites we are excluding.
+//
+// Currently, the effectively disables:
+//
+// tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
+// tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
+// tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+// tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+// tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+// tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+// tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+// tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+// tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+// tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
+// tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+// tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+// tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA
+//
 func initCipherSuites() {
-	var topCipherSuites []uint16
 	if HasAESNI() {
 		// If AES-GCM hardware is provided then prioritize AES-GCM
 		// cipher suites.
-		topCipherSuites = []uint16{
+		defaultCipherSuites = []uint16{
 			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
 			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
 			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
@@ -171,7 +196,7 @@ func initCipherSuites() {
 	} else {
 		// Without AES-GCM hardware, we put the ChaCha20-Poly1305
 		// cipher suites first.
-		topCipherSuites = []uint16{
+		defaultCipherSuites = []uint16{
 			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
 			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
 			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
@@ -180,29 +205,4 @@ func initCipherSuites() {
 			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
 		}
 	}
-	commonCiphers := []uint16{
-		tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
-		tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
-		// Disabled since our new SSL scanner disallows anything with CBC
-		//
-		// tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-		// tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-		// tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-		// tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-		// tls.TLS_RSA_WITH_AES_128_CBC_SHA,
-		// tls.TLS_RSA_WITH_AES_256_CBC_SHA,
-		//
-		// Golang enables these but we disable them
-		// because 3DES makes scanners unhappy
-		//
-		// tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
-		// tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
-		//
-		// Disabled in Golang since only SHA1 CBC has Lucky13 mitigations
-		// tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-		// tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
-		// tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-	}
-
-	defaultCipherSuites = append(topCipherSuites, commonCiphers...)
 }
diff --git a/semgrep/rxjs-syntax.yaml b/semgrep/rxjs-syntax.yaml
deleted file mode 100644
index a90877d9b..000000000
--- a/semgrep/rxjs-syntax.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-rules:
-
-  - id: combineLatest-not-combining
-    pattern: combineLatest([$SINGLE_EXPRESSION])
-    fix:  $SINGLE_EXPRESSION
-    message: combineLatest is not needed with a single argument
-    languages: [ts]
-    severity: WARNING
-
-  - id: observables-from-select-or-pipe-not-ending-with-dollar-sign
-    patterns:
-    - pattern-regex: '\w+\s*(:.+?)?=.*?(select|pipe)'
-    - pattern-either:
-      - pattern-inside: $VAR = $EXPR.select(...);
-      - pattern-inside: $VAR = $EXPR.pipe(...);
-    - pattern-not-inside: $VAR = d3.select(...);
-    message: |
-      Observable variable should end with a dollar sign.
-    languages: [ts]
-    severity: ERROR
-
-  - id: observables-from-values-not-ending-with-dollar-sign
-    patterns:
-    - pattern-regex: '\w+\s*(:.+?)?=\s*(observableOf|of)'
-    - pattern-either:
-      - pattern-inside: $VAR = observableOf(...)
-      - pattern-inside: $VAR = of(...)
-    message: |
-      Observable variable should end with a dollar sign.
-    languages: [ts]
-    severity: ERROR


Test patch:
diff --git a/components/automate-gateway/integration/nodes_test.go b/components/automate-gateway/integration/nodes_test.go
index fd94ab9b5..17db341e4 100644
--- a/components/automate-gateway/integration/nodes_test.go
+++ b/components/automate-gateway/integration/nodes_test.go
@@ -1,256 +1,256 @@
 package integration
 
-import (
-	"fmt"
-	"time"
+// import (
+// 	"fmt"
+// 	"time"
 
-	"github.com/chef/automate/api/external/common/query"
-	gwnodes "github.com/chef/automate/api/external/nodes"
-	"github.com/chef/automate/api/external/secrets"
-	"github.com/chef/automate/api/interservice/compliance/jobs"
-	"github.com/chef/automate/api/interservice/nodemanager/nodes"
-	"github.com/chef/automate/lib/pcmp/passert"
-)
+// 	"github.com/chef/automate/api/external/common/query"
+// 	gwnodes "github.com/chef/automate/api/external/nodes"
+// 	"github.com/chef/automate/api/external/secrets"
+// 	"github.com/chef/automate/api/interservice/compliance/jobs"
+// 	"github.com/chef/automate/api/interservice/nodemanager/nodes"
+// 	"github.com/chef/automate/lib/pcmp/passert"
+// )
 
-func (suite *GatewayTestSuite) TestGatewayNodesClient() {
-	// get the gateway nodes client (this is where we trigger detect job)
-	gatewayNodesClient := gwnodes.NewNodesServiceClient(suite.gwConn)
+// func (suite *GatewayTestSuite) TestGatewayNodesClient() {
+// 	// get the gateway nodes client (this is where we trigger detect job)
+// 	gatewayNodesClient := gwnodes.NewNodesServiceClient(suite.gwConn)
 
-	// setup secrets service for secret creation
-	secretsClient, err := suite.clients.SecretClient()
-	suite.Require().NoError(err)
+// 	// setup secrets service for secret creation
+// 	secretsClient, err := suite.clients.SecretClient()
+// 	suite.Require().NoError(err)
 
-	// setup compliance-service clients for job creation
-	jobsClient, err := suite.clients.ComplianceJobsServiceClient()
-	suite.Require().NoError(err)
+// 	// setup compliance-service clients for job creation
+// 	jobsClient, err := suite.clients.ComplianceJobsServiceClient()
+// 	suite.Require().NoError(err)
 
-	// setup nodemanager-service clients for node creation
-	nodesClient, err := suite.clients.NodesClient()
-	suite.Require().NoError(err)
+// 	// setup nodemanager-service clients for node creation
+// 	nodesClient, err := suite.clients.NodesClient()
+// 	suite.Require().NoError(err)
 
-	// delete all jobs so we start with a clean slate
-	jobsList, err := jobsClient.List(suite.ctx, &jobs.Query{})
-	suite.Require().NoError(err)
-	for _, job := range jobsList.Jobs {
-		_, err := jobsClient.Delete(suite.ctx, &jobs.Id{Id: job.Id})
-		suite.Require().NoError(err)
-	}
+// 	// delete all jobs so we start with a clean slate
+// 	jobsList, err := jobsClient.List(suite.ctx, &jobs.Query{})
+// 	suite.Require().NoError(err)
+// 	for _, job := range jobsList.Jobs {
+// 		_, err := jobsClient.Delete(suite.ctx, &jobs.Id{Id: job.Id})
+// 		suite.Require().NoError(err)
+// 	}
 
-	fmt.Println("creating secrets for the nodes tests")
-	// create secret for node
-	secretID, err := secretsClient.Create(suite.ctx, &secrets.Secret{
-		Name: "test secret",
-		Type: "ssh",
-		Data: []*query.Kv{
-			{Key: "username", Value: suite.target.User},
-			{Key: "key", Value: suite.target.Key},
-		},
-	})
-	suite.Require().NoError(err)
+// 	fmt.Println("creating secrets for the nodes tests")
+// 	// create secret for node
+// 	secretID, err := secretsClient.Create(suite.ctx, &secrets.Secret{
+// 		Name: "test secret",
+// 		Type: "ssh",
+// 		Data: []*query.Kv{
+// 			{Key: "username", Value: suite.target.User},
+// 			{Key: "key", Value: suite.target.Key},
+// 		},
+// 	})
+// 	suite.Require().NoError(err)
 
-	// create bad secret for testing
-	badSecretID, err := secretsClient.Create(suite.ctx, &secrets.Secret{
-		Name: "test secret",
-		Type: "ssh",
-		Data: []*query.Kv{
-			{Key: "username", Value: suite.target.User},
-			{Key: "password", Value: "definitely not the password"},
-		},
-	})
-	suite.Require().NoError(err)
+// 	// create bad secret for testing
+// 	badSecretID, err := secretsClient.Create(suite.ctx, &secrets.Secret{
+// 		Name: "test secret",
+// 		Type: "ssh",
+// 		Data: []*query.Kv{
+// 			{Key: "username", Value: suite.target.User},
+// 			{Key: "password", Value: "definitely not the password"},
+// 		},
+// 	})
+// 	suite.Require().NoError(err)
 
-	fmt.Println("creating node with no auto detect tag")
-	// create node
-	nodeID, err := gatewayNodesClient.Create(suite.ctx, &gwnodes.Node{
-		Name: "test node",
-		Tags: []*query.Kv{
-			{Key: "_no_auto_detect", Value: "true"},
-		},
-		TargetConfig: &gwnodes.TargetConfig{
-			Backend: "ssh",
-			Host:    suite.target.Host,
-			Port:    22,
-			Secrets: []string{badSecretID.GetId()},
-		},
-	})
-	suite.Require().NoError(err)
+// 	fmt.Println("creating node with no auto detect tag")
+// 	// create node
+// 	nodeID, err := gatewayNodesClient.Create(suite.ctx, &gwnodes.Node{
+// 		Name: "test node",
+// 		Tags: []*query.Kv{
+// 			{Key: "_no_auto_detect", Value: "true"},
+// 		},
+// 		TargetConfig: &gwnodes.TargetConfig{
+// 			Backend: "ssh",
+// 			Host:    suite.target.Host,
+// 			Port:    22,
+// 			Secrets: []string{badSecretID.GetId()},
+// 		},
+// 	})
+// 	suite.Require().NoError(err)
 
-	fmt.Println("checking node status: expected unknown")
-	// no detect job should have been triggered, reading node should have status unknown
-	node, err := nodesClient.Read(suite.ctx, &nodes.Id{Id: nodeID.Id})
-	suite.Require().NoError(err)
-	suite.Equal("unknown", node.Status)
+// 	fmt.Println("checking node status: expected unknown")
+// 	// no detect job should have been triggered, reading node should have status unknown
+// 	node, err := nodesClient.Read(suite.ctx, &nodes.Id{Id: nodeID.Id})
+// 	suite.Require().NoError(err)
+// 	suite.Equal("unknown", node.Status)
 
-	fmt.Println("update node to run detect job with bad secret")
-	// call update on the node id, ensure detect job was triggered
-	_, err = gatewayNodesClient.Update(suite.ctx, &gwnodes.Node{
-		Id:   nodeID.Id,
-		Name: "test node",
-		Tags: []*query.Kv{
-			{Key: "_no_auto_detect", Value: "false"},
-		},
-		TargetConfig: &gwnodes.TargetConfig{
-			Backend: "ssh",
-			Host:    suite.target.Host,
-			Port:    22,
-			Secrets: []string{badSecretID.GetId()},
-		},
-	})
-	suite.Require().NoError(err)
+// 	fmt.Println("update node to run detect job with bad secret")
+// 	// call update on the node id, ensure detect job was triggered
+// 	_, err = gatewayNodesClient.Update(suite.ctx, &gwnodes.Node{
+// 		Id:   nodeID.Id,
+// 		Name: "test node",
+// 		Tags: []*query.Kv{
+// 			{Key: "_no_auto_detect", Value: "false"},
+// 		},
+// 		TargetConfig: &gwnodes.TargetConfig{
+// 			Backend: "ssh",
+// 			Host:    suite.target.Host,
+// 			Port:    22,
+// 			Secrets: []string{badSecretID.GetId()},
+// 		},
+// 	})
+// 	suite.Require().NoError(err)
 
-	fmt.Println("checking node status, expected unreachable")
-	nodeStatus := "unknown"
-	counter := 0
-	for nodeStatus == "unknown" {
-		// read gateway compliance nodes to ensure node was created, has a status of unreachable b/c we used bad secret
-		node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: nodeID.GetId()})
-		suite.Require().NoError(err)
-		nodeStatus = node.Status
-		counter++
-		time.Sleep(1 * time.Second)
-		if counter == 60 {
-			suite.T().Log("timed out waiting for node at line 112")
-			suite.T().Fail()
-		}
-	}
-	suite.Equal("unreachable", node.Status)
+// 	fmt.Println("checking node status, expected unreachable")
+// 	nodeStatus := "unknown"
+// 	counter := 0
+// 	for nodeStatus == "unknown" {
+// 		// read gateway compliance nodes to ensure node was created, has a status of unreachable b/c we used bad secret
+// 		node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: nodeID.GetId()})
+// 		suite.Require().NoError(err)
+// 		nodeStatus = node.Status
+// 		counter++
+// 		time.Sleep(1 * time.Second)
+// 		if counter == 60 {
+// 			suite.T().Log("timed out waiting for node at line 112")
+// 			suite.T().Fail()
+// 		}
+// 	}
+// 	suite.Equal("unreachable", node.Status)
 
-	fmt.Println("update node to run detect job with good secret")
-	// call update on the node id, ensure detect job was triggered
-	_, err = gatewayNodesClient.Update(suite.ctx, &gwnodes.Node{
-		Id:   nodeID.Id,
-		Name: "test node",
-		TargetConfig: &gwnodes.TargetConfig{
-			Backend: "ssh",
-			Host:    suite.target.Host,
-			Port:    22,
-			Secrets: []string{secretID.GetId()},
-		},
-	})
-	suite.Require().NoError(err)
+// 	fmt.Println("update node to run detect job with good secret")
+// 	// call update on the node id, ensure detect job was triggered
+// 	_, err = gatewayNodesClient.Update(suite.ctx, &gwnodes.Node{
+// 		Id:   nodeID.Id,
+// 		Name: "test node",
+// 		TargetConfig: &gwnodes.TargetConfig{
+// 			Backend: "ssh",
+// 			Host:    suite.target.Host,
+// 			Port:    22,
+// 			Secrets: []string{secretID.GetId()},
+// 		},
+// 	})
+// 	suite.Require().NoError(err)
 
-	fmt.Println("checking node status, expected reachable, with completed job data")
+// 	fmt.Println("checking node status, expected reachable, with completed job data")
 
-	nodeStatus = "unreachable"
-	counter = 0
-	for nodeStatus == "unreachable" {
-		node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: nodeID.Id})
-		suite.Require().NoError(err)
-		nodeStatus = node.Status
-		counter++
-		time.Sleep(1 * time.Second)
-		if counter == 120 {
-			suite.T().Log("timed out waiting for node at line 143")
-			suite.T().Fail()
-		}
-	}
+// 	nodeStatus = "unreachable"
+// 	counter = 0
+// 	for nodeStatus == "unreachable" {
+// 		node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: nodeID.Id})
+// 		suite.Require().NoError(err)
+// 		nodeStatus = node.Status
+// 		counter++
+// 		time.Sleep(1 * time.Second)
+// 		if counter == 120 {
+// 			suite.T().Log("timed out waiting for node at line 143")
+// 			suite.T().Fail()
+// 		}
+// 	}
 
-	node.LastJob.JobId = ""
-	node.LastJob.EndTime = nil
-	node.LastJob.StartTime = nil
-	suite.Equal(nodes.ResultsRow{
-		NodeId: nodeID.Id,
-		Status: "completed",
-	}, *node.LastJob)
+// 	node.LastJob.JobId = ""
+// 	node.LastJob.EndTime = nil
+// 	node.LastJob.StartTime = nil
+// 	suite.Equal(nodes.ResultsRow{
+// 		NodeId: nodeID.Id,
+// 		Status: "completed",
+// 	}, *node.LastJob)
 
-	tc := *node.TargetConfig
-	suite.Equal(1, len(tc.KeyFiles))
-	tc.KeyFiles = []string{}
-	suite.Equal(nodes.TargetConfig{
-		Backend:  "ssh",
-		Host:     suite.target.Host,
-		Port:     22,
-		Secrets:  []string{secretID.GetId()},
-		User:     suite.target.User,
-		KeyFiles: []string{},
-	}, tc)
+// 	tc := *node.TargetConfig
+// 	suite.Equal(1, len(tc.KeyFiles))
+// 	tc.KeyFiles = []string{}
+// 	suite.Equal(nodes.TargetConfig{
+// 		Backend:  "ssh",
+// 		Host:     suite.target.Host,
+// 		Port:     22,
+// 		Secrets:  []string{secretID.GetId()},
+// 		User:     suite.target.User,
+// 		KeyFiles: []string{},
+// 	}, tc)
 
-	node.LastContact = nil
-	node.LastJob = nil
-	node.TargetConfig = nil
-	passert.Equal(suite.T(), &nodes.Node{
-		ConnectionError: "authentication failed\n\nAuthentication failed for inspec-target-rhel7-dev.cd.chef.co\n\nNet::SSH::AuthenticationFailed",
-		Id:              nodeID.Id,
-		Name:            "test node",
-		Manager:         "automate",
-		Platform:        "redhat",
-		PlatformVersion: "7.7",
-		ManagerIds:      []string{"e69dc612-7e67-43f2-9b19-256afd385820"},
-		Status:          "reachable",
-		State:           "RUNNING",
-		ScanData:        &nodes.LastContactData{},
-		RunData:         &nodes.LastContactData{},
-		CloudInfo:       &nodes.CloudInfo{},
-	}, node)
+// 	node.LastContact = nil
+// 	node.LastJob = nil
+// 	node.TargetConfig = nil
+// 	passert.Equal(suite.T(), &nodes.Node{
+// 		ConnectionError: "authentication failed\n\nAuthentication failed for inspec-target-rhel7-dev.cd.chef.co\n\nNet::SSH::AuthenticationFailed",
+// 		Id:              nodeID.Id,
+// 		Name:            "test node",
+// 		Manager:         "automate",
+// 		Platform:        "redhat",
+// 		PlatformVersion: "7.7",
+// 		ManagerIds:      []string{"e69dc612-7e67-43f2-9b19-256afd385820"},
+// 		Status:          "reachable",
+// 		State:           "RUNNING",
+// 		ScanData:        &nodes.LastContactData{},
+// 		RunData:         &nodes.LastContactData{},
+// 		CloudInfo:       &nodes.CloudInfo{},
+// 	}, node)
 
-	jobsList, err = jobsClient.List(suite.ctx, &jobs.Query{})
-	suite.Require().NoError(err)
-	suite.Equal(int32(2), jobsList.Total)
+// 	jobsList, err = jobsClient.List(suite.ctx, &jobs.Query{})
+// 	suite.Require().NoError(err)
+// 	suite.Equal(int32(2), jobsList.Total)
 
-	fmt.Println("call rerun on the node to ensure detect job is created")
-	// call rerun on the node id, ensure another detect job was triggered
-	_, err = gatewayNodesClient.Rerun(suite.ctx, &gwnodes.Id{Id: nodeID.Id})
-	suite.Require().NoError(err)
+// 	fmt.Println("call rerun on the node to ensure detect job is created")
+// 	// call rerun on the node id, ensure another detect job was triggered
+// 	_, err = gatewayNodesClient.Rerun(suite.ctx, &gwnodes.Id{Id: nodeID.Id})
+// 	suite.Require().NoError(err)
 
-	jobsList, err = jobsClient.List(suite.ctx, &jobs.Query{})
-	suite.Require().NoError(err)
-	suite.Equal(int32(3), jobsList.Total)
+// 	jobsList, err = jobsClient.List(suite.ctx, &jobs.Query{})
+// 	suite.Require().NoError(err)
+// 	suite.Equal(int32(3), jobsList.Total)
 
-	fmt.Println("create node with bulk create endpoint, no auto detect")
-	// test bulk create - no detect job
-	ids, err := gatewayNodesClient.BulkCreate(suite.ctx, &gwnodes.Nodes{
-		Nodes: []*gwnodes.Node{{
-			Name: "test node",
-			Tags: []*query.Kv{
-				{Key: "_no_auto_detect", Value: "true"},
-			},
-			TargetConfig: &gwnodes.TargetConfig{
-				Backend: "ssh",
-				Hosts:   []string{suite.target.Host},
-				Port:    22,
-				Secrets: []string{secretID.GetId()},
-			},
-		}},
-	})
-	suite.Require().NoError(err)
-	suite.Equal(1, len(ids.GetIds()))
+// 	fmt.Println("create node with bulk create endpoint, no auto detect")
+// 	// test bulk create - no detect job
+// 	ids, err := gatewayNodesClient.BulkCreate(suite.ctx, &gwnodes.Nodes{
+// 		Nodes: []*gwnodes.Node{{
+// 			Name: "test node",
+// 			Tags: []*query.Kv{
+// 				{Key: "_no_auto_detect", Value: "true"},
+// 			},
+// 			TargetConfig: &gwnodes.TargetConfig{
+// 				Backend: "ssh",
+// 				Hosts:   []string{suite.target.Host},
+// 				Port:    22,
+// 				Secrets: []string{secretID.GetId()},
+// 			},
+// 		}},
+// 	})
+// 	suite.Require().NoError(err)
+// 	suite.Equal(1, len(ids.GetIds()))
 
-	fmt.Println("checking node status, expected unknown")
-	node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: ids.GetIds()[0]})
-	suite.Require().NoError(err)
-	suite.Equal("unknown", node.Status)
+// 	fmt.Println("checking node status, expected unknown")
+// 	node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: ids.GetIds()[0]})
+// 	suite.Require().NoError(err)
+// 	suite.Equal("unknown", node.Status)
 
-	fmt.Println("create node with bulk create endpoint, with auto detect")
-	// test bulk create - with detect job
-	ids, err = gatewayNodesClient.BulkCreate(suite.ctx, &gwnodes.Nodes{
-		Nodes: []*gwnodes.Node{{
-			Name: "test node",
-			Tags: []*query.Kv{},
-			TargetConfig: &gwnodes.TargetConfig{
-				Backend: "ssh",
-				Hosts:   []string{suite.target.Host},
-				Port:    22,
-				Secrets: []string{secretID.GetId()},
-			},
-		}},
-	})
-	suite.Require().NoError(err)
-	suite.Equal(1, len(ids.GetIds()))
+// 	fmt.Println("create node with bulk create endpoint, with auto detect")
+// 	// test bulk create - with detect job
+// 	ids, err = gatewayNodesClient.BulkCreate(suite.ctx, &gwnodes.Nodes{
+// 		Nodes: []*gwnodes.Node{{
+// 			Name: "test node",
+// 			Tags: []*query.Kv{},
+// 			TargetConfig: &gwnodes.TargetConfig{
+// 				Backend: "ssh",
+// 				Hosts:   []string{suite.target.Host},
+// 				Port:    22,
+// 				Secrets: []string{secretID.GetId()},
+// 			},
+// 		}},
+// 	})
+// 	suite.Require().NoError(err)
+// 	suite.Equal(1, len(ids.GetIds()))
 
-	fmt.Println("checking node status, expected reachable")
-	nodeStatus = "unknown"
-	counter = 0
-	for nodeStatus == "unknown" {
-		// read gateway compliance nodes to ensure node was created, has a status of unreachable b/c we used bad secret
-		node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: ids.GetIds()[0]})
-		suite.Require().NoError(err)
-		nodeStatus = node.Status
-		counter++
-		time.Sleep(1 * time.Second)
-		if counter == 120 {
-			suite.T().Log("timed out waiting for node at line 250")
-			suite.T().Fail()
-		}
-	}
-	suite.Equal("reachable", node.Status)
-}
+// 	fmt.Println("checking node status, expected reachable")
+// 	nodeStatus = "unknown"
+// 	counter = 0
+// 	for nodeStatus == "unknown" {
+// 		// read gateway compliance nodes to ensure node was created, has a status of unreachable b/c we used bad secret
+// 		node, err = nodesClient.Read(suite.ctx, &nodes.Id{Id: ids.GetIds()[0]})
+// 		suite.Require().NoError(err)
+// 		nodeStatus = node.Status
+// 		counter++
+// 		time.Sleep(1 * time.Second)
+// 		if counter == 120 {
+// 			suite.T().Log("timed out waiting for node at line 250")
+// 			suite.T().Fail()
+// 		}
+// 	}
+// 	suite.Equal("reachable", node.Status)
+// }
diff --git a/e2e/cypress/integration/api/iam/projects_api_scanjob_ingestion.spec.ts b/e2e/cypress/integration/api/iam/projects_api_scanjob_ingestion.spec.ts
index 5c6d97e47..6546cfe15 100644
--- a/e2e/cypress/integration/api/iam/projects_api_scanjob_ingestion.spec.ts
+++ b/e2e/cypress/integration/api/iam/projects_api_scanjob_ingestion.spec.ts
@@ -141,7 +141,7 @@ describe('ScanJob Ingestion project tagging', () => {
     });
   });
 
-  it('when a project has a rule that matches a node\'s environment' +
+  it.skip('when a project has a rule that matches a node\'s environment' +
   ' successfully associates that node with the project', () => {
 
     // Create a secret
diff --git a/integration/helpers/ssl_tests.sh b/integration/helpers/ssl_tests.sh
index 8749c4deb..eb1ec1328 100644
--- a/integration/helpers/ssl_tests.sh
+++ b/integration/helpers/ssl_tests.sh
@@ -105,6 +105,6 @@ fixup_testssl() {
 write_host_file() {
     HOSTFILE=$(mktemp)
     # temporarily remove port 10120 from list until it is a running service
-    chef-automate dev ports list | awk "/https|grpc/ { print \"127.0.0.1:\" \$3 }" > "$HOSTFILE"
+    chef-automate dev ports list | awk "/https|grpc|nats-gateway/ { print \"127.0.0.1:\" \$3 }" > "$HOSTFILE"
     echo "$HOSTFILE"
 }
diff --git a/lib/config/resolve_config_parser_test.go b/lib/config/resolve_config_parser_test.go
new file mode 100644
index 000000000..655ae9989
--- /dev/null
+++ b/lib/config/resolve_config_parser_test.go
@@ -0,0 +1,81 @@
+package config_test
+
+import (
+	"testing"
+
+	"github.com/chef/automate/lib/config"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestParseNameServers(t *testing.T) {
+	cases := []struct {
+		desc     string
+		content  []byte
+		expected []string
+	}{
+		{
+			desc: "parses IPv4 nameserver",
+			content: []byte(`
+nameserver 10.0.0.3
+	`),
+			expected: []string{"10.0.0.3"},
+		},
+		{
+			desc: "parses IPv6 nameserver",
+			content: []byte(`
+	nameserver 2001:0db8:85a3:0000:0000:8a2e:0370:7334
+	`),
+			expected: []string{"2001:0db8:85a3:0000:0000:8a2e:0370:7334"},
+		},
+		{
+			desc: "parses spaces between nameserver and IP",
+			content: []byte(`
+nameserver                          10.0.0.3
+	`),
+			expected: []string{"10.0.0.3"},
+		},
+		{
+			desc: "parses multiple nameservers",
+			content: []byte(`
+	nameserver 10.0.0.3
+	nameserver 10.0.0.4
+	`),
+			expected: []string{"10.0.0.3", "10.0.0.4"},
+		},
+		{
+			desc: "parses content with single-line comments",
+			content: []byte(`
+	# Here goes the name servers
+	nameserver 10.0.0.3
+	`),
+			expected: []string{"10.0.0.3"},
+		},
+		{
+			desc: "parses content with same-line comments",
+			content: []byte(`
+	nameserver 10.0.0.3 # Here goes the name servers
+	`),
+			expected: []string{"10.0.0.3"},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.desc, func(t *testing.T) {
+			ns := config.ParseNameServers(c.content)
+			assert.Equal(t, c.expected, ns)
+
+		})
+	}
+}
+
+func TestGetNameServersFromResolveConfig(t *testing.T) {
+	var filePath = "./testdata/sample_resolv_conf"
+	ns, err := config.GetNameServersFromResolveConfig(filePath)
+	require.NoError(t, err, "failed to parse config")
+
+	var expectedResult = []string{"255.0.0.1"}
+
+	require.Equal(t, expectedResult, ns)
+}
diff --git a/lib/config/testdata/sample_resolv_conf b/lib/config/testdata/sample_resolv_conf
new file mode 100644
index 000000000..d2ac5fd1d
--- /dev/null
+++ b/lib/config/testdata/sample_resolv_conf
@@ -0,0 +1,19 @@
+# This file is managed by man:systemd-resolved(8). Do not edit.
+#
+# This is a dynamic resolv.conf file for connecting local clients to the
+# internal DNS stub resolver of systemd-resolved. This file lists all
+# configured search domains.
+#
+# Run "resolvectl status" to see details about the uplink DNS servers
+# currently in use.
+#
+# Third party programs must not access this file directly, but only through the
+# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
+# replace this symlink by a static file or a different symlink.
+#
+# See man:systemd-resolved.service(8) for details about the supported modes of
+# operation for /etc/resolv.conf.
+
+nameserver 255.0.0.1
+options edns0
+search us-west-2.compute.internal
\ No newline at end of file