Skip to content

Commit 0fcc569

Browse files
authored
Electron breaking changes (#196)
* 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.
1 parent d868847 commit 0fcc569

File tree

11 files changed

+197
-2061
lines changed

11 files changed

+197
-2061
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

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

5+
## [5.0.0](https://github.com/Netflix-Skunkworks/stethoscope-app/tree/v5.0.0)
6+
7+
Fix auto update issue, caused by undocumented changes in Electron
8+
9+
### Changed
10+
- stethoscope://update deep link now forces update without prompting
11+
12+
## [4.0.2](https://github.com/Netflix-Skunkworks/stethoscope-app/tree/v4.0.2)
13+
14+
Bug fixes
15+
16+
### Changed
17+
- Config files are loaded directly by React app now, removing XHR requests.
18+
- Ensure window is not destroyed before autoscanning
19+
520
## [4.0.1](https://github.com/Netflix-Skunkworks/stethoscope-app/tree/v4.0.1)
621

722
### Added

package.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Stethoscope",
3-
"version": "4.0.1",
3+
"version": "5.0.0",
44
"private": true,
55
"homepage": "./",
66
"author": "Netflix",
@@ -67,6 +67,7 @@
6767
"extend": "^3.0.2",
6868
"fast-glob": "^2.2.6",
6969
"generic-pool": "^3.4.2",
70+
"glob": "^7.1.6",
7071
"graphql": "^14.3.1",
7172
"graphql-server-express": "^1.2.0",
7273
"graphql-tools": "^2.12.0",
@@ -150,11 +151,15 @@
150151
"gatekeeperAssess": false,
151152
"hardenedRuntime": true,
152153
"entitlements": "entitlements.mac.plist",
153-
"entitlementsInherit": "entitlements.mac.plist"
154+
"entitlementsInherit": "entitlements.mac.plist",
155+
"extraResources": [
156+
"src/practices"
157+
]
154158
},
155159
"win": {
156160
"target": "nsis",
157161
"extraResources": [
162+
"src/practices",
158163
"bitlocker-status/bitlocker-status.exe",
159164
"bitlocker-status/bitlocker-status.exe.confg",
160165
"bitlocker-status/bitlocker-status.pdb",
@@ -173,7 +178,10 @@
173178
},
174179
"linux": {
175180
"target": "AppImage",
176-
"category": "System"
181+
"category": "System",
182+
"extraResources": [
183+
"src/practices"
184+
]
177185
},
178186
"protocols": [
179187
{

src/App.js

Lines changed: 67 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global fetch, Notification */
1+
/* global Notification */
22
import React, { Component } from 'react'
33
import Stethoscope from './lib/Stethoscope'
44
import Device from './Device'
@@ -13,11 +13,16 @@ import { HOST } from './constants'
1313
import appConfig from './config.json'
1414
import pkg from '../package.json'
1515
import ErrorMessage from './ErrorMessage'
16+
import yaml from 'js-yaml'
1617
import './App.css'
1718
const socket = openSocket(HOST)
19+
1820
// CRA doesn't like importing native node modules
1921
// have to use window.require AFAICT
2022
const os = window.require('os')
23+
const glob = window.require('glob')
24+
const { readFileSync } = window.require('fs')
25+
const path = window.require('path')
2126
const { shell, remote, ipcRenderer } = window.require('electron')
2227
const settings = window.require('electron-settings')
2328
const log = remote.getGlobal('log')
@@ -53,7 +58,11 @@ class App extends Component {
5358
this.setState({ recentHang: settings.get('recentHang', 0) > 1 })
5459
ipcRenderer.send('scan:init')
5560
// perform the initial policy load & scan
56-
await this.loadPractices()
61+
try {
62+
await this.loadPractices()
63+
} catch (e) {
64+
console.error('Unable to load practices')
65+
}
5766
// flag ensures the download:start event isn't sent multiple times
5867
this.downloadStartSent = false
5968
// handle context menu
@@ -63,7 +72,8 @@ class App extends Component {
6372
// handles any errors that occur when updating (restores window size, etc.)
6473
ipcRenderer.on('download:error', this.onDownloadError)
6574
// trigger scan from main process
66-
ipcRenderer.on('autoscan:start', ({ notificationOnViolation = false }) => {
75+
ipcRenderer.on('autoscan:start', (args = {}) => {
76+
// const { notificationOnViolation = false } = args
6777
if (!this.state.scanIsRunning) {
6878
ipcRenderer.send('scan:init')
6979
if (Object.keys(this.state.policy).length) {
@@ -196,30 +206,33 @@ class App extends Component {
196206
* using them
197207
*/
198208
loadPractices = () => {
199-
this.setState({ loading: true }, () => {
200-
const files = ['config', 'policy', 'instructions']
201-
const promises = files.map(item =>
202-
fetch(`${HOST}/${item}`)
203-
.then(async res => {
204-
if (!res.ok) {
205-
log.error(`Unable to locate ${item}`)
206-
const response = await res.json()
207-
throw new Error(response.error || `Unable to locate ${item}`)
209+
return new Promise((resolve, reject) =>
210+
this.setState({ loading: true }, () => {
211+
const process = remote.process
212+
const dev = process.env.STETHOSCOPE_ENV === 'development'
213+
const basePath = `${dev ? '.' : process.resourcesPath}/src/practices`
214+
215+
glob(`${basePath}/*.yaml`, (err, files) => {
216+
if (err || !files.length) {
217+
reject(err)
218+
}
219+
const configs = {}
220+
files.forEach(filePath => {
221+
const parts = path.parse(filePath)
222+
const handle = readFileSync(filePath, 'utf8')
223+
configs[parts.name.split('.').shift()] = yaml.safeLoad(handle)
224+
})
225+
226+
this.setState({ ...configs, loading: false }, () => {
227+
if (!this.state.scanIsRunning) {
228+
this.handleScan()
208229
}
209-
return res
210230
})
211-
.then(res => res.json())
212-
.catch(this.handleErrorYAML)
213-
)
214231

215-
Promise.all(promises).then(([config, policy, instructions]) => {
216-
this.setState({ config, policy, instructions }, () => {
217-
if (!this.state.scanIsRunning) {
218-
this.handleScan()
219-
}
232+
resolve()
220233
})
221-
}).catch(this.handleErrorGraphQL)
222-
})
234+
})
235+
)
223236
}
224237

225238
/**
@@ -322,33 +335,37 @@ class App extends Component {
322335
// assume no errors and loaded state
323336
if (!content) {
324337
const args = [policy, result, device, instructions.practices, platform]
325-
const secInfo = Stethoscope.partitionSecurityInfo(...args)
326-
const decoratedDevice = Object.assign({}, device, secInfo, { lastScanTime })
327-
const lastScanFriendly = moment(lastScanTime).fromNow()
328-
329-
content = (
330-
<div>
331-
<Device
332-
{...decoratedDevice}
333-
org={instructions.organization}
334-
scanResult={result}
335-
strings={instructions.strings}
336-
policy={policy}
337-
lastScanTime={lastScanFriendly}
338-
lastScanDuration={lastScanDuration}
339-
scannedBy={scannedBy}
340-
onExpandPolicyViolation={this.handleHighlightRescanButton}
341-
/>
342-
<Footer
343-
highlightRescan={highlightRescan}
344-
result={result}
345-
instructions={instructions}
346-
webScopeLink={appConfig.stethoscopeWebURI}
347-
onClickOpen={this.handleOpenExternal}
348-
onRescan={this.handleScan}
349-
/>
350-
</div>
351-
)
338+
try {
339+
const secInfo = Stethoscope.partitionSecurityInfo(...args)
340+
const decoratedDevice = Object.assign({}, device, secInfo, { lastScanTime })
341+
const lastScanFriendly = moment(lastScanTime).fromNow()
342+
343+
content = (
344+
<div>
345+
<Device
346+
{...decoratedDevice}
347+
org={instructions.organization}
348+
scanResult={result}
349+
strings={instructions.strings}
350+
policy={policy}
351+
lastScanTime={lastScanFriendly}
352+
lastScanDuration={lastScanDuration}
353+
scannedBy={scannedBy}
354+
onExpandPolicyViolation={this.handleHighlightRescanButton}
355+
/>
356+
<Footer
357+
highlightRescan={highlightRescan}
358+
result={result}
359+
instructions={instructions}
360+
webScopeLink={appConfig.stethoscopeWebURI}
361+
onClickOpen={this.handleOpenExternal}
362+
onRescan={this.handleScan}
363+
/>
364+
</div>
365+
)
366+
} catch (e) {
367+
throw new Error(`Unable to partition data: ${e.message}\n${args.join(', ')}`)
368+
}
352369
}
353370

354371
return (

src/Footer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function Footer (props) {
1717
onClick={onRescan}
1818
>
1919
<span className='icon icon-arrows-ccw' />
20-
{instructions.strings.rescanButton}
20+
{instructions && instructions.strings && instructions.strings.rescanButton}
2121
</button>
2222
{webScopeLink && (
2323
<button

src/__tests__/test-build.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,11 @@ async function main () {
102102
if (config.testHosts && Array.isArray(config.testHosts)) {
103103
for (const { url, label } of config.testHosts) {
104104
const response = await scan(url)
105-
const timing = Math.round(response.extensions.timing.total / 1000 * 100) / 100
106105
if (response !== false) {
106+
const timing = Math.round(response.extensions.timing.total / 1000 * 100) / 100
107107
console.log(chalk.green('✓'), `[Remote:${label}]\tscan from test URL '${url}' successful\t${chalk.yellow(`${timing} seconds`)}`)
108+
} else {
109+
console.log(chalk.red('x'), `${url} - ${label} failed`)
108110
}
109111
}
110112
}

src/lib/Stethoscope.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* global fetch */
22
import { HOST, PASS, FAIL } from '../constants'
3+
const { remote } = window.require('electron')
4+
const log = remote.getGlobal('log')
35

46
const handleValidate = (result, partitions, device, practices, platform) => {
57
const { status, ...rest } = result
@@ -16,14 +18,21 @@ const handleValidate = (result, partitions, device, practices, platform) => {
1618
}
1719
}
1820

19-
const item = {
20-
title: key,
21-
name: key,
22-
status: itemStatus,
23-
actions: '',
24-
link: '',
25-
...practices[key],
26-
directions: practices[key].directions[platform]
21+
let item = {}
22+
23+
try {
24+
item = {
25+
title: key,
26+
name: key,
27+
status: itemStatus,
28+
actions: '',
29+
link: '',
30+
...practices[key],
31+
directions: practices[key].directions[platform]
32+
}
33+
} catch (e) {
34+
log.error(`Stethoscope:handleValidate - ${e.message}`)
35+
return acc
2736
}
2837

2938
if (Array.isArray(rest[key])) {

src/resolvers/platform/LinuxDevice.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ export default {
1010
async disks (root, args, context) {
1111
const { disks } = await kmd('disks', context)
1212
// ignore spammy squashfs volumes, typically generated by Snap applications
13-
const volumes = (disks.volumes || []).filter(vol => vol.type != 'squashfs')
13+
const volumes = (disks.volumes || []).filter(vol => vol.type !== 'squashfs')
1414

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

1818
return volumes
1919
}

src/resolvers/platform/WindowsSecurity.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import semver from '../../lib/patchedSemver'
2-
import Device from '../platform/WindowsDevice'
32
import kmd from '../../lib/kmd'
43
import { UNKNOWN, DEFAULT_WIN32_APP_REGISTRY_PATH } from '../../constants'
54

@@ -47,7 +46,6 @@ export default {
4746
const chargingTimeout = parseInt(lock.chargingTimeout, 10)
4847
const batteryTimeout = parseInt(lock.batteryTimeout, 10)
4948

50-
5149
return (
5250
// According to Windows: 0 = Never
5351
chargingTimeout !== 0 &&

0 commit comments

Comments
 (0)