Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Add a way to view TLS certificates when error #2428

Merged
merged 1 commit into from
Jul 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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