mihari 5.2.2 → 5.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/build_frontend.sh +1 -9
- data/frontend/.browserslistrc +3 -0
- data/frontend/.eslintrc.js +33 -0
- data/frontend/.gitignore +25 -0
- data/frontend/README.md +3 -0
- data/frontend/babel.config.js +3 -0
- data/frontend/index.html +21 -0
- data/frontend/jest.config.js +9 -0
- data/frontend/package-lock.json +13216 -0
- data/frontend/package.json +54 -0
- data/frontend/public/favicon.ico +0 -0
- data/frontend/scripts/swagger_doc_to_yaml.rb +23 -0
- data/frontend/src/App.vue +27 -0
- data/frontend/src/api-helper.ts +113 -0
- data/frontend/src/api.ts +105 -0
- data/frontend/src/components/ErrorMessage.vue +32 -0
- data/frontend/src/components/Loading.vue +15 -0
- data/frontend/src/components/Navbar.vue +59 -0
- data/frontend/src/components/Pagination.vue +126 -0
- data/frontend/src/components/alert/Alert.vue +92 -0
- data/frontend/src/components/alert/Alerts.vue +66 -0
- data/frontend/src/components/alert/AlertsWithPagination.vue +91 -0
- data/frontend/src/components/alert/AlertsWrapper.vue +141 -0
- data/frontend/src/components/alert/Form.vue +185 -0
- data/frontend/src/components/artifact/AS.vue +29 -0
- data/frontend/src/components/artifact/Artifact.vue +321 -0
- data/frontend/src/components/artifact/ArtifactTag.vue +70 -0
- data/frontend/src/components/artifact/ArtifactTags.vue +29 -0
- data/frontend/src/components/artifact/ArtifactWrapper.vue +62 -0
- data/frontend/src/components/artifact/CPEs.vue +23 -0
- data/frontend/src/components/artifact/DnsRecords.vue +38 -0
- data/frontend/src/components/artifact/Ports.vue +23 -0
- data/frontend/src/components/artifact/ReverseDnsNames.vue +31 -0
- data/frontend/src/components/artifact/Tags.vue +29 -0
- data/frontend/src/components/artifact/WhoisRecord.vue +49 -0
- data/frontend/src/components/config/Configs.vue +68 -0
- data/frontend/src/components/config/ConfigsWrapper.vue +40 -0
- data/frontend/src/components/link/Link.vue +32 -0
- data/frontend/src/components/link/Links.vue +47 -0
- data/frontend/src/components/rule/EditRule.vue +74 -0
- data/frontend/src/components/rule/EditRuleWrapper.vue +56 -0
- data/frontend/src/components/rule/Form.vue +160 -0
- data/frontend/src/components/rule/InputForm.vue +80 -0
- data/frontend/src/components/rule/NewRule.vue +60 -0
- data/frontend/src/components/rule/Rule.vue +108 -0
- data/frontend/src/components/rule/RuleWrapper.vue +62 -0
- data/frontend/src/components/rule/Rules.vue +88 -0
- data/frontend/src/components/rule/RulesWrapper.vue +130 -0
- data/frontend/src/components/rule/YAML.vue +47 -0
- data/frontend/src/components/tag/Tag.vue +73 -0
- data/frontend/src/components/tag/Tags.vue +37 -0
- data/frontend/src/countries.ts +350 -0
- data/frontend/src/index.ts +23 -0
- data/frontend/src/links/anyrun.ts +19 -0
- data/frontend/src/links/base.ts +14 -0
- data/frontend/src/links/censys.ts +20 -0
- data/frontend/src/links/crtsh.ts +20 -0
- data/frontend/src/links/dnslytics.ts +38 -0
- data/frontend/src/links/greynoise.ts +20 -0
- data/frontend/src/links/index.ts +40 -0
- data/frontend/src/links/intezer.ts +20 -0
- data/frontend/src/links/otx.ts +33 -0
- data/frontend/src/links/securitytrails.ts +38 -0
- data/frontend/src/links/shodan.ts +20 -0
- data/frontend/src/links/urlscan.ts +50 -0
- data/frontend/src/links/virustotal.ts +72 -0
- data/frontend/src/main.ts +11 -0
- data/frontend/src/router/index.ts +57 -0
- data/frontend/src/rule.ts +14 -0
- data/frontend/src/shims-vue.d.ts +6 -0
- data/frontend/src/swagger.yaml +737 -0
- data/frontend/src/types.ts +188 -0
- data/frontend/src/utils.ts +60 -0
- data/frontend/src/views/Alerts.vue +20 -0
- data/frontend/src/views/Artifact.vue +44 -0
- data/frontend/src/views/Configs.vue +20 -0
- data/frontend/src/views/EditRule.vue +44 -0
- data/frontend/src/views/NewRule.vue +26 -0
- data/frontend/src/views/Rule.vue +44 -0
- data/frontend/src/views/Rules.vue +20 -0
- data/frontend/tests/unit/utils.spec.ts +7 -0
- data/frontend/tsconfig.json +40 -0
- data/frontend/vite.config.js +24 -0
- data/lefthook.yml +10 -0
- data/lib/mihari/analyzers/base.rb +22 -5
- data/lib/mihari/analyzers/binaryedge.rb +0 -1
- data/lib/mihari/analyzers/censys.rb +7 -2
- data/lib/mihari/analyzers/circl.rb +1 -1
- data/lib/mihari/analyzers/passivetotal.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +43 -73
- data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -2
- data/lib/mihari/clients/base.rb +1 -1
- data/lib/mihari/commands/database.rb +12 -11
- data/lib/mihari/commands/rule.rb +47 -45
- data/lib/mihari/commands/search.rb +73 -45
- data/lib/mihari/commands/version.rb +8 -6
- data/lib/mihari/commands/web.rb +26 -23
- data/lib/mihari/emitters/base.rb +14 -1
- data/lib/mihari/emitters/database.rb +3 -10
- data/lib/mihari/emitters/misp.rb +16 -5
- data/lib/mihari/emitters/slack.rb +13 -15
- data/lib/mihari/emitters/the_hive.rb +17 -19
- data/lib/mihari/emitters/webhook.rb +23 -23
- data/lib/mihari/enrichers/whois.rb +1 -0
- data/lib/mihari/entities/rule.rb +1 -1
- data/lib/mihari/entities/tag.rb +1 -1
- data/lib/mihari/feed/parser.rb +1 -0
- data/lib/mihari/feed/reader.rb +29 -14
- data/lib/mihari/mixins/configurable.rb +13 -4
- data/lib/mihari/schemas/analyzer.rb +2 -7
- data/lib/mihari/schemas/rule.rb +1 -1
- data/lib/mihari/structs/censys.rb +96 -82
- data/lib/mihari/structs/config.rb +46 -21
- data/lib/mihari/structs/google_public_dns.rb +27 -23
- data/lib/mihari/structs/greynoise.rb +44 -38
- data/lib/mihari/structs/onyphe.rb +34 -30
- data/lib/mihari/structs/rule.rb +1 -1
- data/lib/mihari/structs/shodan.rb +77 -69
- data/lib/mihari/structs/urlscan.rb +42 -36
- data/lib/mihari/structs/virustotal_intelligence.rb +57 -49
- data/lib/mihari/type_checker.rb +10 -8
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/public/assets/index-ac4e5ffa.js +50 -0
- data/lib/mihari/web/public/index.html +1 -1
- data/mihari.gemspec +8 -8
- metadata +103 -22
- data/.gitmodules +0 -0
- data/.overcommit.yml +0 -12
- data/lib/mihari/web/public/assets/index-cbe1734c.js +0 -50
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export interface Pagination {
|
|
2
|
+
total: number;
|
|
3
|
+
currentPage: number;
|
|
4
|
+
pageSize: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ConfigValue {
|
|
8
|
+
key: string;
|
|
9
|
+
value: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Config {
|
|
13
|
+
name: string;
|
|
14
|
+
isConfigured: boolean;
|
|
15
|
+
values: ConfigValue[];
|
|
16
|
+
type: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Tag {
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Tags {
|
|
24
|
+
tags: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RuleSet {
|
|
28
|
+
ruleIds: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DnsRecord {
|
|
32
|
+
resource: string;
|
|
33
|
+
value: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface Contact {
|
|
37
|
+
name: string | null;
|
|
38
|
+
organization: string | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Registrar {
|
|
42
|
+
name: string | null;
|
|
43
|
+
organization: string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface WhoisRecord {
|
|
47
|
+
createdOn: Date | null;
|
|
48
|
+
updatedOn: Date | null;
|
|
49
|
+
expiresOn: Date | null;
|
|
50
|
+
registrar: Registrar | null;
|
|
51
|
+
contacts: Contact[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface AutonomousSystem {
|
|
55
|
+
asn: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface Geolocation {
|
|
59
|
+
country: string;
|
|
60
|
+
countryCode: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ReverseDnsName {
|
|
64
|
+
name: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CPE {
|
|
68
|
+
cpe: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface Port {
|
|
72
|
+
port: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface Artifact {
|
|
76
|
+
id: string;
|
|
77
|
+
data: string;
|
|
78
|
+
dataType: string;
|
|
79
|
+
source: string;
|
|
80
|
+
metadata: unknown | null;
|
|
81
|
+
createdAt: string;
|
|
82
|
+
|
|
83
|
+
autonomousSystem: AutonomousSystem | null;
|
|
84
|
+
whoisRecord: WhoisRecord | null;
|
|
85
|
+
geolocation: Geolocation | null;
|
|
86
|
+
|
|
87
|
+
dnsRecords: DnsRecord[] | null;
|
|
88
|
+
reverseDnsNames: ReverseDnsName[] | null;
|
|
89
|
+
cpes: CPE[] | null;
|
|
90
|
+
ports: Port[] | null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ArtifactWithTags extends Artifact {
|
|
94
|
+
tags: string[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface Alert {
|
|
98
|
+
id: string;
|
|
99
|
+
ruleId: string;
|
|
100
|
+
createdAt: string;
|
|
101
|
+
|
|
102
|
+
tags: Tag[];
|
|
103
|
+
artifacts: Artifact[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface Alerts extends Pagination {
|
|
107
|
+
alerts: Alert[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface PaginationParams {
|
|
111
|
+
page: number | undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface AlertSearchParams extends PaginationParams {
|
|
115
|
+
artifact: string | undefined;
|
|
116
|
+
ruleId: string | undefined;
|
|
117
|
+
tag: string | undefined;
|
|
118
|
+
fromAt: string | undefined;
|
|
119
|
+
toAt: string | undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface IPInfo {
|
|
123
|
+
ip: string;
|
|
124
|
+
hostname: string | null;
|
|
125
|
+
loc: string;
|
|
126
|
+
countryCode: string;
|
|
127
|
+
asn: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface GCS {
|
|
131
|
+
lat: number;
|
|
132
|
+
long: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface Country {
|
|
136
|
+
name: string;
|
|
137
|
+
code: string;
|
|
138
|
+
lat: number;
|
|
139
|
+
long: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type LinkType = "ip" | "domain" | "url" | "hash";
|
|
143
|
+
|
|
144
|
+
export interface Link {
|
|
145
|
+
name: string;
|
|
146
|
+
type: string;
|
|
147
|
+
baseURL: string;
|
|
148
|
+
// eslint-disable-next-line no-unused-vars
|
|
149
|
+
href(data: string): string;
|
|
150
|
+
favicon(): string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface Rule {
|
|
154
|
+
id: string;
|
|
155
|
+
title: string;
|
|
156
|
+
description: string;
|
|
157
|
+
yaml: string;
|
|
158
|
+
createdAt: string;
|
|
159
|
+
updatedAt: string;
|
|
160
|
+
tags: Tag[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface CreateRule {
|
|
164
|
+
yaml: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface UpdateRule {
|
|
168
|
+
id: string;
|
|
169
|
+
yaml: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface Query {
|
|
173
|
+
analyzer: string;
|
|
174
|
+
query: string;
|
|
175
|
+
interval: null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface Rules extends Pagination {
|
|
179
|
+
rules: Rule[];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface RuleSearchParams extends PaginationParams {
|
|
183
|
+
description: string | undefined;
|
|
184
|
+
tag: string | undefined;
|
|
185
|
+
title: string | undefined;
|
|
186
|
+
fromAt: string | undefined;
|
|
187
|
+
toAt: string | undefined;
|
|
188
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import relativeTime from "dayjs/plugin/relativeTime";
|
|
3
|
+
import timezone from "dayjs/plugin/timezone";
|
|
4
|
+
import utc from "dayjs/plugin/utc";
|
|
5
|
+
import { LocationQueryValue } from "vue-router";
|
|
6
|
+
|
|
7
|
+
import { getCountryByCode } from "@/countries";
|
|
8
|
+
import { GCS, IPInfo } from "@/types";
|
|
9
|
+
|
|
10
|
+
dayjs.extend(relativeTime);
|
|
11
|
+
dayjs.extend(timezone);
|
|
12
|
+
dayjs.extend(utc);
|
|
13
|
+
|
|
14
|
+
export function getLocalDatetime(datetime: string): string {
|
|
15
|
+
return dayjs(datetime).local().format("YYYY-MM-DD HH:mm:ss");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getHumanizedRelativeTime(datetime: string): string {
|
|
19
|
+
return dayjs(datetime).local().fromNow();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getGCSByCountryCode(countryCode: string): GCS | undefined {
|
|
23
|
+
const country = getCountryByCode(countryCode);
|
|
24
|
+
if (country !== undefined) {
|
|
25
|
+
return { lat: country.lat, long: country.long };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getGCSByIPInfo(ipinfo: IPInfo): GCS | undefined {
|
|
30
|
+
if (ipinfo.loc !== undefined) {
|
|
31
|
+
const numbers = ipinfo.loc.split(",");
|
|
32
|
+
if (numbers.length === 2) {
|
|
33
|
+
const lat = numbers[0];
|
|
34
|
+
const long = numbers[1];
|
|
35
|
+
|
|
36
|
+
return { lat: parseFloat(lat), long: parseFloat(long) };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return getGCSByCountryCode(ipinfo.countryCode);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function normalizeQueryParam(
|
|
43
|
+
param:
|
|
44
|
+
| undefined
|
|
45
|
+
| null
|
|
46
|
+
| string
|
|
47
|
+
| string[]
|
|
48
|
+
| LocationQueryValue
|
|
49
|
+
| LocationQueryValue[]
|
|
50
|
+
): string | undefined {
|
|
51
|
+
if (param === undefined || param === null) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof param === "string") {
|
|
56
|
+
return param;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return param.toString();
|
|
60
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Alerts></Alerts>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent } from "vue";
|
|
8
|
+
|
|
9
|
+
import Alerts from "@/components/alert/AlertsWrapper.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "AlertsView",
|
|
13
|
+
components: {
|
|
14
|
+
Alerts,
|
|
15
|
+
},
|
|
16
|
+
setup() {
|
|
17
|
+
useTitle("Alerts - Mihari");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Artifact :id="artifactId"></Artifact>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent, onMounted, ref, watch } from "vue";
|
|
8
|
+
|
|
9
|
+
import Artifact from "@/components/artifact/ArtifactWrapper.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "ArtifactView",
|
|
13
|
+
components: {
|
|
14
|
+
Artifact,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
id: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
setup(props) {
|
|
23
|
+
const artifactId = ref<string>(props.id);
|
|
24
|
+
|
|
25
|
+
const updateTitle = () => {
|
|
26
|
+
useTitle(`Artifact:${artifactId.value} - Mihari`);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
onMounted(() => {
|
|
30
|
+
updateTitle();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
watch(
|
|
34
|
+
() => props.id,
|
|
35
|
+
() => {
|
|
36
|
+
artifactId.value = props.id;
|
|
37
|
+
updateTitle();
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return { artifactId };
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Configs></Configs>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent } from "vue";
|
|
8
|
+
|
|
9
|
+
import Configs from "@/components/config/ConfigsWrapper.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "ConfigView",
|
|
13
|
+
components: {
|
|
14
|
+
Configs,
|
|
15
|
+
},
|
|
16
|
+
setup() {
|
|
17
|
+
useTitle("Config - Mihari");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<EditRule :id="id"></EditRule>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent, onMounted, ref, watch } from "vue";
|
|
8
|
+
|
|
9
|
+
import EditRule from "@/components/rule/EditRuleWrapper.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "EditRuleView",
|
|
13
|
+
components: {
|
|
14
|
+
EditRule,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
id: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
setup(props) {
|
|
23
|
+
const ruleId = ref<string>(props.id);
|
|
24
|
+
|
|
25
|
+
const updateTitle = () => {
|
|
26
|
+
useTitle(`Edit rule:${ruleId.value} - Mihari`);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
onMounted(() => {
|
|
30
|
+
updateTitle();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
watch(
|
|
34
|
+
() => props.id,
|
|
35
|
+
() => {
|
|
36
|
+
ruleId.value = props.id;
|
|
37
|
+
updateTitle();
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return { ruleId };
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<NewRule></NewRule>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent, onMounted } from "vue";
|
|
8
|
+
|
|
9
|
+
import NewRule from "@/components/rule/NewRule.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "NewRuleView",
|
|
13
|
+
components: {
|
|
14
|
+
NewRule,
|
|
15
|
+
},
|
|
16
|
+
setup() {
|
|
17
|
+
const updateTitle = () => {
|
|
18
|
+
useTitle(`New rule - Mihari`);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
onMounted(() => {
|
|
22
|
+
updateTitle();
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Rule :id="ruleId"></Rule>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent, onMounted, ref, watch } from "vue";
|
|
8
|
+
|
|
9
|
+
import Rule from "@/components/rule/RuleWrapper.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "RuleView",
|
|
13
|
+
components: {
|
|
14
|
+
Rule,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
id: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
setup(props) {
|
|
23
|
+
const ruleId = ref<string>(props.id);
|
|
24
|
+
|
|
25
|
+
const updateTitle = () => {
|
|
26
|
+
useTitle(`Rule:${ruleId.value} - Mihari`);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
onMounted(() => {
|
|
30
|
+
updateTitle();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
watch(
|
|
34
|
+
() => props.id,
|
|
35
|
+
() => {
|
|
36
|
+
ruleId.value = props.id;
|
|
37
|
+
updateTitle();
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return { ruleId };
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Rules></Rules>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { useTitle } from "@vueuse/core";
|
|
7
|
+
import { defineComponent } from "vue";
|
|
8
|
+
|
|
9
|
+
import Rules from "@/components/rule/RulesWrapper.vue";
|
|
10
|
+
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
name: "RulesView",
|
|
13
|
+
components: {
|
|
14
|
+
Rules,
|
|
15
|
+
},
|
|
16
|
+
setup() {
|
|
17
|
+
useTitle("Rules - Mihari");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"jsx": "preserve",
|
|
7
|
+
"importHelpers": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"baseUrl": ".",
|
|
14
|
+
"types": [
|
|
15
|
+
"webpack-env",
|
|
16
|
+
"jest"
|
|
17
|
+
],
|
|
18
|
+
"paths": {
|
|
19
|
+
"@/*": [
|
|
20
|
+
"src/*"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"lib": [
|
|
24
|
+
"esnext",
|
|
25
|
+
"dom",
|
|
26
|
+
"dom.iterable",
|
|
27
|
+
"scripthost"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"include": [
|
|
31
|
+
"src/**/*.ts",
|
|
32
|
+
"src/**/*.tsx",
|
|
33
|
+
"src/**/*.vue",
|
|
34
|
+
"tests/**/*.ts",
|
|
35
|
+
"tests/**/*.tsx"
|
|
36
|
+
],
|
|
37
|
+
"exclude": [
|
|
38
|
+
"node_modules"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import vue from "@vitejs/plugin-vue";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { defineConfig, loadEnv } from "vite";
|
|
4
|
+
|
|
5
|
+
export default defineConfig(({ _, mode }) => {
|
|
6
|
+
const env = loadEnv(mode, process.cwd(), "");
|
|
7
|
+
const target = env.BACKEND_URL || "http://localhost:9292/";
|
|
8
|
+
const port = env.port || 8080;
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
plugins: [vue()],
|
|
12
|
+
server: {
|
|
13
|
+
port,
|
|
14
|
+
proxy: {
|
|
15
|
+
"/api": target,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
resolve: {
|
|
19
|
+
alias: {
|
|
20
|
+
"@": path.resolve(__dirname, "./src"),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
});
|
data/lefthook.yml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pre-commit:
|
|
2
|
+
parallel: true
|
|
3
|
+
commands:
|
|
4
|
+
standard:
|
|
5
|
+
glob: "*.rb"
|
|
6
|
+
run: bundle exec standardrb --fix {staged_files} && git add {staged_files}
|
|
7
|
+
eslint:
|
|
8
|
+
root: "frontend/"
|
|
9
|
+
glob: "*.{js,ts,vue}"
|
|
10
|
+
run: npx eslint --fix {staged_files} && git add {staged_files}
|
|
@@ -35,17 +35,34 @@ module Mihari
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# @return [String]
|
|
39
|
-
def source
|
|
40
|
-
self.class.to_s.split("::").last.to_s
|
|
41
|
-
end
|
|
42
|
-
|
|
43
38
|
# @return [String]
|
|
44
39
|
def class_name
|
|
45
40
|
self.class.to_s.split("::").last
|
|
46
41
|
end
|
|
47
42
|
|
|
43
|
+
alias_method :source, :class_name
|
|
44
|
+
|
|
48
45
|
class << self
|
|
46
|
+
#
|
|
47
|
+
# Initialize an analyzer by query params
|
|
48
|
+
#
|
|
49
|
+
# @param [Hash] params
|
|
50
|
+
#
|
|
51
|
+
# @return [Mihari::Analyzers::Base]
|
|
52
|
+
#
|
|
53
|
+
def from_query(params)
|
|
54
|
+
# get options and set default value as an empty hash
|
|
55
|
+
options = params[:options] || {}
|
|
56
|
+
|
|
57
|
+
# set interval in the top level
|
|
58
|
+
interval = options[:interval]
|
|
59
|
+
params[:interval] = interval if interval
|
|
60
|
+
|
|
61
|
+
query = params[:query]
|
|
62
|
+
|
|
63
|
+
new(query, **params)
|
|
64
|
+
end
|
|
65
|
+
|
|
49
66
|
def inherited(child)
|
|
50
67
|
super
|
|
51
68
|
Mihari.analyzers << child
|
|
@@ -37,7 +37,12 @@ module Mihari
|
|
|
37
37
|
response = client.search(query, cursor: cursor)
|
|
38
38
|
artifacts << response.result.to_artifacts
|
|
39
39
|
cursor = response.result.links.next
|
|
40
|
-
|
|
40
|
+
# NOTE: Censys's search API is unstable recently
|
|
41
|
+
# it may returns empty links or empty string cursors
|
|
42
|
+
# - Empty links: "links": {}
|
|
43
|
+
# - Empty cursors: "links": { "next": "", "prev": "" }
|
|
44
|
+
# So it needs to check both cases
|
|
45
|
+
break if cursor.nil? || cursor.empty?
|
|
41
46
|
|
|
42
47
|
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
|
43
48
|
sleep interval
|
|
@@ -50,7 +55,7 @@ module Mihari
|
|
|
50
55
|
# @return [Boolean]
|
|
51
56
|
#
|
|
52
57
|
def configured?
|
|
53
|
-
configuration_keys
|
|
58
|
+
configuration_keys? || (id? && secret?)
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
private
|