add phishing detection example

This commit is contained in:
dillon 2015-10-02 15:38:48 -07:00
parent cc70d15515
commit a8b76f1078
21 changed files with 1493 additions and 0 deletions

1
examples/N1-Phishing-Detection/.gitignore vendored Executable file
View file

@ -0,0 +1 @@
node_modules

View file

@ -0,0 +1,19 @@
## Phishing Detection
A sample package for Nylas Mail to detect simple phishing attempts. This package display a simple warning if
a message's originating address is different from its return address. The warning looks like this:
![screenshot](./screenshot.png)
#### Who is this for?
This package is our slimmest example package. It's annotated for developers who have no experience with React, Flux, Electron, or N1.
#### To build documentation (the manual way):
```
cjsx-transform lib/main.cjsx > docs/main.coffee
docco docs/main.coffee
rm docs/main.coffee
```

View file

@ -0,0 +1,518 @@
/*--------------------- Typography ----------------------------*/
@font-face {
font-family: 'aller-light';
src: url('public/fonts/aller-light.eot');
src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
url('public/fonts/aller-light.woff') format('woff'),
url('public/fonts/aller-light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'aller-bold';
src: url('public/fonts/aller-bold.eot');
src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
url('public/fonts/aller-bold.woff') format('woff'),
url('public/fonts/aller-bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'roboto-black';
src: url('public/fonts/roboto-black.eot');
src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'),
url('public/fonts/roboto-black.woff') format('woff'),
url('public/fonts/roboto-black.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
/*--------------------- Layout ----------------------------*/
html { height: 100%; }
body {
font-family: "aller-light";
font-size: 14px;
line-height: 18px;
color: #30404f;
margin: 0; padding: 0;
height:100%;
}
#container { min-height: 100%; }
a {
color: #000;
}
b, strong {
font-weight: normal;
font-family: "aller-bold";
}
p {
margin: 15px 0 0px;
}
.annotation ul, .annotation ol {
margin: 25px 0;
}
.annotation ul li, .annotation ol li {
font-size: 14px;
line-height: 18px;
margin: 10px 0;
}
h1, h2, h3, h4, h5, h6 {
color: #112233;
line-height: 1em;
font-weight: normal;
font-family: "roboto-black";
text-transform: uppercase;
margin: 30px 0 15px 0;
}
h1 {
margin-top: 40px;
}
h2 {
font-size: 1.26em;
}
hr {
border: 0;
background: 1px #ddd;
height: 1px;
margin: 20px 0;
}
pre, tt, code {
font-size: 12px; line-height: 16px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
.annotation pre {
display: block;
margin: 0;
padding: 7px 10px;
background: #fcfcfc;
-moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
overflow-x: auto;
}
.annotation pre code {
border: 0;
padding: 0;
background: transparent;
}
blockquote {
border-left: 5px solid #ccc;
margin: 0;
padding: 1px 0 1px 1em;
}
.sections blockquote p {
font-family: Menlo, Consolas, Monaco, monospace;
font-size: 12px; line-height: 16px;
color: #999;
margin: 10px 0 0;
white-space: pre-wrap;
}
ul.sections {
list-style: none;
padding:0 0 5px 0;;
margin:0;
}
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
ul.sections > li > div {
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
box-sizing: border-box; /* css3 */
}
/*---------------------- Jump Page -----------------------------*/
#jump_to, #jump_page {
margin: 0;
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 16px Arial;
cursor: pointer;
text-align: right;
list-style: none;
}
#jump_to a {
text-decoration: none;
}
#jump_to a.large {
display: none;
}
#jump_to a.small {
font-size: 22px;
font-weight: bold;
color: #676767;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 10px 15px;
margin:0;
}
#jump_wrapper {
display: none;
padding:0;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page_wrapper{
position: fixed;
right: 0;
top: 0;
bottom: 0;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
max-height: 100%;
overflow: auto;
}
#jump_page .source {
display: block;
padding: 15px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
/*---------------------- Low resolutions (> 320px) ---------------------*/
@media only screen and (min-width: 320px) {
.pilwrap { display: none; }
ul.sections > li > div {
display: block;
padding:5px 10px 0 10px;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
padding-left: 30px;
}
ul.sections > li > div.content {
overflow-x:auto;
-webkit-box-shadow: inset 0 0 5px #e5e5ee;
box-shadow: inset 0 0 5px #e5e5ee;
border: 1px solid #dedede;
margin:5px 10px 5px 10px;
padding-bottom: 5px;
}
ul.sections > li > div.annotation pre {
margin: 7px 0 7px;
padding-left: 15px;
}
ul.sections > li > div.annotation p tt, .annotation code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
}
/*---------------------- (> 481px) ---------------------*/
@media only screen and (min-width: 481px) {
#container {
position: relative;
}
body {
background-color: #F5F5FF;
font-size: 15px;
line-height: 21px;
}
pre, tt, code {
line-height: 18px;
}
p, ul, ol {
margin: 0 0 15px;
}
#jump_to {
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
}
#jump_to, #jump_page {
font: 10px Arial;
text-transform: uppercase;
}
#jump_page .source {
padding: 5px 10px;
}
#jump_to a.large {
display: inline-block;
}
#jump_to a.small {
display: none;
}
#background {
position: absolute;
top: 0; bottom: 0;
width: 350px;
background: #fff;
border-right: 1px solid #e5e5ee;
z-index: -1;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
padding-left: 40px;
}
ul.sections > li {
white-space: nowrap;
}
ul.sections > li > div {
display: inline-block;
}
ul.sections > li > div.annotation {
max-width: 350px;
min-width: 350px;
min-height: 5px;
padding: 13px;
overflow-x: hidden;
white-space: normal;
vertical-align: top;
text-align: left;
}
ul.sections > li > div.annotation pre {
margin: 15px 0 15px;
padding-left: 15px;
}
ul.sections > li > div.content {
padding: 13px;
vertical-align: top;
border: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.pilwrap {
position: relative;
display: inline;
}
.pilcrow {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
.for-h1 .pilcrow {
top: 47px;
}
.for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
top: 35px;
}
ul.sections > li > div.annotation:hover .pilcrow {
opacity: 1;
}
}
/*---------------------- (> 1025px) ---------------------*/
@media only screen and (min-width: 1025px) {
body {
font-size: 16px;
line-height: 24px;
}
#background {
width: 525px;
}
ul.sections > li > div.annotation {
max-width: 525px;
min-width: 525px;
padding: 10px 25px 1px 50px;
}
ul.sections > li > div.content {
padding: 9px 15px 16px 25px;
}
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
pre code {
display: block; padding: 0.5em;
color: #000;
background: #f8f8ff
}
pre .hljs-comment,
pre .hljs-template_comment,
pre .hljs-diff .hljs-header,
pre .hljs-javadoc {
color: #408080;
font-style: italic
}
pre .hljs-keyword,
pre .hljs-assignment,
pre .hljs-literal,
pre .hljs-css .hljs-rule .hljs-keyword,
pre .hljs-winutils,
pre .hljs-javascript .hljs-title,
pre .hljs-lisp .hljs-title,
pre .hljs-subst {
color: #954121;
/*font-weight: bold*/
}
pre .hljs-number,
pre .hljs-hexcolor {
color: #40a070
}
pre .hljs-string,
pre .hljs-tag .hljs-value,
pre .hljs-phpdoc,
pre .hljs-tex .hljs-formula {
color: #219161;
}
pre .hljs-title,
pre .hljs-id {
color: #19469D;
}
pre .hljs-params {
color: #00F;
}
pre .hljs-javascript .hljs-title,
pre .hljs-lisp .hljs-title,
pre .hljs-subst {
font-weight: normal
}
pre .hljs-class .hljs-title,
pre .hljs-haskell .hljs-label,
pre .hljs-tex .hljs-command {
color: #458;
font-weight: bold
}
pre .hljs-tag,
pre .hljs-tag .hljs-title,
pre .hljs-rules .hljs-property,
pre .hljs-django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal
}
pre .hljs-attribute,
pre .hljs-variable,
pre .hljs-instancevar,
pre .hljs-lisp .hljs-body {
color: #008080
}
pre .hljs-regexp {
color: #B68
}
pre .hljs-class {
color: #458;
font-weight: bold
}
pre .hljs-symbol,
pre .hljs-ruby .hljs-symbol .hljs-string,
pre .hljs-ruby .hljs-symbol .hljs-keyword,
pre .hljs-ruby .hljs-symbol .hljs-keymethods,
pre .hljs-lisp .hljs-keyword,
pre .hljs-tex .hljs-special,
pre .hljs-input_number {
color: #990073
}
pre .hljs-builtin,
pre .hljs-constructor,
pre .hljs-built_in,
pre .hljs-lisp .hljs-title {
color: #0086b3
}
pre .hljs-preprocessor,
pre .hljs-pi,
pre .hljs-doctype,
pre .hljs-shebang,
pre .hljs-cdata {
color: #999;
font-weight: bold
}
pre .hljs-deletion {
background: #fdd
}
pre .hljs-addition {
background: #dfd
}
pre .hljs-diff .hljs-change {
background: #0086b3
}
pre .hljs-chunk {
color: #aaa
}
pre .hljs-tex .hljs-formula {
opacity: 0.5;
}

View file

@ -0,0 +1,93 @@
# # Phishing Detection
#
# This is a simple package to notify N1 users if an email is a potential
# phishing scam.
# You can access N1 dependencies by requiring 'nylas-exports'
{React,
# The ComponentRegistry manages all React components in N1.
ComponentRegistry,
# A `Store` is a Flux component which contains all business logic and data
# models to be consumed by React components to render markup.
MessageStore} = require 'nylas-exports'
# Notice that this file is `main.cjsx` rather than `main.coffee`. We use the
# `.cjsx` filetype because we use the CJSX DSL to describe markup for React to
# render. Without the CJSX, we could just name this file `main.coffee` instead.
class PhishingIndicator extends React.Component
# Adding a @displayName to a React component helps for debugging.
@displayName: 'PhishingIndicator'
# @propTypes is an object which validates the datatypes of properties that
# this React component can receive.
@propTypes:
thread: React.PropTypes.object.isRequired
# A React component's `render` method returns a virtual DOM element described
# in CJSX. `render` is deterministic: with the same input, it will always
# render the same output. Here, the input is provided by @isPhishingAttempt.
# `@state` and `@props` are popular inputs as well.
render: =>
# Our inputs for the virtual DOM to render come from @isPhishingAttempt.
[from, reply_to] = @isPhishingAttempt()
# We add some more application logic to decide how to render.
if from isnt null and reply_to isnt null
React.createElement("div", {"className": "phishingIndicator"},
React.createElement("b", null, "This message looks suspicious!"),
React.createElement("p", null, "It originates from ", (from), " but replies will go to ", (reply_to), ".")
)
# If you don't want a React component to render anything at all, then your
# `render` method should return `null` or `undefined`.
else
null
isPhishingAttempt: =>
# In this package, the MessageStore is the source of our data which will be
# the input for the `render` function. @isPhishingAttempt is performing some
# domain-specific application logic to prepare the data for `render`.
message = MessageStore.items()[0]
# This package's strategy to ascertain whether or not the email is a
# phishing attempt boils down to checking the `replyTo` attributes on
# `Message` models from `MessageStore`.
if message.replyTo? and message.replyTo.length != 0
# The `from` and `replyTo` attributes on `Message` models both refer to
# arrays of `Contact` models, which in turn have `email` attributes.
from = message.from[0].email
reply_to = message.replyTo[0].email
# This is our core logic for our whole package! If the `from` and
# `replyTo` emails are different, then we want to show a phishing warning.
if reply_to isnt from
return [from, reply_to]
return [null, null];
module.exports =
# Activate is called when the package is loaded. If your package previously
# saved state using `serialize` it is provided.
activate: (@state) ->
# This is a good time to tell the `ComponentRegistry` to insert our
# React component into the `'MessageListHeaders'` part of the application.
ComponentRegistry.register PhishingIndicator,
role: 'MessageListHeaders'
# 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.
serialize: ->
# This **optional** method is called when the window is shutting down,
# or when your package is being updated or disabled. If your package is
# watching any files, holding external resources, providing commands or
# subscribing to events, release them here.
deactivate: ->
ComponentRegistry.unregister(PhishingIndicator)

View file

@ -0,0 +1,342 @@
<!DOCTYPE html>
<html>
<head>
<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" />
</head>
<body>
<div id="container">
<div id="background"></div>
<ul class="sections">
<li id="section-1">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-1">&#182;</a>
</div>
<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>
</li>
<li id="section-2">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-2">&#182;</a>
</div>
<p>You can access N1 dependencies by requiring nylas-exports</p>
</div>
<div class="content"><div class='highlight'><pre>{React,</pre></div></div>
</li>
<li id="section-3">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-3">&#182;</a>
</div>
<p>The ComponentRegistry manages all React components in N1.</p>
</div>
<div class="content"><div class='highlight'><pre> ComponentRegistry,</pre></div></div>
</li>
<li id="section-4">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-4">&#182;</a>
</div>
<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> MessageStore} = <span class="hljs-built_in">require</span> <span class="hljs-string">'nylas-exports'</span></pre></div></div>
</li>
<li id="section-5">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-5">&#182;</a>
</div>
<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-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>
<li id="section-6">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-6">&#182;</a>
</div>
<p>Adding a @displayName to a React component helps for debugging.</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@displayName</span>: <span class="hljs-string">'PhishingIndicator'</span></pre></div></div>
</li>
<li id="section-7">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-7">&#182;</a>
</div>
<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-property">@propTypes</span>:
<span class="hljs-attribute">thread</span>: React.PropTypes.object.isRequired</pre></div></div>
</li>
<li id="section-8">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-8">&#182;</a>
</div>
<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> <span class="hljs-attribute">render</span>: <span class="hljs-function">=&gt;</span></pre></div></div>
</li>
<li id="section-9">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-9">&#182;</a>
</div>
<p>Our inputs for the virtual DOM to render come from @isPhishingAttempt.</p>
</div>
<div class="content"><div class='highlight'><pre> [from, reply_to] = <span class="hljs-property">@isPhishingAttempt</span>()</pre></div></div>
</li>
<li id="section-10">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-10">&#182;</a>
</div>
<p>We add some more application logic to decide how to render.</p>
</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>
<li id="section-11">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-11">&#182;</a>
</div>
<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-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>
<li id="section-12">
<div class="annotation">
<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>
</div>
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">serialize</span>: <span class="hljs-function">-&gt;</span></pre></div></div>
</li>
<li id="section-19">
<div class="annotation">
<div class="pilwrap ">
<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
watching any files, holding external resources, providing commands or
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(PhishingIndicator)</pre></div></div>
</li>
</ul>
</div>
</body>
</html>

View file

@ -0,0 +1,375 @@
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View file

@ -0,0 +1,93 @@
# # Phishing Detection
#
# This is a simple package to notify N1 users if an email is a potential
# phishing scam.
# You can access N1 dependencies by requiring 'nylas-exports'
{React,
# The ComponentRegistry manages all React components in N1.
ComponentRegistry,
# A `Store` is a Flux component which contains all business logic and data
# models to be consumed by React components to render markup.
MessageStore} = require 'nylas-exports'
# Notice that this file is `main.cjsx` rather than `main.coffee`. We use the
# `.cjsx` filetype because we use the CJSX DSL to describe markup for React to
# render. Without the CJSX, we could just name this file `main.coffee` instead.
class PhishingIndicator extends React.Component
# Adding a @displayName to a React component helps for debugging.
@displayName: 'PhishingIndicator'
# @propTypes is an object which validates the datatypes of properties that
# this React component can receive.
@propTypes:
thread: React.PropTypes.object.isRequired
# A React component's `render` method returns a virtual DOM element described
# in CJSX. `render` is deterministic: with the same input, it will always
# render the same output. Here, the input is provided by @isPhishingAttempt.
# `@state` and `@props` are popular inputs as well.
render: =>
# Our inputs for the virtual DOM to render come from @isPhishingAttempt.
[from, reply_to] = @isPhishingAttempt()
# We add some more application logic to decide how to render.
if from isnt null and reply_to isnt null
<div className="phishingIndicator">
<b>This message looks suspicious!</b>
<p>It originates from {from} but replies will go to {reply_to}.</p>
</div>
# If you don't want a React component to render anything at all, then your
# `render` method should return `null` or `undefined`.
else
null
isPhishingAttempt: =>
# In this package, the MessageStore is the source of our data which will be
# the input for the `render` function. @isPhishingAttempt is performing some
# domain-specific application logic to prepare the data for `render`.
message = MessageStore.items()[0]
# This package's strategy to ascertain whether or not the email is a
# phishing attempt boils down to checking the `replyTo` attributes on
# `Message` models from `MessageStore`.
if message.replyTo? and message.replyTo.length != 0
# The `from` and `replyTo` attributes on `Message` models both refer to
# arrays of `Contact` models, which in turn have `email` attributes.
from = message.from[0].email
reply_to = message.replyTo[0].email
# This is our core logic for our whole package! If the `from` and
# `replyTo` emails are different, then we want to show a phishing warning.
if reply_to isnt from
return [from, reply_to]
return [null, null];
module.exports =
# Activate is called when the package is loaded. If your package previously
# saved state using `serialize` it is provided.
activate: (@state) ->
# This is a good time to tell the `ComponentRegistry` to insert our
# React component into the `'MessageListHeaders'` part of the application.
ComponentRegistry.register PhishingIndicator,
role: 'MessageListHeaders'
# 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.
serialize: ->
# This **optional** method is called when the window is shutting down,
# or when your package is being updated or disabled. If your package is
# watching any files, holding external resources, providing commands or
# subscribing to events, release them here.
deactivate: ->
ComponentRegistry.unregister(PhishingIndicator)

View file

@ -0,0 +1,22 @@
{
"name": "phishing",
"version": "0.2.0",
"main": "./lib/main",
"description": "An example package for Nylas Mail that translates drafts into other languages using the Yandex API.",
"license": "Proprietary",
"engines": {
"atom": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/nylas/translate"
},
"windowTypes": {
"default": true,
"composer": true
},
"dependencies": {
"request": "^2.53",
"underscore": "*"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View file

@ -0,0 +1,12 @@
describe "AccountSidebarStore", ->
xit "should update it's selected ID when the focusTag action fires", ->
true
xit "should update when the DatabaseStore emits changes to tags", ->
true
xit "should update when the NamespaceStore emits", ->
true
xit "should provide an array of sections to the sidebar view", ->
true

View file

@ -0,0 +1,7 @@
@import "ui-variables";
@import "ui-mixins";
.phishing-indicator {
text-align: center;
background-color: white;
}

View file

@ -0,0 +1,11 @@
.phishingIndicator {
display: block;
box-sizing: border-box;
-webkit-print-color-adjust: exact;
padding: 8px 12px;
margin-bottom: 5px;
border: 1px solid rgb(235, 204, 209);
border-radius: 4px;
color: rgb(169, 68, 66);
background-color: rgb(242, 222, 222);
}