Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use JSON to pass the command instruction #555

Merged
merged 1 commit into from
Mar 20, 2021
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
57 changes: 36 additions & 21 deletions src/background.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { browser, ContextMenus, Extension } from "webextension-polyfill-ts";

import { Command } from "@/command";
import { CommandPacker } from "@/command/packer";
import { CommandRunner } from "@/command/runner";
import { Selector } from "@/selector";
import { truncate } from "@/truncate";
import {
AnalyzerEntry,
GeneralSettings,
Expand All @@ -20,9 +20,9 @@ export async function showNotification(message: string): Promise<void> {
});
}

export async function search(command: Command): Promise<void> {
export async function search(runner: CommandRunner): Promise<void> {
try {
const url: string = command.search();
const url: string = runner.search();
if (url !== "") {
await browser.tabs.create({ url });
}
Expand All @@ -32,10 +32,10 @@ export async function search(command: Command): Promise<void> {
}
}

export async function searchAll(command: Command): Promise<void> {
export async function searchAll(runner: CommandRunner): Promise<void> {
try {
const states: SearcherStates = await getSearcherStates();
const urls = command.searchAll(states);
const urls = runner.searchAll(states);
for (const url of urls) {
await browser.tabs.create({ url });
}
Expand All @@ -45,10 +45,10 @@ export async function searchAll(command: Command): Promise<void> {
}
}

export async function scan(command: Command): Promise<void> {
export async function scan(runner: CommandRunner): Promise<void> {
const apiKeys = await getApiKeys();
try {
const url: string = await command.scan(apiKeys);
const url: string = await runner.scan(apiKeys);
if (url !== "") {
await browser.tabs.create({ url });
}
Expand Down Expand Up @@ -95,9 +95,16 @@ export async function createContextMenus(
firstEntry = entry;
}

const command: CommandPacker = new CommandPacker(
"search",
entry.query,
entry.type,
name
);

// it tells action, query, type and target to the listener
const id = `Search ${entry.query} as a ${entry.type} on ${name}`;
const title = `Search ${truncate(entry.query)} on ${name}`;
const id = command.getJSON();
const title = command.getMessage();
const options = { contexts, id, title };
browser.contextMenus.create(options, createContextMenuErrorHandler);
}
Expand All @@ -106,8 +113,15 @@ export async function createContextMenus(
if (firstEntry !== undefined) {
const query = firstEntry.query;
const type = firstEntry.type;
const id = `Search ${query} as a ${type} on all`;
const title = `Search ${truncate(query)} on all`;

const command: CommandPacker = new CommandPacker(
"search",
query,
type,
"all"
);
const id = command.getJSON();
const title = command.getMessage();
const options = { contexts, id, title };
browser.contextMenus.create(options, createContextMenuErrorHandler);
}
Expand All @@ -116,9 +130,10 @@ export async function createContextMenus(
const scannerEntries: AnalyzerEntry[] = selector.getScannerEntries();
for (const entry of scannerEntries) {
const name = entry.analyzer.name;
// it tells action/query/type/target to the listener
const id = `Scan ${entry.query} as a ${entry.type} on ${name}`;
const title = `Scan ${truncate(entry.query)} on ${name}`;

const command = new CommandPacker("scan", entry.query, entry.type, name);
const id = command.getJSON();
const title = command.getMessage();
const options = { contexts, id, title };
browser.contextMenus.create(options, createContextMenuErrorHandler);
}
Expand Down Expand Up @@ -148,17 +163,17 @@ if (typeof browser !== "undefined" && browser.runtime !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
browser.contextMenus.onClicked.addListener(async (info, tab_) => {
const id: string = info.menuItemId.toString();
const command = new Command(id);
switch (command.action) {
const runner = new CommandRunner(id);
switch (runner.command.action) {
case "search":
if (command.target === "all") {
await searchAll(command);
if (runner.command.target === "all") {
await searchAll(runner);
} else {
await search(command);
await search(runner);
}
break;
case "scan":
await scan(command);
await scan(runner);
break;
}
});
Expand Down
55 changes: 55 additions & 0 deletions src/command/packer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { truncate } from "@/truncate";
import { Command, SearchableType } from "@/types";

const capitalize = (s: string) => {
return s.charAt(0).toUpperCase() + s.slice(1);
};

export class CommandPacker {
public action: string;
public query: string;
public target: string;
public type: SearchableType;

public constructor(
action: string,
query: string,
type: SearchableType,
target: string
) {
this.action = action;
this.query = query;
this.type = type;
this.target = target;
}

private isAbbreviationType(): boolean {
const abbreviations = ["ip", "asn", "cve", "eth", "url"];
return abbreviations.includes(this.type);
}

private getNormalizedType(): string {
if (this.isAbbreviationType()) {
return this.type.toUpperCase();
}
return this.type;
}

public getJSON(): string {
const command: Command = {
action: this.action,
query: this.query,
type: this.type,
target: this.target,
};
return JSON.stringify(command);
}

public getMessage(): string {
const type = this.getNormalizedType();

return `${capitalize(this.action)} ${truncate(this.query)} as ${type} on ${
this.target
}`;
}
}
39 changes: 17 additions & 22 deletions src/command.ts → src/command/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,21 @@ import { Selector } from "@/selector";
import {
AnalyzerEntry,
ApiKeys,
Command,
Scanner,
ScannerTable,
Searcher,
SearcherStates,
SearcherTable,
} from "@/types";

export class Command {
public action: string;
public query: string;
public target: string;
public type: string;
export class CommandRunner {
public command: Command;

public constructor(command: string) {
// command = `Search ${entry.query} as a ${entry.type} on ${name}`;
const parts: string[] = command.split(" ");
this.action = parts[0].toLowerCase();
this.type = parts[parts.length - 3];
this.query = parts.slice(1, parts.length - 5).join(" ");
this.target = parts[parts.length - 1];
public constructor(commandString: string) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const command: Command = JSON.parse(commandString);
this.command = command;
}

private searcherTable: SearcherTable = {
Expand Down Expand Up @@ -94,15 +89,15 @@ export class Command {
};

public search(): string {
const selector: Selector = new Selector(this.query);
const selector: Selector = new Selector(this.command.query);
const entries: AnalyzerEntry[] = selector.getSearcherEntries();
const entry = entries.find((r) => r.analyzer.name === this.target);
const entry = entries.find((r) => r.analyzer.name === this.command.target);
let url = "";

if (entry !== undefined) {
const searcher = entry.analyzer as Searcher;
if (this.type in this.searcherTable) {
const fn = this.searcherTable[this.type];
if (this.command.type in this.searcherTable) {
const fn = this.searcherTable[this.command.type];
url = fn(searcher, entry.query);
}
}
Expand All @@ -111,10 +106,10 @@ export class Command {
}

public searchAll(searcherStates: SearcherStates): string[] {
const selector: Selector = new Selector(this.query);
const selector: Selector = new Selector(this.command.query);
const entries: AnalyzerEntry[] = selector
.getSearcherEntries()
.filter((entry) => this.type === entry.type);
.filter((entry) => this.command.type === entry.type);
const selectedEntries = entries.filter(
(entry) =>
!(entry.analyzer.name in searcherStates) ||
Expand All @@ -123,9 +118,9 @@ export class Command {
const urls: string[] = [];
for (const entry of selectedEntries) {
const searcher = entry.analyzer as Searcher;
if (this.type in this.searcherTable) {
if (this.command.type in this.searcherTable) {
try {
const fn = this.searcherTable[this.type];
const fn = this.searcherTable[this.command.type];
urls.push(fn(searcher, entry.query));
} catch (err) {
continue;
Expand Down Expand Up @@ -157,9 +152,9 @@ export class Command {
};

public async scan(apiKeys: ApiKeys): Promise<string> {
const selector: Selector = new Selector(this.query);
const selector: Selector = new Selector(this.command.query);
const entries: AnalyzerEntry[] = selector.getScannerEntries();
const entry = entries.find((r) => r.analyzer.name === this.target);
const entry = entries.find((r) => r.analyzer.name === this.command.target);
let url = "";
if (entry !== undefined) {
const scanner = entry.analyzer as Scanner;
Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ export interface Config {
searcherStates: SearcherStates;
}

export interface Command {
action: string;
query: string;
type: SearchableType;
target: string;
}

export const MD5_LENGTH = 32;
export const SHA1_LENGTH = 40;
export const SHA256_LENGTH = 64;
46 changes: 32 additions & 14 deletions test/backgroud.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
searchAll,
showNotification,
} from "@/background";
import { Command } from "@/command";
import { CommandPacker } from "@/command/packer";
import { CommandRunner } from "@/command/runner";

import { browserMock } from "./browserMock";
import sinon = require("sinon");
Expand Down Expand Up @@ -43,10 +44,14 @@ describe("Background script", function () {
describe("#search", function () {
context("when given a valid input", function () {
it("should call chrome.tabs.create()", async function () {
const command = new Command(
"Search https://github.com as a url on Urlscan"
const packer = new CommandPacker(
"search",
"https://github.com",
"url",
"Urlscan"
);
await search(command);
const runner = new CommandRunner(packer.getJSON());
await search(runner);
browserMock.tabs.create.assertCalls([
[
{
Expand All @@ -72,10 +77,14 @@ describe("Background script", function () {
});
});
it("should call chrome.tabs.create", async function () {
const command = new Command(
"Search pub-9383614236930773 as a gaPubID on all"
const packer = new CommandPacker(
"search",
"pub-9383614236930773",
"gaPubID",
"all"
);
await searchAll(command);
const runner = new CommandRunner(packer.getJSON());
await searchAll(runner);
browserMock.tabs.create.assertCalls([
[
{
Expand Down Expand Up @@ -103,11 +112,16 @@ describe("Background script", function () {
});

it("should call chrome.tabs.create()", async function () {
const command = new Command(
"Scan https://www.wikipedia.org/ as a url on Urlscan"
const packer = new CommandPacker(
"scan",
"https://www.wikipedia.org/",
"url",
"Urlscan"
);
const runner = new CommandRunner(packer.getJSON());
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const commandStub: sinon.SinonStub<any, any> = sandbox
.stub(command, "scan")
.stub(runner, "scan")
.withArgs({
hybridAnalysisApiKey: "test",
urlscanApiKey: "test",
Expand All @@ -117,7 +131,7 @@ describe("Background script", function () {
"https://urlscan.io/entry/ac04bc14-4efe-439d-b356-8384843daf75/"
);

await scan(command);
await scan(runner);
browserMock.tabs.create.assertCalls([
[
{
Expand All @@ -140,11 +154,15 @@ describe("Background script", function () {
});

it("should not call chrome.tabs.create()", async function () {
const command = new Command(
"Scan https://www.wikipedia.org/ as a url on Urlscan"
const packer = new CommandPacker(
"scan",
"https://www.wikipedia.org/",
"url",
"Urlscan"
);
const runner = new CommandRunner(packer.getJSON());

await scan(command);
await scan(runner);
browserMock.tabs.create.assertCalls([]);
});
}
Expand Down
Loading