add annotated source for phishing detection

This commit is contained in:
dillon 2015-10-02 15:52:31 -07:00
parent 82a5c745cb
commit 8b7964a140

View file

@ -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">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
<p>The <code>render</code> method returns a React Virtual DOM element. This code looks
like HTML, but dont be fooled. The CJSX preprocessor converts</p>
<p><code>&lt;a href=&quot;http://facebook.github.io/react/&quot;&gt;Hello!&lt;/a&gt;</code></p>
<p>into Javascript objects which describe the HTML you want:</p>
<p><code>React.createElement(&#39;a&#39;, {href: &#39;http://facebook.github.io/react/&#39;}, &#39;Hello!&#39;)</code></p>
<p>Were rendering a <code>Popover</code> with a <code>Menu</code> inside. These components are part
of Edgehills standard <code>nylas-component-kit</code> library, and make it easy to build
interfaces that match the rest of Edgehills 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">=&gt;</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) -&gt; item )</span>, \
"itemContent": <span class="hljs-params">( (item) -&gt; 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 Edgehills UI.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-5">&#182;</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">=&gt;</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> =&gt;</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 Edgehills UI.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-6">&#182;</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> =&gt;
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">&#182;</a>
</div>
<p>Identify the text we want to translate. We need to make sure we
dont 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 &gt; <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 @@ dont translate quoted text.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-8">&#182;</a>
</div>
<p>Use Nodes <code>request</code> library to perform the translation using the Yandex API.</p>
<p>A React components <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> =&gt;</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">=&gt;</span></pre></div></div>
</li>
@ -222,13 +155,11 @@ dont translate quoted text.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-9">&#182;</a>
</div>
<p>The new text of the draft is our translated response, plus any quoted text
that we didnt 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 &gt; <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 didnt process.</p>
<div class="pilwrap ">
<a class="pilcrow" href="#section-10">&#182;</a>
</div>
<p>To update the draft, we add the new body to its 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> =&gt;</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">&#182;</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 dont 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> -&gt;</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">=&gt;</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">&#182;</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">&#182;</a>
</div>
<p>This packages 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">&#182;</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">&#182;</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">&#182;</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> -&gt;</span></pre></div></div>
</li>
<li id="section-17">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-17">&#182;</a>
</div>
<p>This is a good time to tell the <code>ComponentRegistry</code> to insert our
React component into the <code>&#39;MessageListHeaders&#39;</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">&#182;</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">&#182;</a>
<a class="pilcrow" href="#section-19">&#182;</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">-&gt;</span>
ComponentRegistry.unregister(TranslateButton)</pre></div></div>
ComponentRegistry.unregister(PhishingIndicator)</pre></div></div>
</li>