mihari 5.2.3 → 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/rule.rb +8 -29
- data/lib/mihari/commands/search.rb +16 -7
- data/lib/mihari/entities/rule.rb +1 -1
- data/lib/mihari/entities/tag.rb +1 -1
- data/lib/mihari/schemas/analyzer.rb +2 -7
- data/lib/mihari/schemas/rule.rb +1 -1
- data/lib/mihari/structs/config.rb +39 -16
- data/lib/mihari/structs/rule.rb +1 -1
- 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 +5 -5
- metadata +97 -16
- 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
|
|
@@ -46,7 +46,7 @@ module Mihari
|
|
|
46
46
|
#
|
|
47
47
|
# @param [Mihari::Structs::Rule] rule
|
|
48
48
|
#
|
|
49
|
-
def initialize(rule
|
|
49
|
+
def initialize(rule)
|
|
50
50
|
@rule = rule
|
|
51
51
|
@base_time = Time.now.utc
|
|
52
52
|
|
|
@@ -153,15 +153,6 @@ module Mihari
|
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
-
#
|
|
157
|
-
# Deep copied queries
|
|
158
|
-
#
|
|
159
|
-
# @return [Array<Hash>]
|
|
160
|
-
#
|
|
161
|
-
def queries
|
|
162
|
-
rule.queries.map(&:deep_dup)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
156
|
#
|
|
166
157
|
# Get analyzer class
|
|
167
158
|
#
|
|
@@ -177,26 +168,13 @@ module Mihari
|
|
|
177
168
|
end
|
|
178
169
|
|
|
179
170
|
#
|
|
180
|
-
# @return [Array<Mihari::Analyzers::Base>]
|
|
171
|
+
# @return [Array<Mihari::Analyzers::Base>]
|
|
181
172
|
#
|
|
182
173
|
def analyzers
|
|
183
|
-
@analyzers ||= queries.map do |
|
|
184
|
-
analyzer_name =
|
|
174
|
+
@analyzers ||= rule.queries.map do |query_params|
|
|
175
|
+
analyzer_name = query_params[:analyzer]
|
|
185
176
|
klass = get_analyzer_class(analyzer_name)
|
|
186
|
-
|
|
187
|
-
# set interval in the top level
|
|
188
|
-
options = params[:options] || {}
|
|
189
|
-
interval = options[:interval]
|
|
190
|
-
params[:interval] = interval if interval
|
|
191
|
-
|
|
192
|
-
# set rule
|
|
193
|
-
params[:rule] = rule
|
|
194
|
-
query = params[:query]
|
|
195
|
-
|
|
196
|
-
analyzer = klass.new(query, **params)
|
|
197
|
-
raise ConfigurationError, "#{analyzer.source} is not configured correctly" unless analyzer.configured?
|
|
198
|
-
|
|
199
|
-
analyzer
|
|
177
|
+
klass.from_query(query_params)
|
|
200
178
|
end
|
|
201
179
|
end
|
|
202
180
|
|
|
@@ -240,8 +218,9 @@ module Mihari
|
|
|
240
218
|
# Validate configuration of analyzers
|
|
241
219
|
#
|
|
242
220
|
def validate_analyzer_configurations
|
|
243
|
-
|
|
244
|
-
|
|
221
|
+
analyzers.map do |analyzer|
|
|
222
|
+
raise ConfigurationError, "#{analyzer.source} is not configured correctly" unless analyzer.configured?
|
|
223
|
+
end
|
|
245
224
|
end
|
|
246
225
|
end
|
|
247
226
|
end
|