mihari 5.2.2 → 5.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|