mihari 5.2.3 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/README.md +0 -10
  4. data/Rakefile +7 -1
  5. data/build_frontend.sh +2 -10
  6. data/frontend/.eslintrc.cjs +22 -0
  7. data/frontend/.gitignore +31 -0
  8. data/frontend/.prettierrc.json +8 -0
  9. data/frontend/README.md +3 -0
  10. data/frontend/env.d.ts +5 -0
  11. data/frontend/index.html +21 -0
  12. data/frontend/package-lock.json +8650 -0
  13. data/frontend/package.json +64 -0
  14. data/frontend/public/favicon.ico +0 -0
  15. data/frontend/scripts/swagger_doc_to_yaml.rb +23 -0
  16. data/frontend/src/App.vue +27 -0
  17. data/frontend/src/api-helper.ts +111 -0
  18. data/frontend/src/api.ts +105 -0
  19. data/frontend/src/components/ErrorMessage.vue +32 -0
  20. data/frontend/src/components/Loading.vue +15 -0
  21. data/frontend/src/components/Navbar.vue +42 -0
  22. data/frontend/src/components/Pagination.vue +119 -0
  23. data/frontend/src/components/alert/Alert.vue +87 -0
  24. data/frontend/src/components/alert/Alerts.vue +64 -0
  25. data/frontend/src/components/alert/AlertsWithPagination.vue +91 -0
  26. data/frontend/src/components/alert/AlertsWrapper.vue +134 -0
  27. data/frontend/src/components/alert/Form.vue +184 -0
  28. data/frontend/src/components/artifact/AS.vue +29 -0
  29. data/frontend/src/components/artifact/Artifact.vue +304 -0
  30. data/frontend/src/components/artifact/ArtifactTag.vue +64 -0
  31. data/frontend/src/components/artifact/ArtifactTags.vue +29 -0
  32. data/frontend/src/components/artifact/ArtifactWrapper.vue +59 -0
  33. data/frontend/src/components/artifact/CPEs.vue +23 -0
  34. data/frontend/src/components/artifact/DnsRecords.vue +38 -0
  35. data/frontend/src/components/artifact/Ports.vue +23 -0
  36. data/frontend/src/components/artifact/ReverseDnsNames.vue +31 -0
  37. data/frontend/src/components/artifact/Tags.vue +29 -0
  38. data/frontend/src/components/artifact/WhoisRecord.vue +47 -0
  39. data/frontend/src/components/config/Configs.vue +65 -0
  40. data/frontend/src/components/config/ConfigsWrapper.vue +34 -0
  41. data/frontend/src/components/link/Link.vue +32 -0
  42. data/frontend/src/components/link/Links.vue +42 -0
  43. data/frontend/src/components/rule/EditRule.vue +74 -0
  44. data/frontend/src/components/rule/EditRuleWrapper.vue +50 -0
  45. data/frontend/src/components/rule/Form.vue +160 -0
  46. data/frontend/src/components/rule/InputForm.vue +86 -0
  47. data/frontend/src/components/rule/NewRule.vue +60 -0
  48. data/frontend/src/components/rule/Rule.vue +106 -0
  49. data/frontend/src/components/rule/RuleWrapper.vue +55 -0
  50. data/frontend/src/components/rule/Rules.vue +84 -0
  51. data/frontend/src/components/rule/RulesWrapper.vue +127 -0
  52. data/frontend/src/components/rule/YAML.vue +44 -0
  53. data/frontend/src/components/tag/Tag.vue +65 -0
  54. data/frontend/src/components/tag/Tags.vue +37 -0
  55. data/frontend/src/countries.ts +350 -0
  56. data/frontend/src/index.ts +20 -0
  57. data/frontend/src/links/anyrun.ts +19 -0
  58. data/frontend/src/links/base.ts +14 -0
  59. data/frontend/src/links/censys.ts +20 -0
  60. data/frontend/src/links/crtsh.ts +20 -0
  61. data/frontend/src/links/dnslytics.ts +38 -0
  62. data/frontend/src/links/greynoise.ts +20 -0
  63. data/frontend/src/links/index.ts +40 -0
  64. data/frontend/src/links/intezer.ts +20 -0
  65. data/frontend/src/links/otx.ts +33 -0
  66. data/frontend/src/links/securitytrails.ts +38 -0
  67. data/frontend/src/links/shodan.ts +20 -0
  68. data/frontend/src/links/urlscan.ts +50 -0
  69. data/frontend/src/links/virustotal.ts +72 -0
  70. data/frontend/src/main.ts +11 -0
  71. data/frontend/src/router/index.ts +57 -0
  72. data/frontend/src/rule.ts +14 -0
  73. data/frontend/src/shims-vue.d.ts +6 -0
  74. data/frontend/src/swagger.yaml +737 -0
  75. data/frontend/src/types.ts +188 -0
  76. data/frontend/src/utils.ts +54 -0
  77. data/frontend/src/views/Alerts.vue +20 -0
  78. data/frontend/src/views/Artifact.vue +44 -0
  79. data/frontend/src/views/Configs.vue +20 -0
  80. data/frontend/src/views/EditRule.vue +44 -0
  81. data/frontend/src/views/NewRule.vue +26 -0
  82. data/frontend/src/views/Rule.vue +44 -0
  83. data/frontend/src/views/Rules.vue +20 -0
  84. data/frontend/tests/utils.spec.ts +9 -0
  85. data/frontend/tsconfig.app.json +21 -0
  86. data/frontend/tsconfig.json +14 -0
  87. data/frontend/tsconfig.node.json +13 -0
  88. data/frontend/tsconfig.vitest.json +12 -0
  89. data/frontend/vite.config.ts +24 -0
  90. data/frontend/vitest.config.ts +21 -0
  91. data/lefthook.yml +12 -0
  92. data/lib/mihari/analyzers/base.rb +63 -12
  93. data/lib/mihari/analyzers/binaryedge.rb +10 -15
  94. data/lib/mihari/analyzers/censys.rb +12 -15
  95. data/lib/mihari/analyzers/circl.rb +10 -10
  96. data/lib/mihari/analyzers/crtsh.rb +10 -6
  97. data/lib/mihari/analyzers/dnstwister.rb +6 -8
  98. data/lib/mihari/analyzers/feed.rb +21 -10
  99. data/lib/mihari/analyzers/greynoise.rb +10 -20
  100. data/lib/mihari/analyzers/onyphe.rb +9 -14
  101. data/lib/mihari/analyzers/otx.rb +8 -9
  102. data/lib/mihari/analyzers/passivetotal.rb +10 -10
  103. data/lib/mihari/analyzers/pulsedive.rb +21 -31
  104. data/lib/mihari/analyzers/rule.rb +8 -29
  105. data/lib/mihari/analyzers/securitytrails.rb +8 -6
  106. data/lib/mihari/analyzers/shodan.rb +8 -13
  107. data/lib/mihari/analyzers/urlscan.rb +15 -20
  108. data/lib/mihari/analyzers/virustotal.rb +16 -26
  109. data/lib/mihari/analyzers/virustotal_intelligence.rb +11 -17
  110. data/lib/mihari/analyzers/zoomeye.rb +12 -17
  111. data/lib/mihari/commands/search.rb +16 -7
  112. data/lib/mihari/config.rb +133 -0
  113. data/lib/mihari/constants.rb +3 -0
  114. data/lib/mihari/emitters/slack.rb +13 -3
  115. data/lib/mihari/entities/rule.rb +1 -1
  116. data/lib/mihari/entities/tag.rb +1 -1
  117. data/lib/mihari/errors.rb +1 -1
  118. data/lib/mihari/http.rb +2 -3
  119. data/lib/mihari/schemas/analyzer.rb +4 -7
  120. data/lib/mihari/schemas/rule.rb +1 -1
  121. data/lib/mihari/structs/config.rb +39 -16
  122. data/lib/mihari/structs/rule.rb +1 -1
  123. data/lib/mihari/type_checker.rb +6 -6
  124. data/lib/mihari/version.rb +1 -1
  125. data/lib/mihari/web/endpoints/configs.rb +5 -1
  126. data/lib/mihari/web/public/assets/{index-eed1bcd8.css → index-2ba8f0a6.css} +1 -1
  127. data/lib/mihari/web/public/assets/index-71285b15.js +50 -0
  128. data/lib/mihari/web/public/index.html +2 -2
  129. data/lib/mihari/web/public/redoc-static.html +388 -2193
  130. data/lib/mihari.rb +9 -59
  131. data/mihari.gemspec +13 -13
  132. metadata +112 -69
  133. data/.gitmodules +0 -0
  134. data/.overcommit.yml +0 -12
  135. data/lib/mihari/web/public/assets/index-cbe1734c.js +0 -50
@@ -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 type { 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,134 @@
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 v-if="getAlertsTask.isError" :error="getAlertsTask.last?.error"></ErrorMessage>
35
+
36
+ <AlertsComponent
37
+ :alerts="getAlertsTask.last.value"
38
+ v-if="getAlertsTask.last?.value"
39
+ @refresh-page="refreshPage"
40
+ @update-page="updatePage"
41
+ @update-tag="updateTag"
42
+ ></AlertsComponent>
43
+ </div>
44
+ </template>
45
+
46
+ <script lang="ts">
47
+ import { defineComponent, nextTick, onMounted, ref, watch } from "vue"
48
+
49
+ import { generateGetAlertsTask, generateGetRuleSetTask, generateGetTagsTask } from "@/api-helper"
50
+ import AlertsComponent from "@/components/alert/Alerts.vue"
51
+ import FormComponent from "@/components/alert/Form.vue"
52
+ import ErrorMessage from "@/components/ErrorMessage.vue"
53
+ import Loading from "@/components/Loading.vue"
54
+ import type { AlertSearchParams } from "@/types"
55
+
56
+ export default defineComponent({
57
+ name: "AlertsWrapper",
58
+ components: {
59
+ AlertsComponent,
60
+ FormComponent,
61
+ Loading,
62
+ ErrorMessage
63
+ },
64
+ setup() {
65
+ const page = ref(1)
66
+ const tag = ref<string | undefined>(undefined)
67
+ const form = ref<InstanceType<typeof FormComponent>>()
68
+
69
+ const getAlertsTask = generateGetAlertsTask()
70
+ const getTagsTask = generateGetTagsTask()
71
+ const getRuleSetTask = generateGetRuleSetTask()
72
+
73
+ const getAlerts = async () => {
74
+ const params = form.value?.getSearchParams() as AlertSearchParams
75
+ return await getAlertsTask.perform(params)
76
+ }
77
+
78
+ const updatePage = (newPage: number) => {
79
+ page.value = newPage
80
+ }
81
+
82
+ const resetPage = () => {
83
+ page.value = 1
84
+ }
85
+
86
+ const search = async () => {
87
+ // reset page
88
+ resetPage()
89
+
90
+ await getAlerts()
91
+ }
92
+
93
+ const updateTag = (newTag: string | undefined) => {
94
+ if (tag.value === newTag) {
95
+ tag.value = undefined
96
+ } else {
97
+ tag.value = newTag
98
+ }
99
+
100
+ nextTick(async () => await search())
101
+ }
102
+
103
+ const refreshPage = async () => {
104
+ // it is just an alias of search
105
+ // this function will be invoked when an alert is deleted
106
+ await search()
107
+ }
108
+
109
+ onMounted(async () => {
110
+ getTagsTask.perform()
111
+ getRuleSetTask.perform()
112
+
113
+ await getAlerts()
114
+ })
115
+
116
+ watch([page, tag], async () => {
117
+ nextTick(async () => await getAlerts())
118
+ })
119
+
120
+ return {
121
+ getAlertsTask,
122
+ getRuleSetTask,
123
+ getTagsTask,
124
+ refreshPage,
125
+ search,
126
+ tag,
127
+ updatePage,
128
+ updateTag,
129
+ form,
130
+ page
131
+ }
132
+ }
133
+ })
134
+ </script>
@@ -0,0 +1,184 @@
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, type PropType, ref, watch } from "vue"
99
+ import { useRoute } from "vue-router"
100
+
101
+ import type { 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 = normalizedAsn === undefined ? undefined : parseInt(normalizedAsn)
140
+
141
+ const dnsRecord_ = route.query["dnsRecord"]
142
+ dnsRecord.value = normalizeQueryParam(dnsRecord_)
143
+
144
+ const reverseDnsName_ = route.query["reverseDnsName"]
145
+ reverseDnsName.value = normalizeQueryParam(reverseDnsName_)
146
+
147
+ const tag_ = route.query["tag"]
148
+ if (tagInput.value === undefined) {
149
+ tagInput.value = normalizeQueryParam(tag_)
150
+ }
151
+ }
152
+
153
+ const getSearchParams = (): AlertSearchParams => {
154
+ updateByQueryParams()
155
+
156
+ const params: AlertSearchParams = {
157
+ artifact: artifact.value === "" ? undefined : artifact.value,
158
+ page: props.page,
159
+ ruleId: ruleId.value === "" ? undefined : ruleId.value,
160
+ tag: tagInput.value === "" ? undefined : tagInput.value,
161
+ toAt: toAt.value === "" ? undefined : toAt.value,
162
+ fromAt: fromAt.value === "" ? undefined : fromAt.value
163
+ }
164
+ return params
165
+ }
166
+
167
+ watch(
168
+ () => props.tag,
169
+ () => {
170
+ tagInput.value = props.tag
171
+ }
172
+ )
173
+
174
+ return {
175
+ artifact,
176
+ fromAt,
177
+ getSearchParams,
178
+ ruleId,
179
+ toAt,
180
+ tagInput
181
+ }
182
+ }
183
+ })
184
+ </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, type PropType } from "vue"
17
+
18
+ import type { 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>