• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ninoseki / mitaka / 14679663674

26 Apr 2025 08:53AM UTC coverage: 74.957% (-0.5%) from 75.461%
14679663674

Pull #875

github

web-flow
Merge 7d86b1b24 into 9db7c6b88
Pull Request #875: Enhance ONYPHE search

399 of 419 branches covered (95.23%)

Branch coverage included in aggregate %.

14 of 42 new or added lines in 6 files covered. (33.33%)

2 existing lines in 1 file now uncovered.

2190 of 3035 relevant lines covered (72.16%)

51.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/command/runner.ts
1
import { err, errAsync, ok, okAsync, Result, ResultAsync } from "neverthrow";
×
2

3
import { URLScan } from "~/scanner";
4
import type { CommandType, OptionsType } from "~/schemas";
5
import { Selector } from "~/selector";
×
6
import type {
7
  Scanner,
8
  ScannerMap,
9
  Searcher,
10
  SearcherMap,
11
  SelectorSlot,
12
} from "~/types";
13
import { isScanner, isSearcher } from "~/utils";
×
14

15
export class CommandRunner {
×
16
  public command: CommandType;
×
17
  protected options: OptionsType;
×
18

19
  public constructor(command: CommandType, options: OptionsType) {
×
20
    this.command = command;
×
21
    this.options = options;
×
22
  }
×
23

24
  private searcherMap: SearcherMap = {
×
25
    ip: (searcher: Searcher, query: string): Result<string, string> => {
×
26
      // Only pass the second argument if the searcher is ONYPHE and onypheType is present
NEW
27
      if (searcher.name === "ONYPHE" && this.command.onypheType) {
×
28
        // Pass the type string directly for ONYPHE
NEW
29
        return searcher.searchByIP(query, this.command.onypheType);
×
NEW
30
      }
×
31
      return searcher.searchByIP(query);
×
32
    },
×
33
    domain: (searcher: Searcher, query: string): Result<string, string> => {
×
34
      return searcher.searchByDomain(query);
×
35
    },
×
36
    url: (searcher: Searcher, query: string): Result<string, string> => {
×
37
      return searcher.searchByURL(query);
×
38
    },
×
39
    asn: (searcher: Searcher, query: string): Result<string, string> => {
×
40
      return searcher.searchByASN(query);
×
41
    },
×
42
    email: (searcher: Searcher, query: string): Result<string, string> => {
×
43
      return searcher.searchByEmail(query);
×
44
    },
×
45
    hash: (searcher: Searcher, query: string): Result<string, string> => {
×
46
      return searcher.searchByHash(query);
×
47
    },
×
48
    cve: (searcher: Searcher, query: string): Result<string, string> => {
×
49
      return searcher.searchByCVE(query);
×
50
    },
×
51
    btc: (searcher: Searcher, query: string): Result<string, string> => {
×
52
      return searcher.searchByBTC(query);
×
53
    },
×
54
    gaPubID: (searcher: Searcher, query: string): Result<string, string> => {
×
55
      return searcher.searchByGAPubID(query);
×
56
    },
×
57
    gaTrackID: (searcher: Searcher, query: string): Result<string, string> => {
×
58
      return searcher.searchByGATrackID(query);
×
59
    },
×
60
    eth: (searcher: Searcher, query: string): Result<string, string> => {
×
61
      return searcher.searchByETH(query);
×
62
    },
×
63
  };
×
64

65
  public search(): Result<string, string> {
×
66
    const getSlot = (): Result<SelectorSlot, string> => {
×
67
      const selector: Selector = new Selector(this.command.query, this.options);
×
68
      const slots: SelectorSlot[] = selector.getSearcherSlots();
×
69
      const slot = slots.find((s) => s.analyzer.name === this.command.name);
×
70
      if (!slot) {
×
71
        return err(`Slot for ${this.command.name} is missing`);
×
72
      }
×
73

74
      if (!isSearcher(slot.analyzer)) {
×
75
        return err(`${slot.analyzer.name} is not a searcher`);
×
76
      }
×
77

78
      return ok(slot);
×
79
    };
×
80

81
    const search = (slot: SelectorSlot) => {
×
82
      const searcher = slot.analyzer as Searcher;
×
83
      if (this.command.type in this.searcherMap) {
×
84
        const fn = this.searcherMap[this.command.type];
×
85
        return fn(searcher, slot.query);
×
86
      }
×
87
      return err("Something goes wrong");
×
88
    };
×
89

90
    return getSlot().andThen(search);
×
91
  }
×
92

93
  public searchAll(): Result<string, string>[] {
×
94
    const selector: Selector = new Selector(this.command.query, this.options);
×
95
    const slots: SelectorSlot[] = selector.getSearcherSlots();
×
96
    const slotsWithoutAll = slots.filter(
×
97
      (slot) => slot.analyzer.name !== "all",
×
98
    );
×
99
    return slotsWithoutAll.map((slot) => {
×
100
      const searcher = slot.analyzer as Searcher;
×
101
      if (this.command.type in this.searcherMap) {
×
102
        const fn = this.searcherMap[this.command.type];
×
103
        return fn(searcher, slot.query);
×
104
      }
×
105
      return err(`${this.command.type} is not supported`);
×
106
    });
×
107
  }
×
108

109
  private scannerMap: ScannerMap = {
×
110
    ip: (scanner: Scanner, query: string) => {
×
111
      return scanner.scanByIP(query);
×
112
    },
×
113
    domain: (scanner: Scanner, query: string) => {
×
114
      return scanner.scanByDomain(query);
×
115
    },
×
116
    url: (scanner: Scanner, query: string) => {
×
117
      return scanner.scanByURL(query);
×
118
    },
×
119
  };
×
120

121
  public scan(): ResultAsync<string, string> {
×
122
    const getSlot = (): ResultAsync<SelectorSlot, string> => {
×
123
      const selector: Selector = new Selector(this.command.query);
×
124
      const slots: SelectorSlot[] = selector.getScannerSlots();
×
125
      const slot = slots.find((s) => s.analyzer.name === this.command.name);
×
126

127
      if (!slot) {
×
128
        return errAsync(`Slot for ${this.command.name} is missing`);
×
129
      }
×
130

131
      if (!isScanner(slot.analyzer)) {
×
132
        return errAsync(`${slot.analyzer.name} is not a scanner`);
×
133
      }
×
134

135
      return okAsync(slot);
×
136
    };
×
137

138
    const scan = (slot: SelectorSlot): ResultAsync<string, string> => {
×
139
      const scanner = slot.analyzer as Scanner;
×
140

141
      switch (scanner.name) {
×
142
        case "HybridAnalysis":
×
143
          scanner.setAPIKey(this.options.hybridAnalysisAPIKey);
×
144
          break;
×
145
        case "urlscan.io":
×
146
          scanner.setAPIKey(this.options.urlscanAPIKey);
×
147
          (scanner as URLScan).setVisibility(this.options.urlscanVisibility);
×
148
          break;
×
149
        case "VirusTotal":
×
150
          scanner.setAPIKey(this.options.virusTotalAPIKey);
×
151
          break;
×
152
        default:
×
153
          break;
×
154
      }
×
155

156
      if (slot.type in this.scannerMap) {
×
157
        const fn = this.scannerMap[slot.type];
×
158
        return fn(scanner, slot.query);
×
159
      }
×
160

161
      return errAsync("Something goes wrong");
×
162
    };
×
163

164
    return getSlot().andThen(scan);
×
165
  }
×
NEW
166
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc