Skip to content

Commit

Permalink
Electron breaking changes (#196)
Browse files Browse the repository at this point in the history
* use remote process to get env

* Window fixes to resolve autoscan crash when window is destroyed

* Fixing issue with validation assumptions in Stethoscope class

* added glob and bumped version

* Updated changelog

* Merge branch 'dialog-fix' into catalina-fixes

* Fixed issue with auto updater, added ability to force update without prompts

* Catch force update errors

* Don't set enabled on updater object when updating

* Fixed issue with spectron tests, fixed linter issues, major version change because 4.* is broken.
  • Loading branch information
rmcvey authored Mar 12, 2020
1 parent d868847 commit 0fcc569
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 2,061 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

All notable changes to this project will be documented in this file.

## [5.0.0](https://github.com/Netflix-Skunkworks/stethoscope-app/tree/v5.0.0)

Fix auto update issue, caused by undocumented changes in Electron

### Changed
- stethoscope://update deep link now forces update without prompting

## [4.0.2](https://github.com/Netflix-Skunkworks/stethoscope-app/tree/v4.0.2)

Bug fixes

### Changed
- Config files are loaded directly by React app now, removing XHR requests.
- Ensure window is not destroyed before autoscanning

## [4.0.1](https://github.com/Netflix-Skunkworks/stethoscope-app/tree/v4.0.1)

### Added
Expand Down
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Stethoscope",
"version": "4.0.1",
"version": "5.0.0",
"private": true,
"homepage": "./",
"author": "Netflix",
Expand Down Expand Up @@ -67,6 +67,7 @@
"extend": "^3.0.2",
"fast-glob": "^2.2.6",
"generic-pool": "^3.4.2",
"glob": "^7.1.6",
"graphql": "^14.3.1",
"graphql-server-express": "^1.2.0",
"graphql-tools": "^2.12.0",
Expand Down Expand Up @@ -150,11 +151,15 @@
"gatekeeperAssess": false,
"hardenedRuntime": true,
"entitlements": "entitlements.mac.plist",
"entitlementsInherit": "entitlements.mac.plist"
"entitlementsInherit": "entitlements.mac.plist",
"extraResources": [
"src/practices"
]
},
"win": {
"target": "nsis",
"extraResources": [
"src/practices",
"bitlocker-status/bitlocker-status.exe",
"bitlocker-status/bitlocker-status.exe.confg",
"bitlocker-status/bitlocker-status.pdb",
Expand All @@ -173,7 +178,10 @@
},
"linux": {
"target": "AppImage",
"category": "System"
"category": "System",
"extraResources": [
"src/practices"
]
},
"protocols": [
{
Expand Down
117 changes: 67 additions & 50 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global fetch, Notification */
/* global Notification */
import React, { Component } from 'react'
import Stethoscope from './lib/Stethoscope'
import Device from './Device'
Expand All @@ -13,11 +13,16 @@ import { HOST } from './constants'
import appConfig from './config.json'
import pkg from '../package.json'
import ErrorMessage from './ErrorMessage'
import yaml from 'js-yaml'
import './App.css'
const socket = openSocket(HOST)

// CRA doesn't like importing native node modules
// have to use window.require AFAICT
const os = window.require('os')
const glob = window.require('glob')
const { readFileSync } = window.require('fs')
const path = window.require('path')
const { shell, remote, ipcRenderer } = window.require('electron')
const settings = window.require('electron-settings')
const log = remote.getGlobal('log')
Expand Down Expand Up @@ -53,7 +58,11 @@ class App extends Component {
this.setState({ recentHang: settings.get('recentHang', 0) > 1 })
ipcRenderer.send('scan:init')
// perform the initial policy load & scan
await this.loadPractices()
try {
await this.loadPractices()
} catch (e) {
console.error('Unable to load practices')
}
// flag ensures the download:start event isn't sent multiple times
this.downloadStartSent = false
// handle context menu
Expand All @@ -63,7 +72,8 @@ class App extends Component {
// handles any errors that occur when updating (restores window size, etc.)
ipcRenderer.on('download:error', this.onDownloadError)
// trigger scan from main process
ipcRenderer.on('autoscan:start', ({ notificationOnViolation = false }) => {
ipcRenderer.on('autoscan:start', (args = {}) => {
// const { notificationOnViolation = false } = args
if (!this.state.scanIsRunning) {
ipcRenderer.send('scan:init')
if (Object.keys(this.state.policy).length) {
Expand Down Expand Up @@ -196,30 +206,33 @@ class App extends Component {
* using them
*/
loadPractices = () => {
this.setState({ loading: true }, () => {
const files = ['config', 'policy', 'instructions']
const promises = files.map(item =>
fetch(`${HOST}/${item}`)
.then(async res => {
if (!res.ok) {
log.error(`Unable to locate ${item}`)
const response = await res.json()
throw new Error(response.error || `Unable to locate ${item}`)
return new Promise((resolve, reject) =>
this.setState({ loading: true }, () => {
const process = remote.process
const dev = process.env.STETHOSCOPE_ENV === 'development'
const basePath = `${dev ? '.' : process.resourcesPath}/src/practices`

glob(`${basePath}/*.yaml`, (err, files) => {
if (err || !files.length) {
reject(err)
}
const configs = {}
files.forEach(filePath => {
const parts = path.parse(filePath)
const handle = readFileSync(filePath, 'utf8')
configs[parts.name.split('.').shift()] = yaml.safeLoad(handle)
})

this.setState({ ...configs, loading: false }, () => {
if (!this.state.scanIsRunning) {
this.handleScan()
}
return res
})
.then(res => res.json())
.catch(this.handleErrorYAML)
)

Promise.all(promises).then(([config, policy, instructions]) => {
this.setState({ config, policy, instructions }, () => {
if (!this.state.scanIsRunning) {
this.handleScan()
}
resolve()
})
}).catch(this.handleErrorGraphQL)
})
})
)
}

/**
Expand Down Expand Up @@ -322,33 +335,37 @@ class App extends Component {
// assume no errors and loaded state
if (!content) {
const args = [policy, result, device, instructions.practices, platform]
const secInfo = Stethoscope.partitionSecurityInfo(...args)
const decoratedDevice = Object.assign({}, device, secInfo, { lastScanTime })
const lastScanFriendly = moment(lastScanTime).fromNow()

content = (
<div>
<Device
{...decoratedDevice}
org={instructions.organization}
scanResult={result}
strings={instructions.strings}
policy={policy}
lastScanTime={lastScanFriendly}
lastScanDuration={lastScanDuration}
scannedBy={scannedBy}
onExpandPolicyViolation={this.handleHighlightRescanButton}
/>
<Footer
highlightRescan={highlightRescan}
result={result}
instructions={instructions}
webScopeLink={appConfig.stethoscopeWebURI}
onClickOpen={this.handleOpenExternal}
onRescan={this.handleScan}
/>
</div>
)
try {
const secInfo = Stethoscope.partitionSecurityInfo(...args)
const decoratedDevice = Object.assign({}, device, secInfo, { lastScanTime })
const lastScanFriendly = moment(lastScanTime).fromNow()

content = (
<div>
<Device
{...decoratedDevice}
org={instructions.organization}
scanResult={result}
strings={instructions.strings}
policy={policy}
lastScanTime={lastScanFriendly}
lastScanDuration={lastScanDuration}
scannedBy={scannedBy}
onExpandPolicyViolation={this.handleHighlightRescanButton}
/>
<Footer
highlightRescan={highlightRescan}
result={result}
instructions={instructions}
webScopeLink={appConfig.stethoscopeWebURI}
onClickOpen={this.handleOpenExternal}
onRescan={this.handleScan}
/>
</div>
)
} catch (e) {
throw new Error(`Unable to partition data: ${e.message}\n${args.join(', ')}`)
}
}

return (
Expand Down
2 changes: 1 addition & 1 deletion src/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function Footer (props) {
onClick={onRescan}
>
<span className='icon icon-arrows-ccw' />
{instructions.strings.rescanButton}
{instructions && instructions.strings && instructions.strings.rescanButton}
</button>
{webScopeLink && (
<button
Expand Down
4 changes: 3 additions & 1 deletion src/__tests__/test-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ async function main () {
if (config.testHosts && Array.isArray(config.testHosts)) {
for (const { url, label } of config.testHosts) {
const response = await scan(url)
const timing = Math.round(response.extensions.timing.total / 1000 * 100) / 100
if (response !== false) {
const timing = Math.round(response.extensions.timing.total / 1000 * 100) / 100
console.log(chalk.green('✓'), `[Remote:${label}]\tscan from test URL '${url}' successful\t${chalk.yellow(`${timing} seconds`)}`)
} else {
console.log(chalk.red('x'), `${url} - ${label} failed`)
}
}
}
Expand Down
25 changes: 17 additions & 8 deletions src/lib/Stethoscope.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* global fetch */
import { HOST, PASS, FAIL } from '../constants'
const { remote } = window.require('electron')
const log = remote.getGlobal('log')

const handleValidate = (result, partitions, device, practices, platform) => {
const { status, ...rest } = result
Expand All @@ -16,14 +18,21 @@ const handleValidate = (result, partitions, device, practices, platform) => {
}
}

const item = {
title: key,
name: key,
status: itemStatus,
actions: '',
link: '',
...practices[key],
directions: practices[key].directions[platform]
let item = {}

try {
item = {
title: key,
name: key,
status: itemStatus,
actions: '',
link: '',
...practices[key],
directions: practices[key].directions[platform]
}
} catch (e) {
log.error(`Stethoscope:handleValidate - ${e.message}`)
return acc
}

if (Array.isArray(rest[key])) {
Expand Down
4 changes: 2 additions & 2 deletions src/resolvers/platform/LinuxDevice.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ export default {
async disks (root, args, context) {
const { disks } = await kmd('disks', context)
// ignore spammy squashfs volumes, typically generated by Snap applications
const volumes = (disks.volumes || []).filter(vol => vol.type != 'squashfs')
const volumes = (disks.volumes || []).filter(vol => vol.type !== 'squashfs')

// set encrypted flag for LUKS volumes
volumes.forEach(vol => { vol.encrypted = vol.type == 'crypto_LUKS' });
volumes.forEach(vol => { vol.encrypted = vol.type === 'crypto_LUKS' });

return volumes
}
Expand Down
2 changes: 0 additions & 2 deletions src/resolvers/platform/WindowsSecurity.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import semver from '../../lib/patchedSemver'
import Device from '../platform/WindowsDevice'
import kmd from '../../lib/kmd'
import { UNKNOWN, DEFAULT_WIN32_APP_REGISTRY_PATH } from '../../constants'

Expand Down Expand Up @@ -47,7 +46,6 @@ export default {
const chargingTimeout = parseInt(lock.chargingTimeout, 10)
const batteryTimeout = parseInt(lock.batteryTimeout, 10)


return (
// According to Windows: 0 = Never
chargingTimeout !== 0 &&
Expand Down
Loading

0 comments on commit 0fcc569

Please sign in to comment.