mihari 5.2.2 → 5.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/build_frontend.sh +1 -9
  3. data/frontend/.browserslistrc +3 -0
  4. data/frontend/.eslintrc.js +33 -0
  5. data/frontend/.gitignore +25 -0
  6. data/frontend/README.md +3 -0
  7. data/frontend/babel.config.js +3 -0
  8. data/frontend/index.html +21 -0
  9. data/frontend/jest.config.js +9 -0
  10. data/frontend/package-lock.json +13216 -0
  11. data/frontend/package.json +54 -0
  12. data/frontend/public/favicon.ico +0 -0
  13. data/frontend/scripts/swagger_doc_to_yaml.rb +23 -0
  14. data/frontend/src/App.vue +27 -0
  15. data/frontend/src/api-helper.ts +113 -0
  16. data/frontend/src/api.ts +105 -0
  17. data/frontend/src/components/ErrorMessage.vue +32 -0
  18. data/frontend/src/components/Loading.vue +15 -0
  19. data/frontend/src/components/Navbar.vue +59 -0
  20. data/frontend/src/components/Pagination.vue +126 -0
  21. data/frontend/src/components/alert/Alert.vue +92 -0
  22. data/frontend/src/components/alert/Alerts.vue +66 -0
  23. data/frontend/src/components/alert/AlertsWithPagination.vue +91 -0
  24. data/frontend/src/components/alert/AlertsWrapper.vue +141 -0
  25. data/frontend/src/components/alert/Form.vue +185 -0
  26. data/frontend/src/components/artifact/AS.vue +29 -0
  27. data/frontend/src/components/artifact/Artifact.vue +321 -0
  28. data/frontend/src/components/artifact/ArtifactTag.vue +70 -0
  29. data/frontend/src/components/artifact/ArtifactTags.vue +29 -0
  30. data/frontend/src/components/artifact/ArtifactWrapper.vue +62 -0
  31. data/frontend/src/components/artifact/CPEs.vue +23 -0
  32. data/frontend/src/components/artifact/DnsRecords.vue +38 -0
  33. data/frontend/src/components/artifact/Ports.vue +23 -0
  34. data/frontend/src/components/artifact/ReverseDnsNames.vue +31 -0
  35. data/frontend/src/components/artifact/Tags.vue +29 -0
  36. data/frontend/src/components/artifact/WhoisRecord.vue +49 -0
  37. data/frontend/src/components/config/Configs.vue +68 -0
  38. data/frontend/src/components/config/ConfigsWrapper.vue +40 -0
  39. data/frontend/src/components/link/Link.vue +32 -0
  40. data/frontend/src/components/link/Links.vue +47 -0
  41. data/frontend/src/components/rule/EditRule.vue +74 -0
  42. data/frontend/src/components/rule/EditRuleWrapper.vue +56 -0
  43. data/frontend/src/components/rule/Form.vue +160 -0
  44. data/frontend/src/components/rule/InputForm.vue +80 -0
  45. data/frontend/src/components/rule/NewRule.vue +60 -0
  46. data/frontend/src/components/rule/Rule.vue +108 -0
  47. data/frontend/src/components/rule/RuleWrapper.vue +62 -0
  48. data/frontend/src/components/rule/Rules.vue +88 -0
  49. data/frontend/src/components/rule/RulesWrapper.vue +130 -0
  50. data/frontend/src/components/rule/YAML.vue +47 -0
  51. data/frontend/src/components/tag/Tag.vue +73 -0
  52. data/frontend/src/components/tag/Tags.vue +37 -0
  53. data/frontend/src/countries.ts +350 -0
  54. data/frontend/src/index.ts +23 -0
  55. data/frontend/src/links/anyrun.ts +19 -0
  56. data/frontend/src/links/base.ts +14 -0
  57. data/frontend/src/links/censys.ts +20 -0
  58. data/frontend/src/links/crtsh.ts +20 -0
  59. data/frontend/src/links/dnslytics.ts +38 -0
  60. data/frontend/src/links/greynoise.ts +20 -0
  61. data/frontend/src/links/index.ts +40 -0
  62. data/frontend/src/links/intezer.ts +20 -0
  63. data/frontend/src/links/otx.ts +33 -0
  64. data/frontend/src/links/securitytrails.ts +38 -0
  65. data/frontend/src/links/shodan.ts +20 -0
  66. data/frontend/src/links/urlscan.ts +50 -0
  67. data/frontend/src/links/virustotal.ts +72 -0
  68. data/frontend/src/main.ts +11 -0
  69. data/frontend/src/router/index.ts +57 -0
  70. data/frontend/src/rule.ts +14 -0
  71. data/frontend/src/shims-vue.d.ts +6 -0
  72. data/frontend/src/swagger.yaml +737 -0
  73. data/frontend/src/types.ts +188 -0
  74. data/frontend/src/utils.ts +60 -0
  75. data/frontend/src/views/Alerts.vue +20 -0
  76. data/frontend/src/views/Artifact.vue +44 -0
  77. data/frontend/src/views/Configs.vue +20 -0
  78. data/frontend/src/views/EditRule.vue +44 -0
  79. data/frontend/src/views/NewRule.vue +26 -0
  80. data/frontend/src/views/Rule.vue +44 -0
  81. data/frontend/src/views/Rules.vue +20 -0
  82. data/frontend/tests/unit/utils.spec.ts +7 -0
  83. data/frontend/tsconfig.json +40 -0
  84. data/frontend/vite.config.js +24 -0
  85. data/lefthook.yml +10 -0
  86. data/lib/mihari/analyzers/base.rb +22 -5
  87. data/lib/mihari/analyzers/binaryedge.rb +0 -1
  88. data/lib/mihari/analyzers/censys.rb +7 -2
  89. data/lib/mihari/analyzers/circl.rb +1 -1
  90. data/lib/mihari/analyzers/passivetotal.rb +1 -1
  91. data/lib/mihari/analyzers/rule.rb +43 -73
  92. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -2
  93. data/lib/mihari/clients/base.rb +1 -1
  94. data/lib/mihari/commands/database.rb +12 -11
  95. data/lib/mihari/commands/rule.rb +47 -45
  96. data/lib/mihari/commands/search.rb +73 -45
  97. data/lib/mihari/commands/version.rb +8 -6
  98. data/lib/mihari/commands/web.rb +26 -23
  99. data/lib/mihari/emitters/base.rb +14 -1
  100. data/lib/mihari/emitters/database.rb +3 -10
  101. data/lib/mihari/emitters/misp.rb +16 -5
  102. data/lib/mihari/emitters/slack.rb +13 -15
  103. data/lib/mihari/emitters/the_hive.rb +17 -19
  104. data/lib/mihari/emitters/webhook.rb +23 -23
  105. data/lib/mihari/enrichers/whois.rb +1 -0
  106. data/lib/mihari/entities/rule.rb +1 -1
  107. data/lib/mihari/entities/tag.rb +1 -1
  108. data/lib/mihari/feed/parser.rb +1 -0
  109. data/lib/mihari/feed/reader.rb +29 -14
  110. data/lib/mihari/mixins/configurable.rb +13 -4
  111. data/lib/mihari/schemas/analyzer.rb +2 -7
  112. data/lib/mihari/schemas/rule.rb +1 -1
  113. data/lib/mihari/structs/censys.rb +96 -82
  114. data/lib/mihari/structs/config.rb +46 -21
  115. data/lib/mihari/structs/google_public_dns.rb +27 -23
  116. data/lib/mihari/structs/greynoise.rb +44 -38
  117. data/lib/mihari/structs/onyphe.rb +34 -30
  118. data/lib/mihari/structs/rule.rb +1 -1
  119. data/lib/mihari/structs/shodan.rb +77 -69
  120. data/lib/mihari/structs/urlscan.rb +42 -36
  121. data/lib/mihari/structs/virustotal_intelligence.rb +57 -49
  122. data/lib/mihari/type_checker.rb +10 -8
  123. data/lib/mihari/version.rb +1 -1
  124. data/lib/mihari/web/public/assets/index-ac4e5ffa.js +50 -0
  125. data/lib/mihari/web/public/index.html +1 -1
  126. data/mihari.gemspec +8 -8
  127. metadata +103 -22
  128. data/.gitmodules +0 -0
  129. data/.overcommit.yml +0 -12
  130. data/lib/mihari/web/public/assets/index-cbe1734c.js +0 -50
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <Alert
3
+ v-for="(alert, index) in alerts.alerts"
4
+ :alert="alert"
5
+ :key="index"
6
+ @refresh-page="refreshPage"
7
+ @update-tag="updateTag"
8
+ ></Alert>
9
+
10
+ <Pagination
11
+ :total="alerts.total"
12
+ :currentPage="alerts.currentPage"
13
+ :pageSize="alerts.pageSize"
14
+ @update-page="updatePage"
15
+ ></Pagination>
16
+ <p class="help">
17
+ ({{ alerts.total }} results in total, {{ alerts.alerts.length }} shown)
18
+ </p>
19
+ </template>
20
+
21
+ <script lang="ts">
22
+ import { defineComponent, PropType } from "vue";
23
+
24
+ import Alert from "@/components/alert/Alert.vue";
25
+ import Pagination from "@/components/Pagination.vue";
26
+ import { Alerts } from "@/types";
27
+
28
+ export default defineComponent({
29
+ name: "AlertsItem",
30
+ components: {
31
+ Alert,
32
+ Pagination,
33
+ },
34
+ props: {
35
+ alerts: {
36
+ type: Object as PropType<Alerts>,
37
+ required: true,
38
+ },
39
+ },
40
+ emits: ["update-page", "refresh-page", "update-tag"],
41
+ setup(_, context) {
42
+ const scrollToTop = () => {
43
+ window.scrollTo({
44
+ top: 0,
45
+ });
46
+ };
47
+
48
+ const updatePage = (page: number) => {
49
+ scrollToTop();
50
+ context.emit("update-page", page);
51
+ };
52
+
53
+ const refreshPage = () => {
54
+ scrollToTop();
55
+ context.emit("refresh-page");
56
+ };
57
+
58
+ const updateTag = (tag: string) => {
59
+ scrollToTop();
60
+ context.emit("update-tag", tag);
61
+ };
62
+
63
+ return { updatePage, updateTag, refreshPage };
64
+ },
65
+ });
66
+ </script>
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <Loading v-if="getAlertsTask.isRunning"></Loading>
3
+
4
+ <Alerts
5
+ :alerts="getAlertsTask.last.value"
6
+ v-if="getAlertsTask.last?.value"
7
+ @refresh-page="refreshPage"
8
+ @update-page="updatePage"
9
+ @update-tag="updateTag"
10
+ >
11
+ </Alerts>
12
+ </template>
13
+
14
+ <script lang="ts">
15
+ import { defineComponent, nextTick, onMounted, ref, watch } from "vue";
16
+
17
+ import { generateGetAlertsTask } from "@/api-helper";
18
+ import Alerts from "@/components/alert/Alerts.vue";
19
+ import Loading from "@/components/Loading.vue";
20
+ import { AlertSearchParams } from "@/types";
21
+
22
+ export default defineComponent({
23
+ name: "AlertsWithPagination",
24
+ props: {
25
+ ruleId: {
26
+ type: String,
27
+ },
28
+ artifact: {
29
+ type: String,
30
+ },
31
+ },
32
+ components: {
33
+ Alerts,
34
+ Loading,
35
+ },
36
+ setup(props) {
37
+ const page = ref(1);
38
+ const tag = ref<string | undefined>(undefined);
39
+
40
+ const getAlertsTask = generateGetAlertsTask();
41
+
42
+ const getAlerts = async () => {
43
+ const params: AlertSearchParams = {
44
+ artifact: props.artifact,
45
+ page: page.value,
46
+ ruleId: props.ruleId,
47
+ tag: tag.value,
48
+ toAt: undefined,
49
+ fromAt: undefined,
50
+ };
51
+ return await getAlertsTask.perform(params);
52
+ };
53
+
54
+ const updatePage = (newPage: number) => {
55
+ page.value = newPage;
56
+ };
57
+
58
+ const resetPage = () => {
59
+ page.value = 1;
60
+ };
61
+
62
+ const refreshPage = async () => {
63
+ resetPage();
64
+ await getAlerts();
65
+ };
66
+
67
+ const updateTag = (newTag: string | undefined) => {
68
+ if (tag.value === newTag) {
69
+ tag.value = undefined;
70
+ } else {
71
+ tag.value = newTag;
72
+ }
73
+ };
74
+
75
+ onMounted(async () => {
76
+ await getAlerts();
77
+ });
78
+
79
+ watch([props, page, tag], async () => {
80
+ nextTick(async () => await getAlerts());
81
+ });
82
+
83
+ return {
84
+ getAlertsTask,
85
+ refreshPage,
86
+ updatePage,
87
+ updateTag,
88
+ };
89
+ },
90
+ });
91
+ </script>
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <div class="box mb-6">
3
+ <FormComponent
4
+ ref="form"
5
+ :ruleSet="getRuleSetTask.last?.value || []"
6
+ :tags="getTagsTask.last?.value || []"
7
+ :page="page"
8
+ :tag="tag"
9
+ ></FormComponent>
10
+
11
+ <hr />
12
+
13
+ <div class="columns">
14
+ <div class="column">
15
+ <div class="field is-grouped is-grouped-centered">
16
+ <p class="control">
17
+ <a class="button is-primary" @click="search">
18
+ <span class="icon is-small">
19
+ <i class="fas fa-search"></i>
20
+ </span>
21
+ <span>Search</span>
22
+ </a>
23
+ </p>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+
29
+ <div v-if="getAlertsTask.performCount > 0">
30
+ <hr />
31
+
32
+ <Loading v-if="getAlertsTask.isRunning"></Loading>
33
+
34
+ <ErrorMessage
35
+ v-if="getAlertsTask.isError"
36
+ :error="getAlertsTask.last?.error"
37
+ ></ErrorMessage>
38
+
39
+ <AlertsComponent
40
+ :alerts="getAlertsTask.last.value"
41
+ v-if="getAlertsTask.last?.value"
42
+ @refresh-page="refreshPage"
43
+ @update-page="updatePage"
44
+ @update-tag="updateTag"
45
+ ></AlertsComponent>
46
+ </div>
47
+ </template>
48
+
49
+ <script lang="ts">
50
+ import { defineComponent, nextTick, onMounted, ref, watch } from "vue";
51
+
52
+ import {
53
+ generateGetAlertsTask,
54
+ generateGetRuleSetTask,
55
+ generateGetTagsTask,
56
+ } from "@/api-helper";
57
+ import AlertsComponent from "@/components/alert/Alerts.vue";
58
+ import FormComponent from "@/components/alert/Form.vue";
59
+ import ErrorMessage from "@/components/ErrorMessage.vue";
60
+ import Loading from "@/components/Loading.vue";
61
+ import { AlertSearchParams } from "@/types";
62
+
63
+ export default defineComponent({
64
+ name: "AlertsWrapper",
65
+ components: {
66
+ AlertsComponent,
67
+ FormComponent,
68
+ Loading,
69
+ ErrorMessage,
70
+ },
71
+ setup() {
72
+ const page = ref(1);
73
+ const tag = ref<string | undefined>(undefined);
74
+ const form = ref<InstanceType<typeof FormComponent>>();
75
+
76
+ const getAlertsTask = generateGetAlertsTask();
77
+ const getTagsTask = generateGetTagsTask();
78
+ const getRuleSetTask = generateGetRuleSetTask();
79
+
80
+ const getAlerts = async () => {
81
+ const params = form.value?.getSearchParams() as AlertSearchParams;
82
+ return await getAlertsTask.perform(params);
83
+ };
84
+
85
+ const updatePage = (newPage: number) => {
86
+ page.value = newPage;
87
+ };
88
+
89
+ const resetPage = () => {
90
+ page.value = 1;
91
+ };
92
+
93
+ const search = async () => {
94
+ // reset page
95
+ resetPage();
96
+
97
+ await getAlerts();
98
+ };
99
+
100
+ const updateTag = (newTag: string | undefined) => {
101
+ if (tag.value === newTag) {
102
+ tag.value = undefined;
103
+ } else {
104
+ tag.value = newTag;
105
+ }
106
+
107
+ nextTick(async () => await search());
108
+ };
109
+
110
+ const refreshPage = async () => {
111
+ // it is just an alias of search
112
+ // this function will be invoked when an alert is deleted
113
+ await search();
114
+ };
115
+
116
+ onMounted(async () => {
117
+ getTagsTask.perform();
118
+ getRuleSetTask.perform();
119
+
120
+ await getAlerts();
121
+ });
122
+
123
+ watch([page, tag], async () => {
124
+ nextTick(async () => await getAlerts());
125
+ });
126
+
127
+ return {
128
+ getAlertsTask,
129
+ getRuleSetTask,
130
+ getTagsTask,
131
+ refreshPage,
132
+ search,
133
+ tag,
134
+ updatePage,
135
+ updateTag,
136
+ form,
137
+ page,
138
+ };
139
+ },
140
+ });
141
+ </script>
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <div class="columns">
3
+ <div class="column">
4
+ <div class="field is-horizontal">
5
+ <div class="field-label is-normal">
6
+ <label class="label">Rule</label>
7
+ </div>
8
+ <div class="field-body">
9
+ <div class="field">
10
+ <div class="control">
11
+ <div class="select">
12
+ <select v-model="ruleId">
13
+ <option></option>
14
+ <option v-for="ruleId_ in ruleSet" :key="ruleId_">
15
+ {{ ruleId_ }}
16
+ </option>
17
+ </select>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <div class="column">
25
+ <div class="field is-horizontal">
26
+ <div class="field-label is-normal">
27
+ <label class="label">Artifact</label>
28
+ </div>
29
+ <div class="field-body">
30
+ <div class="field">
31
+ <p class="control">
32
+ <input class="input" type="text" v-model="artifact" />
33
+ </p>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="columns">
41
+ <div class="column">
42
+ <div class="field is-horizontal">
43
+ <div class="field-label is-normal">
44
+ <label class="label">Tag</label>
45
+ </div>
46
+ <div class="field-body">
47
+ <div class="field">
48
+ <div class="control">
49
+ <div class="select">
50
+ <select v-model="tagInput">
51
+ <option></option>
52
+ <option v-for="tag_ in tags" :key="tag_">
53
+ {{ tag_ }}
54
+ </option>
55
+ </select>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ <div class="column"></div>
63
+ </div>
64
+
65
+ <div class="columns">
66
+ <div class="column">
67
+ <div class="field is-horizontal">
68
+ <div class="field-label is-normal">
69
+ <label class="label">From</label>
70
+ </div>
71
+ <div class="field-body">
72
+ <div class="field">
73
+ <p class="control">
74
+ <input class="input" type="date" v-model="fromAt" />
75
+ </p>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ <div class="column">
81
+ <div class="field is-horizontal">
82
+ <div class="field-label is-normal">
83
+ <label class="label">To</label>
84
+ </div>
85
+ <div class="field-body">
86
+ <div class="field">
87
+ <p class="control">
88
+ <input class="input" type="date" v-model="toAt" />
89
+ </p>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </template>
96
+
97
+ <script lang="ts">
98
+ import { defineComponent, PropType, ref, watch } from "vue";
99
+ import { useRoute } from "vue-router";
100
+
101
+ import { AlertSearchParams } from "@/types";
102
+ import { normalizeQueryParam } from "@/utils";
103
+
104
+ export default defineComponent({
105
+ name: "AlertsForm",
106
+ props: {
107
+ tags: {
108
+ type: Array as PropType<string[]>,
109
+ required: true,
110
+ },
111
+ ruleSet: {
112
+ type: Array as PropType<string[]>,
113
+ required: true,
114
+ },
115
+ page: {
116
+ type: Number,
117
+ required: true,
118
+ },
119
+ tag: {
120
+ type: String,
121
+ required: false,
122
+ },
123
+ },
124
+ setup(props) {
125
+ const route = useRoute();
126
+
127
+ const artifact = ref<string | undefined>(undefined);
128
+ const fromAt = ref<string | undefined>(undefined);
129
+ const tagInput = ref<string | undefined>(props.tag);
130
+ const ruleId = ref<string | undefined>(undefined);
131
+ const toAt = ref<string | undefined>(undefined);
132
+ const asn = ref<number | undefined>(undefined);
133
+ const dnsRecord = ref<string | undefined>(undefined);
134
+ const reverseDnsName = ref<string | undefined>(undefined);
135
+
136
+ const updateByQueryParams = () => {
137
+ const asn_ = route.query["asn"];
138
+ const normalizedAsn = normalizeQueryParam(asn_);
139
+ asn.value =
140
+ normalizedAsn === undefined ? undefined : parseInt(normalizedAsn);
141
+
142
+ const dnsRecord_ = route.query["dnsRecord"];
143
+ dnsRecord.value = normalizeQueryParam(dnsRecord_);
144
+
145
+ const reverseDnsName_ = route.query["reverseDnsName"];
146
+ reverseDnsName.value = normalizeQueryParam(reverseDnsName_);
147
+
148
+ const tag_ = route.query["tag"];
149
+ if (tagInput.value === undefined) {
150
+ tagInput.value = normalizeQueryParam(tag_);
151
+ }
152
+ };
153
+
154
+ const getSearchParams = (): AlertSearchParams => {
155
+ updateByQueryParams();
156
+
157
+ const params: AlertSearchParams = {
158
+ artifact: artifact.value === "" ? undefined : artifact.value,
159
+ page: props.page,
160
+ ruleId: ruleId.value === "" ? undefined : ruleId.value,
161
+ tag: tagInput.value === "" ? undefined : tagInput.value,
162
+ toAt: toAt.value === "" ? undefined : toAt.value,
163
+ fromAt: fromAt.value === "" ? undefined : fromAt.value,
164
+ };
165
+ return params;
166
+ };
167
+
168
+ watch(
169
+ () => props.tag,
170
+ () => {
171
+ tagInput.value = props.tag;
172
+ }
173
+ );
174
+
175
+ return {
176
+ artifact,
177
+ fromAt,
178
+ getSearchParams,
179
+ ruleId,
180
+ toAt,
181
+ tagInput,
182
+ };
183
+ },
184
+ });
185
+ </script>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <div class="tags are-medium">
3
+ <router-link
4
+ class="tag"
5
+ :to="{
6
+ name: 'Alerts',
7
+ query: { asn: autonomousSystem.asn },
8
+ }"
9
+ >
10
+ {{ autonomousSystem.asn }}
11
+ </router-link>
12
+ </div>
13
+ </template>
14
+
15
+ <script lang="ts">
16
+ import { defineComponent, PropType } from "vue";
17
+
18
+ import { AutonomousSystem } from "@/types";
19
+
20
+ export default defineComponent({
21
+ name: "AS",
22
+ props: {
23
+ autonomousSystem: {
24
+ type: Object as PropType<AutonomousSystem>,
25
+ required: true,
26
+ },
27
+ },
28
+ });
29
+ </script>