Skip to content

Commit

Permalink
Add a way to view TLS certificates when error
Browse files Browse the repository at this point in the history
  • Loading branch information
darkdh committed Jul 15, 2016
1 parent 7da1bd2 commit 4ae87f8
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 4 deletions.
61 changes: 61 additions & 0 deletions app/extensions/brave/content/scripts/brave-about.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

(function () {
const ipcRenderer = chrome.ipc
window.addEventListener('dispatch-action', (e) => {
ipcRenderer.send('dispatch-action', e.detail)
})
window.addEventListener('change-setting', (e) => {
ipcRenderer.send('change-setting', e.detail.key, e.detail.value)
})
window.addEventListener('change-site-setting', (e) => {
ipcRenderer.send('change-site-setting', e.detail.hostPattern, e.detail.key, e.detail.value)
})
window.addEventListener('cert-error-accepted', (e) => {
ipcRenderer.send('cert-error-accepted', e.detail.url)
})
window.addEventListener('get-cert-error-detail', (e) => {
ipcRenderer.send('get-cert-error-detail', e.detail.url)
})
window.addEventListener('new-frame', (e) => {
ipcRenderer.sendToHost('new-frame', e.detail.frameOpts, e.detail.openInForeground)
})
window.addEventListener('context-menu-opened', (e) => {
ipcRenderer.sendToHost('context-menu-opened', e.detail.nodeProps, e.detail.contextMenuType)
})
window.addEventListener('move-site', (e) => {
ipcRenderer.send('move-site', e.detail.sourceDetail, e.detail.destinationDetail, e.detail.prepend, e.detail.destinationIsParent)
})
window.addEventListener('open-download-path', (e) => {
ipcRenderer.send('open-download-path', e.detail.download)
})
window.addEventListener('decrypt-password', (e) => {
ipcRenderer.send('decrypt-password', e.detail.encryptedPassword, e.detail.authTag, e.detail.iv, e.detail.id)
})
window.addEventListener('set-clipboard', (e) => {
ipcRenderer.send('set-clipboard', e.detail)
})
window.addEventListener('show-notification', (e) => {
ipcRenderer.send('show-notification', e.detail)
})
window.addEventListener('set-resource-enabled', (e) => {
ipcRenderer.send('set-resource-enabled', e.detail.resourceName, e.detail.enabled)
})
window.addEventListener('delete-password', (e) => {
ipcRenderer.send('delete-password', e.detail)
})
window.addEventListener('delete-password-site', (e) => {
ipcRenderer.send('delete-password-site', e.detail)
})
window.addEventListener('clear-passwords', (e) => {
ipcRenderer.send('clear-passwords')
})
window.addEventListener('request-language', (e) => {
ipcRenderer.send('request-language')
})
window.addEventListener('check-flash-installed', (e) => {
ipcRenderer.send('check-flash-installed')
})
}).apply(this)
1 change: 1 addition & 0 deletions app/extensions/brave/locales/en-US/app.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ certErrorText=This site cannot be loaded due to a certificate error:
certErrorAdvanced=Advanced settings
certErrorSafety=Back to safety
certErrorButtonText=Ignore certificate error (dangerous!)
certErrorShowCertificate=Show Certificate
safebrowsingError=Dangerous Site Blocked
safebrowsingErrorText=For your safety, Brave has blocked this site because it is distributing malware or stealing login credentials.
safebrowsingErrorAdvanced=Advanced
Expand Down
23 changes: 23 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ let lastWindowClosed = false

// Domains to accept bad certs for. TODO: Save the accepted cert fingerprints.
let acceptCertDomains = {}
let errorCerts = {}
// URLs to callback for auth.
let authCallbacks = {}
// Don't show the keytar prompt more than once per 24 hours
Expand Down Expand Up @@ -208,6 +209,15 @@ app.on('ready', () => {
return
}

errorCerts[url] = {
subjectName: cert.subjectName,
issuerName: cert.issuerName,
serialNumber: cert.serialNumber,
validStart: cert.validStart,
validExpiry: cert.validExpiry,
fingerprint: cert.fingerprint
}

// Tell the page to show an unlocked icon. Note this is sent to the main
// window webcontents, not the webview webcontents
let sender = webContents.hostWebContents || webContents
Expand Down Expand Up @@ -447,6 +457,19 @@ app.on('ready', () => {
}
})

ipcMain.on(messages.GET_CERT_ERROR_DETAIL, (event, url) => {
if (errorCerts[url]) {
event.sender.send(messages.SET_CERT_ERROR_DETAIL, {
subjectName: errorCerts[url].subjectName,
issuerName: errorCerts[url].issuerName,
serialNumber: errorCerts[url].serialNumber,
validStart: errorCerts[url].validStart,
validExpiry: errorCerts[url].validExpiry,
fingerprint: errorCerts[url].fingerprint
})
}
})

// save app state every 5 minutes regardless of update frequency
setInterval(initiateSessionStateSave, 1000 * 60 * 5)
AppStore.addChangeListener(() => {
Expand Down
9 changes: 9 additions & 0 deletions js/about/aboutActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ const AboutActions = {
ipc.send(messages.CERT_ERROR_ACCEPTED, url)
},

/**
* Get certificate detail when error.
*
* @param {string} url - The URL with the cert error
*/
getCertErrorDetail: function (url) {
ipc.send(messages.GET_CERT_ERROR_DETAIL, url)
},

/**
* Opens a context menu
*/
Expand Down
79 changes: 75 additions & 4 deletions js/about/certerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,67 @@ const React = require('react')
const Button = require('../components/button')
const aboutActions = require('./aboutActions')
const WindowConstants = require('../constants/windowConstants')
const messages = require('../constants/messages')
const ipc = window.chrome.ipc

require('../../less/button.less')
require('../../less/window.less')
require('../../less/about/error.less')

function toHexString (byteArray) {
return byteArray.map(function (byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2)
}).join(':')
}

function toByteArray (str) {
var bytes = []
for (var i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i))
}
return bytes
}

function seperateHex (hexStr) {
var result = []
for (var i = 0; i < hexStr.length; ++i) {
result += hexStr[i]
if (i % 2 && i !== hexStr.length - 1) {
result += ':'
}
}
return result
}

class CertErrorPage extends React.Component {
constructor () {
super()
this.state = {
advanced: false
advanced: false,
certDetail: false,
certIssuerName: null,
certSubjectName: null,
certSerialNumber: null,
certValidStart: null,
certValidExpiry: null,
certFingerprint: null
}

ipc.on(messages.SET_CERT_ERROR_DETAIL, (e, detail) => {
var validStart = new Date()
var validExpiry = new Date()
validStart.setTime(detail.validStart * 1000)
validExpiry.setTime(detail.validExpiry * 1000)
this.setState({
certDetail: true,
certIssuerName: detail.issuerName,
certSubjectName: detail.subjectName,
certSerialNumber: seperateHex(detail.serialNumber),
certValidStart: validStart.toString(),
certValidExpiry: validExpiry.toString(),
certFingerprint: detail.fingerprint.split('/')
})
})
}

onAccept () {
Expand All @@ -40,6 +90,10 @@ class CertErrorPage extends React.Component {
this.setState({advanced: true})
}

onDetail () {
aboutActions.getCertErrorDetail(this.state.url)
}

render () {
return <div className='errorContent'>
<svg width='75' height='75' className='errorLogo' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'>
Expand All @@ -49,12 +103,29 @@ class CertErrorPage extends React.Component {
<span data-l10n-id='certErrorText'></span>&nbsp;
<span className='errorUrl'>{this.state.url || ''}</span>
<span className='errorText'>{this.state.error || ''}</span>
{this.state.certDetail
? (<div>
<span className='certErrorText'>{'Issued To'}</span>
<span className='errorText'>{'Common Name (CN): ' + this.state.certSubjectName}</span>
<span className='errorText'>{'Serial Number: ' + this.state.certSerialNumber}</span>
<span className='certErrorText'>{'Issued By'}</span>
<span className='errorText'>{'Common Name (CN): ' + this.state.certIssuerName}</span>
<span className='certErrorText'>{'Period of Validity'}</span>
<span className='errorText'>{'Begins On: ' + this.state.certValidStart}</span>
<span className='errorText'>{'Expires On: ' + this.state.certValidExpiry}</span>
<span className='certErrorText'>{'Fingerprint: '}</span>
<span className='errorText'>{this.state.certFingerprint[0] + ': ' +
toHexString(toByteArray(window.atob(this.state.certFingerprint[1])))}</span>
</div>) : null}
</div>
<div className='buttons'>
<Button l10nId='certErrorSafety' className='actionButton' onClick={this.onSafety.bind(this)} />
{this.state.url ? (this.state.advanced
? <Button l10nId='certErrorButtonText' className='subtleButton' onClick={this.onAccept.bind(this)} />
: <Button l10nId='certErrorAdvanced' className='subtleButton' onClick={this.onAdvanced.bind(this)} />) : null}
{this.state.url ? (this.state.advanced
? (<div>
<Button l10nId='certErrorButtonText' className='subtleButton' onClick={this.onAccept.bind(this)} />
<Button l10nId='certErrorShowCertificate' className='subtleButton' onClick={this.onDetail.bind(this)} />
</div>)
: <Button l10nId='certErrorAdvanced' className='subtleButton' onClick={this.onAdvanced.bind(this)} />) : null}
</div>
</div>
}
Expand Down
2 changes: 2 additions & 0 deletions js/constants/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ const messages = {
// HTTPS
CERT_ERROR_ACCEPTED: _, /** @arg {string} url where a cert error was accepted */
CHECK_CERT_ERROR_ACCEPTED: _, /** @arg {string} url to check cert error, @arg {number} key of frame */
GET_CERT_ERROR_DETAIL: _,
SET_CERT_ERROR_DETAIL: _,
SET_SECURITY_STATE: _, /** @arg {number} key of frame, @arg {Object} security state */
HTTPSE_RULE_APPLIED: _, /** @arg {string} name of ruleset file, @arg {Object} details of rewritten request */
// Bookmarks
Expand Down

0 comments on commit 4ae87f8

Please sign in to comment.