mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-19 14:40:26 +08:00
add annotated source for phishing detection
This commit is contained in:
parent
82a5c745cb
commit
8b7964a140
1 changed files with 149 additions and 119 deletions
|
@ -2,13 +2,13 @@
|
|||
title: "phishing-detection"
|
||||
description: "Finding and showing you a warning if an email is a possible phishing scam."
|
||||
assumed_experience: "No experience with React, Flux, Electron, or N1."
|
||||
github: "todo-fix-this"
|
||||
github: "https://github.com/nylas/N1/tree/master/examples/N1-Phishing-Detection"
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Translation Plugin</title>
|
||||
<title>Phishing Detection</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
|
||||
<link rel="stylesheet" media="all" href="docco.css" />
|
||||
|
@ -27,40 +27,12 @@ github: "todo-fix-this"
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-1">¶</a>
|
||||
</div>
|
||||
<h1 id="translation-plugin">Translation Plugin</h1>
|
||||
<p>Last Revised: April 23, 2015 by Ben Gotow</p>
|
||||
<p>TranslateButton is a simple React component that allows you to select
|
||||
a language from a popup menu and translates draft text into that language.</p>
|
||||
<h1 id="phishing-detection">Phishing Detection</h1>
|
||||
<p>This is a simple package to notify N1 users if an email is a potential
|
||||
phishing scam.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre>
|
||||
request = <span class="hljs-built_in">require</span> <span class="hljs-string">'request'</span>
|
||||
|
||||
{Utils,
|
||||
React,
|
||||
ComponentRegistry,
|
||||
DraftStore} = <span class="hljs-built_in">require</span> <span class="hljs-string">'nylas-exports'</span>
|
||||
{Menu,
|
||||
RetinaImg,
|
||||
Popover} = <span class="hljs-built_in">require</span> <span class="hljs-string">'nylas-component-kit'</span>
|
||||
|
||||
YandexTranslationURL = <span class="hljs-string">'https://translate.yandex.net/api/v1.5/tr.json/translate'</span>
|
||||
YandexTranslationKey = <span class="hljs-string">'trnsl.1.1.20150415T044616Z.24814c314120d022.0a339e2bc2d2337461a98d5ec9863fc46e42735e'</span>
|
||||
YandexLanguages =
|
||||
<span class="hljs-string">'English'</span>: <span class="hljs-string">'en'</span>
|
||||
<span class="hljs-string">'Spanish'</span>: <span class="hljs-string">'es'</span>
|
||||
<span class="hljs-string">'Russian'</span>: <span class="hljs-string">'ru'</span>
|
||||
<span class="hljs-string">'Chinese'</span>: <span class="hljs-string">'zh'</span>
|
||||
<span class="hljs-string">'French'</span>: <span class="hljs-string">'fr'</span>
|
||||
<span class="hljs-string">'German'</span>: <span class="hljs-string">'de'</span>
|
||||
<span class="hljs-string">'Italian'</span>: <span class="hljs-string">'it'</span>
|
||||
<span class="hljs-string">'Japanese'</span>: <span class="hljs-string">'ja'</span>
|
||||
<span class="hljs-string">'Portuguese'</span>: <span class="hljs-string">'pt'</span>
|
||||
<span class="hljs-string">'Korean'</span>: <span class="hljs-string">'ko'</span>
|
||||
|
||||
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TranslateButton</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
@ -70,11 +42,11 @@ YandexLanguages =
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-2">¶</a>
|
||||
</div>
|
||||
<p>Adding a <code>displayName</code> makes debugging React easier</p>
|
||||
<p>You can access N1 dependencies by requiring ‘nylas-exports’</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@displayName</span>: <span class="hljs-string">'TranslateButton'</span></pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre>{React,</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -85,14 +57,11 @@ YandexLanguages =
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-3">¶</a>
|
||||
</div>
|
||||
<p>Since our button is being injected into the Composer Footer,
|
||||
we receive the local id of the current draft as a <code>prop</code> (a read-only
|
||||
property). Since our code depends on this prop, we mark it as a requirement.</p>
|
||||
<p>The ComponentRegistry manages all React components in N1.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@propTypes</span>:
|
||||
<span class="hljs-attribute">draftLocalId</span>: React.PropTypes.string.isRequired</pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> ComponentRegistry,</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -103,29 +72,12 @@ property). Since our code depends on this prop, we mark it as a requirement.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-4">¶</a>
|
||||
</div>
|
||||
<p>The <code>render</code> method returns a React Virtual DOM element. This code looks
|
||||
like HTML, but don’t be fooled. The CJSX preprocessor converts</p>
|
||||
<p><code><a href="http://facebook.github.io/react/">Hello!</a></code></p>
|
||||
<p>into Javascript objects which describe the HTML you want:</p>
|
||||
<p><code>React.createElement('a', {href: 'http://facebook.github.io/react/'}, 'Hello!')</code></p>
|
||||
<p>We’re rendering a <code>Popover</code> with a <code>Menu</code> inside. These components are part
|
||||
of Edgehill’s standard <code>nylas-component-kit</code> library, and make it easy to build
|
||||
interfaces that match the rest of Edgehill’s UI.</p>
|
||||
<p>A <code>Store</code> is a Flux component which contains all business logic and data
|
||||
models to be consumed by React components to render markup.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">render</span>: <span class="hljs-function">=></span>
|
||||
React.createElement(Popover, {<span class="hljs-string">"ref"</span>: <span class="hljs-string">"popover"</span>, \
|
||||
<span class="hljs-string">"className"</span>: <span class="hljs-string">"translate-language-picker pull-right"</span>, \
|
||||
<span class="hljs-string">"buttonComponent"</span>: (<span class="hljs-property">@_renderButton</span>())},
|
||||
React.createElement(Menu, {<span class="hljs-string">"items"</span>: ( Object.keys(YandexLanguages) ), \
|
||||
<span class="hljs-string">"itemKey"</span>: <span class="hljs-function"><span class="hljs-params">( (item) -> item )</span>, \
|
||||
"itemContent": <span class="hljs-params">( (item) -> item )</span>, \
|
||||
"onSelect": <span class="hljs-params">(<span class="hljs-property">@_onTranslate</span>)</span>
|
||||
})
|
||||
)
|
||||
|
||||
</span></pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> MessageStore} = <span class="hljs-built_in">require</span> <span class="hljs-string">'nylas-exports'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -136,20 +88,13 @@ interfaces that match the rest of Edgehill’s UI.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-5">¶</a>
|
||||
</div>
|
||||
<p>Helper method to render the button that will activate the popover. Using the
|
||||
<code>RetinaImg</code> component makes it easy to display an image from our package.
|
||||
<code>RetinaImg</code> will automatically chose the best image format for our display.</p>
|
||||
<p>Notice that this file is <code>main.cjsx</code> rather than <code>main.coffee</code>. We use the
|
||||
<code>.cjsx</code> filetype because we use the CJSX DSL to describe markup for React to
|
||||
render. Without the CJSX, we could just name this file <code>main.coffee</code> instead.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">_renderButton</span>: <span class="hljs-function">=></span>
|
||||
React.createElement(<span class="hljs-string">"button"</span>, {<span class="hljs-string">"className"</span>: <span class="hljs-string">"btn btn-toolbar"</span>}, <span class="hljs-string">"""
|
||||
Translate
|
||||
"""</span>, React.createElement(RetinaImg, {<span class="hljs-string">"name"</span>: <span class="hljs-string">"toolbar-chevron.png"</span>})
|
||||
)
|
||||
|
||||
<span class="hljs-attribute">_onTranslate</span>: <span class="hljs-function"><span class="hljs-params">(lang)</span> =></span>
|
||||
<span class="hljs-property">@refs</span>.popover.close()</pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PhishingIndicator</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -160,16 +105,11 @@ interfaces that match the rest of Edgehill’s UI.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-6">¶</a>
|
||||
</div>
|
||||
<p>Obtain the session for the current draft. The draft session provides us
|
||||
the draft object and also manages saving changes to the local cache and
|
||||
Nilas API as multiple parts of the application touch the draft.</p>
|
||||
<p>Adding a @displayName to a React component helps for debugging.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> session = DraftStore.sessionForLocalId(<span class="hljs-property">@props</span>.draftLocalId)
|
||||
session.prepare().<span class="hljs-keyword">then</span> =>
|
||||
body = session.draft().body
|
||||
bodyQuoteStart = Utils.quotedTextIndex(body)</pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@displayName</span>: <span class="hljs-string">'PhishingIndicator'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -180,21 +120,13 @@ Nilas API as multiple parts of the application touch the draft.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-7">¶</a>
|
||||
</div>
|
||||
<p>Identify the text we want to translate. We need to make sure we
|
||||
don’t translate quoted text.</p>
|
||||
<p>@propTypes is an object which validates the datatypes of properties that
|
||||
this React component can receive.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> bodyQuoteStart > <span class="hljs-number">0</span>
|
||||
text = body.substr(<span class="hljs-number">0</span>, bodyQuoteStart)
|
||||
<span class="hljs-keyword">else</span>
|
||||
text = body
|
||||
|
||||
query =
|
||||
<span class="hljs-attribute">key</span>: YandexTranslationKey
|
||||
<span class="hljs-attribute">lang</span>: YandexLanguages[lang]
|
||||
<span class="hljs-attribute">text</span>: text
|
||||
<span class="hljs-attribute">format</span>: <span class="hljs-string">'html'</span></pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@propTypes</span>:
|
||||
<span class="hljs-attribute">thread</span>: React.PropTypes.object.isRequired</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -205,13 +137,14 @@ don’t translate quoted text.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-8">¶</a>
|
||||
</div>
|
||||
<p>Use Node’s <code>request</code> library to perform the translation using the Yandex API.</p>
|
||||
<p>A React component’s <code>render</code> method returns a virtual DOM element described
|
||||
in CJSX. <code>render</code> is deterministic: with the same input, it will always
|
||||
render the same output. Here, the input is provided by @isPhishingAttempt.
|
||||
<code>@state</code> and <code>@props</code> are popular inputs as well.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> request {<span class="hljs-attribute">url</span>: YandexTranslationURL, <span class="hljs-attribute">qs</span>: query}, <span class="hljs-function"><span class="hljs-params">(error, resp, data)</span> =></span>
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-property">@_onError</span>(error) <span class="hljs-keyword">unless</span> resp.statusCode <span class="hljs-keyword">is</span> <span class="hljs-number">200</span>
|
||||
json = JSON.parse(data)</pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">render</span>: <span class="hljs-function">=></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -222,13 +155,11 @@ don’t translate quoted text.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-9">¶</a>
|
||||
</div>
|
||||
<p>The new text of the draft is our translated response, plus any quoted text
|
||||
that we didn’t process.</p>
|
||||
<p>Our inputs for the virtual DOM to render come from @isPhishingAttempt.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> translated = json.text.join(<span class="hljs-string">''</span>)
|
||||
translated += body.substr(bodyQuoteStart) <span class="hljs-keyword">if</span> bodyQuoteStart > <span class="hljs-number">0</span></pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> [from, reply_to] = <span class="hljs-property">@isPhishingAttempt</span>()</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -239,22 +170,15 @@ that we didn’t process.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-10">¶</a>
|
||||
</div>
|
||||
<p>To update the draft, we add the new body to it’s session. The session object
|
||||
automatically marshalls changes to the database and ensures that others accessing
|
||||
the same draft are notified of changes.</p>
|
||||
<p>We add some more application logic to decide how to render.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> session.changes.add(<span class="hljs-attribute">body</span>: translated)
|
||||
session.changes.commit()
|
||||
|
||||
<span class="hljs-attribute">_onError</span>: <span class="hljs-function"><span class="hljs-params">(error)</span> =></span>
|
||||
<span class="hljs-property">@refs</span>.popover.close()
|
||||
dialog = <span class="hljs-built_in">require</span>(<span class="hljs-string">'remote'</span>).<span class="hljs-built_in">require</span>(<span class="hljs-string">'dialog'</span>)
|
||||
dialog.showErrorBox(<span class="hljs-string">'Geolocation Failed'</span>, error.toString())
|
||||
|
||||
|
||||
<span class="hljs-built_in">module</span>.exports =</pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> from <span class="hljs-keyword">isnt</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">and</span> reply_to <span class="hljs-keyword">isnt</span> <span class="hljs-literal">null</span>
|
||||
React.createElement(<span class="hljs-string">"div"</span>, {<span class="hljs-string">"className"</span>: <span class="hljs-string">"phishingIndicator"</span>},
|
||||
React.createElement(<span class="hljs-string">"b"</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">"This message looks suspicious!"</span>),
|
||||
React.createElement(<span class="hljs-string">"p"</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">"It originates from "</span>, (from), <span class="hljs-string">" but replies will go to "</span>, (reply_to), <span class="hljs-string">"."</span>)
|
||||
)</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -265,14 +189,15 @@ the same draft are notified of changes.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-11">¶</a>
|
||||
</div>
|
||||
<p>Activate is called when the package is loaded. If your package previously
|
||||
saved state using <code>serialize</code> it is provided.</p>
|
||||
<p>If you don’t want a React component to render anything at all, then your
|
||||
<code>render</code> method should return <code>null</code> or <code>undefined</code>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">activate</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-property">@state</span>)</span> -></span>
|
||||
ComponentRegistry.register TranslateButton,
|
||||
<span class="hljs-attribute">role</span>: <span class="hljs-string">'Composer:ActionButton'</span></pre></div></div>
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span>
|
||||
<span class="hljs-literal">null</span>
|
||||
|
||||
<span class="hljs-attribute">isPhishingAttempt</span>: <span class="hljs-function">=></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
@ -283,6 +208,111 @@ saved state using <code>serialize</code> it is provided.</p>
|
|||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-12">¶</a>
|
||||
</div>
|
||||
<p>In this package, the MessageStore is the source of our data which will be
|
||||
the input for the <code>render</code> function. @isPhishingAttempt is performing some
|
||||
domain-specific application logic to prepare the data for <code>render</code>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> message = MessageStore.items()[<span class="hljs-number">0</span>]</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-13">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-13">¶</a>
|
||||
</div>
|
||||
<p>This package’s strategy to ascertain whether or not the email is a
|
||||
phishing attempt boils down to checking the <code>replyTo</code> attributes on
|
||||
<code>Message</code> models from <code>MessageStore</code>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> message.replyTo? <span class="hljs-keyword">and</span> message.replyTo.length != <span class="hljs-number">0</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-14">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-14">¶</a>
|
||||
</div>
|
||||
<p>The <code>from</code> and <code>replyTo</code> attributes on <code>Message</code> models both refer to
|
||||
arrays of <code>Contact</code> models, which in turn have <code>email</code> attributes.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> from = message.from[<span class="hljs-number">0</span>].email
|
||||
reply_to = message.replyTo[<span class="hljs-number">0</span>].email</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-15">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-15">¶</a>
|
||||
</div>
|
||||
<p>This is our core logic for our whole package! If the <code>from</code> and
|
||||
<code>replyTo</code> emails are different, then we want to show a phishing warning.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> reply_to <span class="hljs-keyword">isnt</span> from
|
||||
<span class="hljs-keyword">return</span> [from, reply_to]
|
||||
|
||||
<span class="hljs-keyword">return</span> [<span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>];
|
||||
|
||||
<span class="hljs-built_in">module</span>.exports =</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-16">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-16">¶</a>
|
||||
</div>
|
||||
<p>Activate is called when the package is loaded. If your package previously
|
||||
saved state using <code>serialize</code> it is provided.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">activate</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-property">@state</span>)</span> -></span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-17">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-17">¶</a>
|
||||
</div>
|
||||
<p>This is a good time to tell the <code>ComponentRegistry</code> to insert our
|
||||
React component into the <code>'MessageListHeaders'</code> part of the application.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> ComponentRegistry.register PhishingIndicator,
|
||||
<span class="hljs-attribute">role</span>: <span class="hljs-string">'MessageListHeaders'</span></pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
<li id="section-18">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-18">¶</a>
|
||||
</div>
|
||||
<p>Serialize is called when your package is about to be unmounted.
|
||||
You can return a state object that will be passed back to your package
|
||||
when it is re-activated.</p>
|
||||
|
@ -294,11 +324,11 @@ when it is re-activated.</p>
|
|||
</li>
|
||||
|
||||
|
||||
<li id="section-13">
|
||||
<li id="section-19">
|
||||
<div class="annotation">
|
||||
|
||||
<div class="pilwrap ">
|
||||
<a class="pilcrow" href="#section-13">¶</a>
|
||||
<a class="pilcrow" href="#section-19">¶</a>
|
||||
</div>
|
||||
<p>This <strong>optional</strong> method is called when the window is shutting down,
|
||||
or when your package is being updated or disabled. If your package is
|
||||
|
@ -308,7 +338,7 @@ subscribing to events, release them here.</p>
|
|||
</div>
|
||||
|
||||
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">deactivate</span>: <span class="hljs-function">-></span>
|
||||
ComponentRegistry.unregister(TranslateButton)</pre></div></div>
|
||||
ComponentRegistry.unregister(PhishingIndicator)</pre></div></div>
|
||||
|
||||
</li>
|
||||
|
||||
|
|
Loading…
Reference in a new issue