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,108 @@
1
+ <template>
2
+ <div class="column">
3
+ <div v-if="runRuleTask.isRunning">
4
+ <Loading></Loading>
5
+ <hr />
6
+ </div>
7
+
8
+ <div v-if="runRuleTask.last?.error">
9
+ <ErrorMessage :error="runRuleTask.last.error"></ErrorMessage>
10
+ <hr />
11
+ </div>
12
+
13
+ <h2 class="is-size-2 mb-4">Rule</h2>
14
+
15
+ <p class="is-clearfix">
16
+ <span class="buttons is-pulled-right">
17
+ <button class="button is-primary is-light is-small" @click="runRule">
18
+ <span>Run</span>
19
+ <span class="icon is-small">
20
+ <i class="fas fa-arrow-right"></i>
21
+ </span>
22
+ </button>
23
+ <router-link
24
+ class="button is-info is-light is-small"
25
+ :to="{ name: 'EditRule', params: { id: rule.id } }"
26
+ >
27
+ <span>Edit</span>
28
+ <span class="icon is-small">
29
+ <i class="fas fa-edit"></i>
30
+ </span>
31
+ </router-link>
32
+ <button class="button is-light is-small" @click="deleteRule">
33
+ <span>Delete</span>
34
+ <span class="icon is-small">
35
+ <i class="fas fa-times"></i>
36
+ </span>
37
+ </button>
38
+ </span>
39
+ </p>
40
+
41
+ <YAML :yaml="rule.yaml"></YAML>
42
+ </div>
43
+
44
+ <hr />
45
+
46
+ <div class="column">
47
+ <h2 class="is-size-2 mb-4">Related alerts</h2>
48
+
49
+ <Alerts :ruleId="rule.id"></Alerts>
50
+ </div>
51
+ </template>
52
+
53
+ <script lang="ts">
54
+ import { defineComponent, PropType } from "vue";
55
+ import { useRouter } from "vue-router";
56
+
57
+ import { generateDeleteRuleTask, generateRunRuleTask } from "@/api-helper";
58
+ import Alerts from "@/components/alert/AlertsWithPagination.vue";
59
+ import ErrorMessage from "@/components/ErrorMessage.vue";
60
+ import Loading from "@/components/Loading.vue";
61
+ import YAML from "@/components/rule/YAML.vue";
62
+ import { Rule } from "@/types";
63
+
64
+ export default defineComponent({
65
+ name: "RuleItem",
66
+ props: {
67
+ rule: {
68
+ type: Object as PropType<Rule>,
69
+ required: true,
70
+ },
71
+ },
72
+ components: {
73
+ YAML,
74
+ Alerts,
75
+ Loading,
76
+ ErrorMessage,
77
+ },
78
+ emits: ["refresh"],
79
+ setup(props, context) {
80
+ const router = useRouter();
81
+
82
+ const deleteRuleTask = generateDeleteRuleTask();
83
+ const runRuleTask = generateRunRuleTask();
84
+
85
+ const deleteRule = async () => {
86
+ const result = window.confirm(
87
+ `Are you sure you want to delete ${props.rule.id}?`
88
+ );
89
+
90
+ if (result) {
91
+ await deleteRuleTask.perform(props.rule.id);
92
+ router.push("/");
93
+ }
94
+ };
95
+
96
+ const runRule = async () => {
97
+ await runRuleTask.perform(props.rule.id);
98
+ context.emit("refresh");
99
+ };
100
+
101
+ return {
102
+ deleteRule,
103
+ runRule,
104
+ runRuleTask,
105
+ };
106
+ },
107
+ });
108
+ </script>
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <Loading v-if="getRuleTask.isRunning"></Loading>
3
+
4
+ <ErrorMessage
5
+ v-if="getRuleTask.isError"
6
+ :error="getRuleTask.last?.error"
7
+ ></ErrorMessage>
8
+
9
+ <Rule
10
+ :rule="getRuleTask.last.value"
11
+ @refresh="refresh"
12
+ v-if="getRuleTask.last?.value"
13
+ ></Rule>
14
+ </template>
15
+
16
+ <script lang="ts">
17
+ import { defineComponent, onMounted, watch } from "vue";
18
+
19
+ import { generateGetRuleTask } from "@/api-helper";
20
+ import ErrorMessage from "@/components/ErrorMessage.vue";
21
+ import Loading from "@/components/Loading.vue";
22
+ import Rule from "@/components/rule/Rule.vue";
23
+
24
+ export default defineComponent({
25
+ name: "RuleWrapper",
26
+ components: {
27
+ Rule,
28
+ Loading,
29
+ ErrorMessage,
30
+ },
31
+ props: {
32
+ id: {
33
+ type: String,
34
+ required: true,
35
+ },
36
+ },
37
+ setup(props) {
38
+ const getRuleTask = generateGetRuleTask();
39
+
40
+ const getRule = async () => {
41
+ await getRuleTask.perform(props.id);
42
+ };
43
+
44
+ const refresh = async () => {
45
+ await getRule();
46
+ };
47
+
48
+ onMounted(async () => {
49
+ await getRule();
50
+ });
51
+
52
+ watch(props, async () => {
53
+ await getRule();
54
+ });
55
+
56
+ return {
57
+ getRuleTask,
58
+ refresh,
59
+ };
60
+ },
61
+ });
62
+ </script>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <div v-if="hasRules">
3
+ <table class="table is-fullwidth">
4
+ <tr>
5
+ <th>ID</th>
6
+ <th>Title</th>
7
+ <th>Description</th>
8
+ <th>Tags</th>
9
+ </tr>
10
+ <tr v-for="rule in rules.rules" :key="rule.id">
11
+ <td>
12
+ <router-link :to="{ name: 'Rule', params: { id: rule.id } }">{{
13
+ rule.id
14
+ }}</router-link>
15
+ </td>
16
+ <td>
17
+ {{ rule.title }}
18
+ </td>
19
+ <td>
20
+ {{ rule.description }}
21
+ </td>
22
+ <td>
23
+ <Tags :tags="rule.tags" @update-tag="updateTag"></Tags>
24
+ </td>
25
+ </tr>
26
+ </table>
27
+ </div>
28
+ <Pagination
29
+ :currentPage="rules.currentPage"
30
+ :total="rules.total"
31
+ :pageSize="rules.pageSize"
32
+ @update-page="updatePage"
33
+ ></Pagination>
34
+ <p class="help">
35
+ ({{ rules.total }} results in total, {{ rules.rules.length }} shown)
36
+ </p>
37
+ </template>
38
+
39
+ <script lang="ts">
40
+ import { computed, defineComponent, PropType } from "vue";
41
+
42
+ import Pagination from "@/components/Pagination.vue";
43
+ import Tags from "@/components/tag/Tags.vue";
44
+ import { Rules } from "@/types";
45
+
46
+ export default defineComponent({
47
+ name: "RulesItem",
48
+ props: {
49
+ rules: {
50
+ type: Object as PropType<Rules>,
51
+ required: true,
52
+ },
53
+ },
54
+ components: {
55
+ Pagination,
56
+ Tags,
57
+ },
58
+ emits: ["update-page", "refresh-page", "update-tag"],
59
+ setup(props, context) {
60
+ const scrollToTop = () => {
61
+ window.scrollTo({
62
+ top: 0,
63
+ });
64
+ };
65
+
66
+ const updatePage = (page: number) => {
67
+ scrollToTop();
68
+ context.emit("update-page", page);
69
+ };
70
+
71
+ const refreshPage = () => {
72
+ scrollToTop();
73
+ context.emit("refresh-page");
74
+ };
75
+
76
+ const updateTag = (tag: string) => {
77
+ scrollToTop();
78
+ context.emit("update-tag", tag);
79
+ };
80
+
81
+ const hasRules = computed(() => {
82
+ return props.rules.rules.length > 0;
83
+ });
84
+
85
+ return { updatePage, refreshPage, updateTag, hasRules };
86
+ },
87
+ });
88
+ </script>
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <div class="box mb-6">
3
+ <FormComponent
4
+ ref="form"
5
+ :tags="getTagsTask.last?.value || []"
6
+ :page="page"
7
+ :tag="tag"
8
+ ></FormComponent>
9
+
10
+ <hr />
11
+
12
+ <div class="column">
13
+ <div class="field is-grouped is-grouped-centered">
14
+ <p class="control">
15
+ <a class="button is-primary" @click="search">
16
+ <span class="icon is-small">
17
+ <i class="fas fa-search"></i>
18
+ </span>
19
+ <span>Search</span>
20
+ </a>
21
+ </p>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ <div v-if="getRulesTask.performCount > 0">
27
+ <hr />
28
+
29
+ <Loading v-if="getRulesTask.isRunning"></Loading>
30
+
31
+ <ErrorMessage
32
+ v-if="getRulesTask.isError"
33
+ :error="getRulesTask.last?.error"
34
+ ></ErrorMessage>
35
+
36
+ <Rules
37
+ :rules="getRulesTask.last.value"
38
+ v-if="getRulesTask.last?.value"
39
+ @refresh-page="refreshPage"
40
+ @update-page="updatePage"
41
+ @update-tag="updateTag"
42
+ ></Rules>
43
+ </div>
44
+ </template>
45
+
46
+ <script lang="ts">
47
+ import { defineComponent, nextTick, onMounted, ref, watch } from "vue";
48
+
49
+ import { generateGetRulesTask, generateGetTagsTask } from "@/api-helper";
50
+ import ErrorMessage from "@/components/ErrorMessage.vue";
51
+ import Loading from "@/components/Loading.vue";
52
+ import FormComponent from "@/components/rule/Form.vue";
53
+ import Rules from "@/components/rule/Rules.vue";
54
+ import { RuleSearchParams } from "@/types";
55
+
56
+ export default defineComponent({
57
+ name: "RulesWrapper",
58
+ components: {
59
+ Rules,
60
+ Loading,
61
+ FormComponent,
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 getRulesTask = generateGetRulesTask();
70
+ const getTagsTask = generateGetTagsTask();
71
+
72
+ const getRules = async () => {
73
+ const params = form.value?.getSearchParams() as RuleSearchParams;
74
+ return await getRulesTask.perform(params);
75
+ };
76
+
77
+ const updatePage = (newPage: number) => {
78
+ page.value = newPage;
79
+ };
80
+
81
+ const resetPage = () => {
82
+ page.value = 1;
83
+ };
84
+
85
+ const search = async () => {
86
+ // reset page
87
+ resetPage();
88
+
89
+ await getRules();
90
+ };
91
+
92
+ const updateTag = (newTag: string | undefined) => {
93
+ if (tag.value === newTag) {
94
+ tag.value = undefined;
95
+ } else {
96
+ tag.value = newTag;
97
+ }
98
+
99
+ nextTick(async () => await search());
100
+ };
101
+
102
+ const refreshPage = async () => {
103
+ // it is just an alias of search
104
+ // this function will be invoked when a rule is deleted
105
+ await search();
106
+ };
107
+
108
+ onMounted(async () => {
109
+ getTagsTask.perform();
110
+ await getRules();
111
+ });
112
+
113
+ watch([page, tag], async () => {
114
+ nextTick(async () => await getRules());
115
+ });
116
+
117
+ return {
118
+ form,
119
+ getRulesTask,
120
+ getTagsTask,
121
+ page,
122
+ tag,
123
+ refreshPage,
124
+ search,
125
+ updatePage,
126
+ updateTag,
127
+ };
128
+ },
129
+ });
130
+ </script>
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <pre
3
+ ref="pre"
4
+ class="line-numbers"
5
+ ><code class="language-yaml">{{ yaml }}</code></pre>
6
+ </template>
7
+
8
+ <script lang="ts">
9
+ // eslint-disable-next-line simple-import-sort/imports
10
+ import { defineComponent, onMounted, ref } from "vue";
11
+
12
+ import Prism from "prismjs";
13
+
14
+ import "prismjs/components/prism-yaml";
15
+ import "prismjs/plugins/custom-class/prism-custom-class";
16
+ import "prismjs/plugins/line-numbers/prism-line-numbers.css";
17
+ import "prismjs/plugins/line-numbers/prism-line-numbers";
18
+ import "prismjs/themes/prism-twilight.css";
19
+
20
+ export default defineComponent({
21
+ name: "YAML",
22
+ props: {
23
+ yaml: {
24
+ type: String,
25
+ required: true,
26
+ },
27
+ },
28
+ setup() {
29
+ const pre = ref<HTMLElement | undefined>(undefined);
30
+
31
+ Prism.plugins.customClass.map({
32
+ number: "prism-number",
33
+ tag: "prism-tag",
34
+ });
35
+
36
+ onMounted(() => {
37
+ if (pre.value) {
38
+ pre.value.querySelectorAll("code").forEach((elem) => {
39
+ Prism.highlightElement(elem);
40
+ });
41
+ }
42
+ });
43
+
44
+ return { pre };
45
+ },
46
+ });
47
+ </script>
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <div class="control" v-if="!isDeleted">
3
+ <div
4
+ class="tags has-addons are-medium"
5
+ v-on:mouseover="showDeleteButton"
6
+ v-on:mouseleave="hideDeleteButton"
7
+ >
8
+ <span class="tag is-info is-light" @click="updateTag">{{
9
+ tag.name
10
+ }}</span>
11
+ <a
12
+ class="tag is-delete"
13
+ v-if="isDeleteButtonEnabled"
14
+ @click="deleteTag"
15
+ ></a>
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script lang="ts">
21
+ import { defineComponent, PropType, ref } from "vue";
22
+
23
+ import { generateDeleteTagTask } from "@/api-helper";
24
+ import { Tag } from "@/types";
25
+
26
+ export default defineComponent({
27
+ name: "TagItem",
28
+ props: {
29
+ tag: {
30
+ type: Object as PropType<Tag>,
31
+ required: true,
32
+ },
33
+ },
34
+ setup(props, context) {
35
+ const isDeleted = ref(false);
36
+ const isDeleteButtonEnabled = ref(false);
37
+
38
+ const deleteTagTask = generateDeleteTagTask();
39
+
40
+ const deleteTag = async () => {
41
+ const result = window.confirm(
42
+ `Are you sure you want to delete ${props.tag.name}?`
43
+ );
44
+
45
+ if (result) {
46
+ await deleteTagTask.perform(props.tag.name);
47
+ isDeleted.value = true;
48
+ }
49
+ };
50
+
51
+ const showDeleteButton = () => {
52
+ isDeleteButtonEnabled.value = true;
53
+ };
54
+
55
+ const hideDeleteButton = () => {
56
+ isDeleteButtonEnabled.value = false;
57
+ };
58
+
59
+ const updateTag = () => {
60
+ context.emit("update-tag", props.tag.name);
61
+ };
62
+
63
+ return {
64
+ updateTag,
65
+ isDeleted,
66
+ deleteTag,
67
+ showDeleteButton,
68
+ hideDeleteButton,
69
+ isDeleteButtonEnabled,
70
+ };
71
+ },
72
+ });
73
+ </script>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <div class="field is-grouped is-grouped-multiline">
3
+ <TagComponent
4
+ v-for="tag in tags"
5
+ :tag="tag"
6
+ :key="tag.name"
7
+ @update-tag="updateTag"
8
+ ></TagComponent>
9
+ </div>
10
+ </template>
11
+
12
+ <script lang="ts">
13
+ import { defineComponent, PropType } from "vue";
14
+
15
+ import TagComponent from "@/components/tag/Tag.vue";
16
+ import { Tag } from "@/types";
17
+
18
+ export default defineComponent({
19
+ name: "TagsItem",
20
+ components: {
21
+ TagComponent,
22
+ },
23
+ props: {
24
+ tags: {
25
+ type: Array as PropType<Tag[]>,
26
+ required: true,
27
+ },
28
+ },
29
+ setup(_, context) {
30
+ const updateTag = (tag: string) => {
31
+ context.emit("update-tag", tag);
32
+ };
33
+
34
+ return { updateTag };
35
+ },
36
+ });
37
+ </script>