From eb3af5f6380a93e080aa5e9dd8c4bd554fae6dd4 Mon Sep 17 00:00:00 2001 From: Rob McVey Date: Mon, 27 Aug 2018 16:40:10 -0700 Subject: [PATCH] added policy documentation, fixed styles on required application practice, fixed issues with handling of required applications. Added instructions for required apps. --- docs/POLICIES.md | 208 +++++++++++++++++++++++++++++- practices/instructions.en.yaml | 16 +++ resolvers/platform/MacSecurity.js | 2 +- src/Action.js | 2 +- src/Device.css | 26 ++++ src/Device.js | 46 ++++++- src/start.js | 2 +- 7 files changed, 296 insertions(+), 6 deletions(-) diff --git a/docs/POLICIES.md b/docs/POLICIES.md index 65b3dba3..bf894737 100644 --- a/docs/POLICIES.md +++ b/docs/POLICIES.md @@ -1 +1,207 @@ -// todo +# Policies + +Policies are a declarative way to describe your organization's security recommendations for endpoint devices. The default policy for Stethoscope can be found in the practices directory [default policy](https://github.com/Netflix-Skunkworks/stethoscope-app/blob/master/practices/policy.yaml). + +Though [osquery](https://osquery.io/) (the main mechanism Stethoscope uses to query device details) provides read access to [most aspects of a device](https://osquery.io/schema/3.2.6), we have chosen to disallow raw queries and limit queries to a predefined set of attributes and schemas to prevent potential abuse. + +If you would like access to data that osquery provides but Stethoscope does not, please file an issue. + +## Specifying Requirements + +The specifics are explained in more detail below, but generally there are two main ways to express a requirement: + + * [Semver](https://semver.org/) string (or platform-specific semver strings) + * `RequirementOption` + +### Semver Strings + +[Semver](https://semver.org/) (Semantic Versioning) is a common versioning practice that uses MAJOR.MINOR.PATCH format (e.g. 10.13.2). Semver supports comparisons and logical operators, and the policy allows any valid semver for comparison. + +**platform-specific semver** + +Some requirements (software versions, os verisons) are dependent on the platform. For these cases we have special GraphQL schemas defined: + +```graphql +# specify one semver requirement per platform +input PlatformStringRequirement { + darwin: Semver + win32: Semver + ubuntu: Semver + linux: Semver + all: Semver +} + +# set bracketed (ok, nudge) requirements for platforms +input PlatformBracketRequirement { + darwin: VersionBracket + win32: VersionBracket + ubuntu: VersionBracket + linux: VersionBracket + all: VersionBracket +} +# defines the acceptable range of versions +input VersionBracket { + ok: Semver + nudge: Semver +} +``` + +### RequirementOption + +The `RequirementOption` enum contains the following `ALWAYS`, `SUGGESTED`, `NEVER`, `IF_SUPPORTED` [schema](https://github.com/Netflix-Skunkworks/stethoscope-app/blob/master/schema.graphql#L251-L256) + + If the requirement is `ALWAYS`, the user will only pass this practice if their setting is enabled + + If the requirement is `SUGGESTED`, the user will pass whether or not the setting is enabled, but will receive a nudge (yellow) suggesting they enable it. + + If the requirement is `NEVER`, the user will only pass if the setting is disabled. + + If the requirement is `IF_SUPPORTED`, the user will only pass if their platform supports the practice (and reliable querying of the practice) + +## Understanding Responses + +Scans return a JSON object with an overall status and individual practice status. Valid return values for a scan/practice are: `PASS`, `NUDGE`, and `FAIL`. + +e.g. +```json +{ + "data": { + "policy": { + "validate": { + "status": "PASS", + "osVersion": "PASS", + "firewall": "PASS", + "diskEncryption": "PASS", + "automaticUpdates": "PASS", + "screenLock": "PASS", + "remoteLogin": "PASS", + "stethoscopeVersion": "PASS", + "requiredApplications": [ + { + "name": "Google Chrome", + "status": "PASS" + } + ] + } + } + } +} +``` + +--- + +## Supported Practices + +### `os version` (yaml) | `osVersion` (json) + +OS Version allows you to specify what operation system version the user is running, based on the platform the app is running on. + +The requirement is specified to each platform and uses `semver` strings to specify version cutoffs. If `ok` matches the user's os version, they will show as passing this practice. If `nudge` matches the user's os version, they will see a yellow warning. If the user's version doesn't match `ok` or `nudge`, they will be shown a red warning. + +**Example os version policy (yaml)** + +```yaml +os version: + darwin: + # High Sierra + ok: ">=10.13.6" + # Sierra + nudge: ">=10.12.6" + win32: + # Version 1803 - Redstone 3 Fall Creators Update + ok: ">=10.0.16299" + # Version 1703 - Redstone 2 Creators Update + nudge: ">=10.0.15063" + ubuntu: + ok: ">=18.4.0" + nudge: ">=18.0.2" +``` +**Example os version policy (JSON)** + +```json +{ + "osVersion": { + "darwin": { + "ok": ">=10.13.6", + "nudge": ">=10.12.6" + }, + "win32": { + "ok": ">=10.0.16299", + "nudge": ">=10.0.15063" + }, + "ubuntu": { + "ok": ">=18.4.0", + "nudge": ">=18.0.2" + } + }, +} +``` + +You can use more complex semver strings if you want to warn on a specific OS version. + +### `firewall` (yaml) | `firewall` (json) + +Firewall checks the local firewall state using the default firewall provider in each platform (and `iptables` on linux). This practice uses the `RequirementOption` enum to specify the requirement. +. +Valid values are: `ALWAYS`, `SUGGESTED`, `NEVER`, `IF_SUPPORTED` + +### `disk encryption` (yaml) | `diskEncryption` (json) + +Disk encryption enumerates mounted drives and checks their encryption status using FileVault (mac), BitLocker (windows), and LUKS (linux). This practice uses the `RequirementOption` enum to specify the requirement. + +Valid values are: `ALWAYS`, `SUGGESTED`, `NEVER`, `IF_SUPPORTED` + +### `automatic updates` (yaml) | `automaticUpdates` (json) + +The automatic updates practice checks that the user has automatic updates enabled on their machine through `plist` values and service state (running). This practice uses the `RequirementOption` enum to specify the requirement. + +Valid values are: `ALWAYS`, `SUGGESTED`, `NEVER`, `IF_SUPPORTED` + +### `screen lock` (yaml) | `screenLock` (json) + +Does not work on El Capitan or higher as this setting was moved to the keychain and is not accessible. This practice uses the `RequirementOption` enum to specify the requirement. + +Valid values are: `ALWAYS`, `SUGGESTED`, `NEVER`, `IF_SUPPORTED` + +### `remote login` (yaml) | `remoteLogin` (json) + +Checks that remote login (RDP, SSH) is disabled for the device. This practice uses the `RequirementOption` enum to specify the requirement. + +Valid values are: `ALWAYS`, `SUGGESTED`, `NEVER`, `IF_SUPPORTED` + +### `required applications` (yaml) | `requiredApplications` (json) + +Application requirements have their own GraphQL schema: + +```graphql +# Defines a requirement for an installed application +input AppRequirement { + # required application name + name: String! + # optional application version requirement + version: Semver + # optional platform if only required for specific OS + platform: PlatformStringRequirement + # controls whether regex or equality check is performed against application name + exactMatch: Boolean + # controls whether bin packages are checked (homebrew, chocolatey, etc) + includePackages: Boolean + # install URL + url: String + # explanation to show user + description: String +} +``` + +You can specify requirements as an array of `AppRequirements`. e.g. + +```json +{ + "requiredApplications": [{ + "name": "Google Chrome", + "version": ">=68.0.3440", + "url": "https://www.google.com/chrome/", + "description": "Google Chrome is a secure browser..." + }] +} +``` diff --git a/practices/instructions.en.yaml b/practices/instructions.en.yaml index 8442b987..8e792607 100644 --- a/practices/instructions.en.yaml +++ b/practices/instructions.en.yaml @@ -199,3 +199,19 @@ practices: 1. Click "Restart now" {{requirement "osVersion" "win32"}} + + requiredApplications: + title: > + {{#if passing}} + Required applications are installed + {{else}} + Missing required applications + {{/if}} + description: > + Certain applications have been designated as necessary by your organization. + directions: + darwin: | + 1. Please install the following applications: + + win32: | + 1. Please install the following applications diff --git a/resolvers/platform/MacSecurity.js b/resolvers/platform/MacSecurity.js index 9ca9a8d9..e5b3048e 100644 --- a/resolvers/platform/MacSecurity.js +++ b/resolvers/platform/MacSecurity.js @@ -1,5 +1,5 @@ const semver = require('semver') -const Device = require('../Device') +const Device = require('./MacDevice') const OSQuery = require('../../sources/osquery') const { getScreenLock } = require('../../src/lib/applescript') const pkg = require('../../package.json') diff --git a/src/Action.js b/src/Action.js index 9c61c88d..43164ab6 100644 --- a/src/Action.js +++ b/src/Action.js @@ -141,7 +141,6 @@ class Action extends Component {
{action.description} - {this.props.children}
{ action.details &&
{action.details}
@@ -156,6 +155,7 @@ class Action extends Component { dangerouslySetInnerHTML={{__html: this.parseDirections()}} /> )} + {this.props.children}
) } diff --git a/src/Device.css b/src/Device.css index e1829509..98ac5d66 100644 --- a/src/Device.css +++ b/src/Device.css @@ -20,6 +20,7 @@ font-size: 12px; color: #aaa; margin-top: 15px; + margin-bottom: 15px; } ul.mac-addresses { padding: 0; @@ -218,3 +219,28 @@ a.btn:hover { .panel.device-summary.warning span { color: #5d4a19; } + +.result-list { + margin-left: 20px; + margin-bottom: 20px; +} +.result-list-item hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #efefef; + margin: 1em 0; + padding: 0; +} +.result-heading { + display: flex; + align-items: baseline; + width: 90%; + justify-content: space-between; +} +.result-list-item { + display: flex; + align-items: baseline; + flex-direction: column; + justify-content: space-between; +} diff --git a/src/Device.js b/src/Device.js index c95f4f64..36adde3f 100644 --- a/src/Device.js +++ b/src/Device.js @@ -54,11 +54,53 @@ class Device extends Component { security: this.props.security } - if (action.results) { + const hasResults = Array.isArray(action.results) + let results = action.results + + if (hasResults && action.name.endsWith('Applications')) { + results = results.map(result => { + if (action.name in this.props.policy && Array.isArray(this.props.policy[action.name])) { + const target = this.props.policy[action.name].find(app => app.name === result.name) + if (target) { + return Object.assign({}, result, target) + } + return result + } + }) + } + + if (hasResults) { return ( ) diff --git a/src/start.js b/src/start.js index d5758a1a..a1b1194e 100644 --- a/src/start.js +++ b/src/start.js @@ -108,7 +108,7 @@ function createWindow () { // app.dock.show() // } - setTimeout(() => app.dock.hide(), 0) + if (!IS_DEV) setTimeout(() => app.dock.hide(), 0) if (process.platform === 'win32') { deeplinkingUrl = process.argv.slice(1)