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.
Files changed (102) 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/rule.rb +8 -29
  88. data/lib/mihari/commands/search.rb +16 -7
  89. data/lib/mihari/entities/rule.rb +1 -1
  90. data/lib/mihari/entities/tag.rb +1 -1
  91. data/lib/mihari/schemas/analyzer.rb +2 -7
  92. data/lib/mihari/schemas/rule.rb +1 -1
  93. data/lib/mihari/structs/config.rb +39 -16
  94. data/lib/mihari/structs/rule.rb +1 -1
  95. data/lib/mihari/version.rb +1 -1
  96. data/lib/mihari/web/public/assets/index-ac4e5ffa.js +50 -0
  97. data/lib/mihari/web/public/index.html +1 -1
  98. data/mihari.gemspec +5 -5
  99. metadata +97 -16
  100. data/.gitmodules +0 -0
  101. data/.overcommit.yml +0 -12
  102. 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>