mihari 5.2.4 → 5.3.1

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 +13 -1
  5. data/build_frontend.sh +1 -1
  6. data/frontend/.eslintrc.cjs +22 -0
  7. data/frontend/.gitignore +18 -12
  8. data/frontend/.prettierrc.json +8 -0
  9. data/frontend/env.d.ts +5 -0
  10. data/frontend/package-lock.json +5213 -9655
  11. data/frontend/package.json +38 -25
  12. data/frontend/src/App.vue +5 -5
  13. data/frontend/src/api-helper.ts +38 -40
  14. data/frontend/src/api.ts +40 -40
  15. data/frontend/src/components/ErrorMessage.vue +8 -8
  16. data/frontend/src/components/Loading.vue +4 -4
  17. data/frontend/src/components/Navbar.vue +10 -27
  18. data/frontend/src/components/Pagination.vue +35 -42
  19. data/frontend/src/components/alert/Alert.vue +22 -27
  20. data/frontend/src/components/alert/Alerts.vue +23 -25
  21. data/frontend/src/components/alert/AlertsWithPagination.vue +34 -34
  22. data/frontend/src/components/alert/AlertsWrapper.vue +43 -50
  23. data/frontend/src/components/alert/Form.vue +39 -40
  24. data/frontend/src/components/artifact/AS.vue +7 -7
  25. data/frontend/src/components/artifact/Artifact.vue +69 -86
  26. data/frontend/src/components/artifact/ArtifactTag.vue +21 -27
  27. data/frontend/src/components/artifact/ArtifactTags.vue +8 -8
  28. data/frontend/src/components/artifact/ArtifactWrapper.vue +22 -25
  29. data/frontend/src/components/artifact/CPEs.vue +6 -6
  30. data/frontend/src/components/artifact/DnsRecords.vue +9 -9
  31. data/frontend/src/components/artifact/Ports.vue +6 -6
  32. data/frontend/src/components/artifact/ReverseDnsNames.vue +7 -7
  33. data/frontend/src/components/artifact/Tags.vue +6 -6
  34. data/frontend/src/components/artifact/WhoisRecord.vue +7 -9
  35. data/frontend/src/components/config/Configs.vue +9 -12
  36. data/frontend/src/components/config/ConfigsWrapper.vue +14 -20
  37. data/frontend/src/components/link/Link.vue +7 -7
  38. data/frontend/src/components/link/Links.vue +16 -21
  39. data/frontend/src/components/rule/EditRule.vue +23 -23
  40. data/frontend/src/components/rule/EditRuleWrapper.vue +22 -28
  41. data/frontend/src/components/rule/Form.vue +28 -28
  42. data/frontend/src/components/rule/InputForm.vue +31 -25
  43. data/frontend/src/components/rule/NewRule.vue +19 -19
  44. data/frontend/src/components/rule/Rule.vue +28 -30
  45. data/frontend/src/components/rule/RuleWrapper.vue +24 -31
  46. data/frontend/src/components/rule/Rules.vue +26 -30
  47. data/frontend/src/components/rule/RulesWrapper.vue +40 -43
  48. data/frontend/src/components/rule/YAML.vue +19 -22
  49. data/frontend/src/components/tag/Tag.vue +24 -32
  50. data/frontend/src/components/tag/Tags.vue +11 -11
  51. data/frontend/src/countries.ts +23 -23
  52. data/frontend/src/index.ts +9 -12
  53. data/frontend/src/links/anyrun.ts +10 -10
  54. data/frontend/src/links/base.ts +3 -3
  55. data/frontend/src/links/censys.ts +10 -10
  56. data/frontend/src/links/crtsh.ts +10 -10
  57. data/frontend/src/links/dnslytics.ts +18 -18
  58. data/frontend/src/links/greynoise.ts +10 -10
  59. data/frontend/src/links/index.ts +15 -15
  60. data/frontend/src/links/intezer.ts +10 -10
  61. data/frontend/src/links/otx.ts +14 -14
  62. data/frontend/src/links/securitytrails.ts +15 -15
  63. data/frontend/src/links/shodan.ts +10 -10
  64. data/frontend/src/links/urlscan.ts +19 -19
  65. data/frontend/src/links/virustotal.ts +27 -27
  66. data/frontend/src/main.ts +38 -8
  67. data/frontend/src/router/index.ts +20 -20
  68. data/frontend/src/rule.ts +6 -6
  69. data/frontend/src/shims-vue.d.ts +2 -2
  70. data/frontend/src/types.ts +91 -91
  71. data/frontend/src/utils.ts +23 -29
  72. data/frontend/src/views/Alerts.vue +7 -7
  73. data/frontend/src/views/Artifact.vue +17 -17
  74. data/frontend/src/views/Configs.vue +7 -7
  75. data/frontend/src/views/EditRule.vue +17 -17
  76. data/frontend/src/views/NewRule.vue +10 -10
  77. data/frontend/src/views/Rule.vue +17 -17
  78. data/frontend/src/views/Rules.vue +7 -7
  79. data/frontend/tests/utils.spec.ts +9 -0
  80. data/frontend/tsconfig.app.json +21 -0
  81. data/frontend/tsconfig.json +10 -36
  82. data/frontend/tsconfig.node.json +13 -0
  83. data/frontend/tsconfig.vitest.json +12 -0
  84. data/frontend/vite.config.ts +24 -0
  85. data/frontend/vitest.config.ts +21 -0
  86. data/lefthook.yml +4 -2
  87. data/lib/mihari/analyzers/base.rb +48 -14
  88. data/lib/mihari/analyzers/binaryedge.rb +10 -15
  89. data/lib/mihari/analyzers/censys.rb +12 -15
  90. data/lib/mihari/analyzers/circl.rb +10 -10
  91. data/lib/mihari/analyzers/crtsh.rb +10 -6
  92. data/lib/mihari/analyzers/dnstwister.rb +6 -8
  93. data/lib/mihari/analyzers/feed.rb +21 -10
  94. data/lib/mihari/analyzers/greynoise.rb +10 -20
  95. data/lib/mihari/analyzers/onyphe.rb +9 -14
  96. data/lib/mihari/analyzers/otx.rb +8 -9
  97. data/lib/mihari/analyzers/passivetotal.rb +10 -10
  98. data/lib/mihari/analyzers/pulsedive.rb +21 -31
  99. data/lib/mihari/analyzers/securitytrails.rb +8 -6
  100. data/lib/mihari/analyzers/shodan.rb +8 -13
  101. data/lib/mihari/analyzers/urlscan.rb +15 -20
  102. data/lib/mihari/analyzers/virustotal.rb +16 -26
  103. data/lib/mihari/analyzers/virustotal_intelligence.rb +11 -17
  104. data/lib/mihari/analyzers/zoomeye.rb +12 -17
  105. data/lib/mihari/config.rb +133 -0
  106. data/lib/mihari/constants.rb +3 -0
  107. data/lib/mihari/emitters/slack.rb +13 -3
  108. data/lib/mihari/errors.rb +1 -1
  109. data/lib/mihari/http.rb +2 -3
  110. data/lib/mihari/schemas/analyzer.rb +2 -0
  111. data/lib/mihari/type_checker.rb +6 -6
  112. data/lib/mihari/version.rb +1 -1
  113. data/lib/mihari/web/endpoints/configs.rb +5 -1
  114. data/lib/mihari/web/public/assets/{index-eed1bcd8.css → index-b17c40c6.css} +1 -5
  115. data/lib/mihari/web/public/assets/index-f740e4f9.js +799 -0
  116. data/lib/mihari/web/public/index.html +2 -2
  117. data/lib/mihari/web/public/redoc-static.html +388 -2193
  118. data/lib/mihari.rb +9 -59
  119. data/mihari.gemspec +8 -8
  120. metadata +29 -117
  121. data/frontend/.browserslistrc +0 -3
  122. data/frontend/.eslintrc.js +0 -33
  123. data/frontend/babel.config.js +0 -3
  124. data/frontend/jest.config.js +0 -9
  125. data/frontend/tests/unit/utils.spec.ts +0 -7
  126. data/frontend/vite.config.js +0 -24
  127. data/lib/mihari/web/public/assets/fa-brands-400-20c4a58b.ttf +0 -0
  128. data/lib/mihari/web/public/assets/fa-brands-400-74833209.woff2 +0 -0
  129. data/lib/mihari/web/public/assets/fa-regular-400-528d022d.ttf +0 -0
  130. data/lib/mihari/web/public/assets/fa-regular-400-8e7e5ea1.woff2 +0 -0
  131. data/lib/mihari/web/public/assets/fa-solid-900-67a65763.ttf +0 -0
  132. data/lib/mihari/web/public/assets/fa-solid-900-7152a693.woff2 +0 -0
  133. data/lib/mihari/web/public/assets/fa-v4compatibility-0515a423.ttf +0 -0
  134. data/lib/mihari/web/public/assets/fa-v4compatibility-694a17c3.woff2 +0 -0
  135. data/lib/mihari/web/public/assets/index-ac4e5ffa.js +0 -50
@@ -1,60 +1,54 @@
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";
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 type { LocationQueryValue } from "vue-router"
6
6
 
7
- import { getCountryByCode } from "@/countries";
8
- import { GCS, IPInfo } from "@/types";
7
+ import { getCountryByCode } from "@/countries"
8
+ import type { GCS, IPInfo } from "@/types"
9
9
 
10
- dayjs.extend(relativeTime);
11
- dayjs.extend(timezone);
12
- dayjs.extend(utc);
10
+ dayjs.extend(relativeTime)
11
+ dayjs.extend(timezone)
12
+ dayjs.extend(utc)
13
13
 
14
14
  export function getLocalDatetime(datetime: string): string {
15
- return dayjs(datetime).local().format("YYYY-MM-DD HH:mm:ss");
15
+ return dayjs(datetime).local().format("YYYY-MM-DD HH:mm:ss")
16
16
  }
17
17
 
18
18
  export function getHumanizedRelativeTime(datetime: string): string {
19
- return dayjs(datetime).local().fromNow();
19
+ return dayjs(datetime).local().fromNow()
20
20
  }
21
21
 
22
22
  export function getGCSByCountryCode(countryCode: string): GCS | undefined {
23
- const country = getCountryByCode(countryCode);
23
+ const country = getCountryByCode(countryCode)
24
24
  if (country !== undefined) {
25
- return { lat: country.lat, long: country.long };
25
+ return { lat: country.lat, long: country.long }
26
26
  }
27
27
  }
28
28
 
29
29
  export function getGCSByIPInfo(ipinfo: IPInfo): GCS | undefined {
30
30
  if (ipinfo.loc !== undefined) {
31
- const numbers = ipinfo.loc.split(",");
31
+ const numbers = ipinfo.loc.split(",")
32
32
  if (numbers.length === 2) {
33
- const lat = numbers[0];
34
- const long = numbers[1];
33
+ const lat = numbers[0]
34
+ const long = numbers[1]
35
35
 
36
- return { lat: parseFloat(lat), long: parseFloat(long) };
36
+ return { lat: parseFloat(lat), long: parseFloat(long) }
37
37
  }
38
38
  }
39
- return getGCSByCountryCode(ipinfo.countryCode);
39
+ return getGCSByCountryCode(ipinfo.countryCode)
40
40
  }
41
41
 
42
42
  export function normalizeQueryParam(
43
- param:
44
- | undefined
45
- | null
46
- | string
47
- | string[]
48
- | LocationQueryValue
49
- | LocationQueryValue[]
43
+ param: undefined | null | string | string[] | LocationQueryValue | LocationQueryValue[]
50
44
  ): string | undefined {
51
45
  if (param === undefined || param === null) {
52
- return undefined;
46
+ return undefined
53
47
  }
54
48
 
55
49
  if (typeof param === "string") {
56
- return param;
50
+ return param
57
51
  }
58
52
 
59
- return param.toString();
53
+ return param.toString()
60
54
  }
@@ -3,18 +3,18 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent } from "vue"
8
8
 
9
- import Alerts from "@/components/alert/AlertsWrapper.vue";
9
+ import Alerts from "@/components/alert/AlertsWrapper.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "AlertsView",
13
13
  components: {
14
- Alerts,
14
+ Alerts
15
15
  },
16
16
  setup() {
17
- useTitle("Alerts - Mihari");
18
- },
19
- });
17
+ useTitle("Alerts - Mihari")
18
+ }
19
+ })
20
20
  </script>
@@ -3,42 +3,42 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent, onMounted, ref, watch } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent, onMounted, ref, watch } from "vue"
8
8
 
9
- import Artifact from "@/components/artifact/ArtifactWrapper.vue";
9
+ import Artifact from "@/components/artifact/ArtifactWrapper.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "ArtifactView",
13
13
  components: {
14
- Artifact,
14
+ Artifact
15
15
  },
16
16
  props: {
17
17
  id: {
18
18
  type: String,
19
- required: true,
20
- },
19
+ required: true
20
+ }
21
21
  },
22
22
  setup(props) {
23
- const artifactId = ref<string>(props.id);
23
+ const artifactId = ref<string>(props.id)
24
24
 
25
25
  const updateTitle = () => {
26
- useTitle(`Artifact:${artifactId.value} - Mihari`);
27
- };
26
+ useTitle(`Artifact:${artifactId.value} - Mihari`)
27
+ }
28
28
 
29
29
  onMounted(() => {
30
- updateTitle();
31
- });
30
+ updateTitle()
31
+ })
32
32
 
33
33
  watch(
34
34
  () => props.id,
35
35
  () => {
36
- artifactId.value = props.id;
37
- updateTitle();
36
+ artifactId.value = props.id
37
+ updateTitle()
38
38
  }
39
- );
39
+ )
40
40
 
41
- return { artifactId };
42
- },
43
- });
41
+ return { artifactId }
42
+ }
43
+ })
44
44
  </script>
@@ -3,18 +3,18 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent } from "vue"
8
8
 
9
- import Configs from "@/components/config/ConfigsWrapper.vue";
9
+ import Configs from "@/components/config/ConfigsWrapper.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "ConfigView",
13
13
  components: {
14
- Configs,
14
+ Configs
15
15
  },
16
16
  setup() {
17
- useTitle("Config - Mihari");
18
- },
19
- });
17
+ useTitle("Config - Mihari")
18
+ }
19
+ })
20
20
  </script>
@@ -3,42 +3,42 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent, onMounted, ref, watch } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent, onMounted, ref, watch } from "vue"
8
8
 
9
- import EditRule from "@/components/rule/EditRuleWrapper.vue";
9
+ import EditRule from "@/components/rule/EditRuleWrapper.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "EditRuleView",
13
13
  components: {
14
- EditRule,
14
+ EditRule
15
15
  },
16
16
  props: {
17
17
  id: {
18
18
  type: String,
19
- required: true,
20
- },
19
+ required: true
20
+ }
21
21
  },
22
22
  setup(props) {
23
- const ruleId = ref<string>(props.id);
23
+ const ruleId = ref<string>(props.id)
24
24
 
25
25
  const updateTitle = () => {
26
- useTitle(`Edit rule:${ruleId.value} - Mihari`);
27
- };
26
+ useTitle(`Edit rule:${ruleId.value} - Mihari`)
27
+ }
28
28
 
29
29
  onMounted(() => {
30
- updateTitle();
31
- });
30
+ updateTitle()
31
+ })
32
32
 
33
33
  watch(
34
34
  () => props.id,
35
35
  () => {
36
- ruleId.value = props.id;
37
- updateTitle();
36
+ ruleId.value = props.id
37
+ updateTitle()
38
38
  }
39
- );
39
+ )
40
40
 
41
- return { ruleId };
42
- },
43
- });
41
+ return { ruleId }
42
+ }
43
+ })
44
44
  </script>
@@ -3,24 +3,24 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent, onMounted } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent, onMounted } from "vue"
8
8
 
9
- import NewRule from "@/components/rule/NewRule.vue";
9
+ import NewRule from "@/components/rule/NewRule.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "NewRuleView",
13
13
  components: {
14
- NewRule,
14
+ NewRule
15
15
  },
16
16
  setup() {
17
17
  const updateTitle = () => {
18
- useTitle(`New rule - Mihari`);
19
- };
18
+ useTitle(`New rule - Mihari`)
19
+ }
20
20
 
21
21
  onMounted(() => {
22
- updateTitle();
23
- });
24
- },
25
- });
22
+ updateTitle()
23
+ })
24
+ }
25
+ })
26
26
  </script>
@@ -3,42 +3,42 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent, onMounted, ref, watch } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent, onMounted, ref, watch } from "vue"
8
8
 
9
- import Rule from "@/components/rule/RuleWrapper.vue";
9
+ import Rule from "@/components/rule/RuleWrapper.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "RuleView",
13
13
  components: {
14
- Rule,
14
+ Rule
15
15
  },
16
16
  props: {
17
17
  id: {
18
18
  type: String,
19
- required: true,
20
- },
19
+ required: true
20
+ }
21
21
  },
22
22
  setup(props) {
23
- const ruleId = ref<string>(props.id);
23
+ const ruleId = ref<string>(props.id)
24
24
 
25
25
  const updateTitle = () => {
26
- useTitle(`Rule:${ruleId.value} - Mihari`);
27
- };
26
+ useTitle(`Rule:${ruleId.value} - Mihari`)
27
+ }
28
28
 
29
29
  onMounted(() => {
30
- updateTitle();
31
- });
30
+ updateTitle()
31
+ })
32
32
 
33
33
  watch(
34
34
  () => props.id,
35
35
  () => {
36
- ruleId.value = props.id;
37
- updateTitle();
36
+ ruleId.value = props.id
37
+ updateTitle()
38
38
  }
39
- );
39
+ )
40
40
 
41
- return { ruleId };
42
- },
43
- });
41
+ return { ruleId }
42
+ }
43
+ })
44
44
  </script>
@@ -3,18 +3,18 @@
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import { useTitle } from "@vueuse/core";
7
- import { defineComponent } from "vue";
6
+ import { useTitle } from "@vueuse/core"
7
+ import { defineComponent } from "vue"
8
8
 
9
- import Rules from "@/components/rule/RulesWrapper.vue";
9
+ import Rules from "@/components/rule/RulesWrapper.vue"
10
10
 
11
11
  export default defineComponent({
12
12
  name: "RulesView",
13
13
  components: {
14
- Rules,
14
+ Rules
15
15
  },
16
16
  setup() {
17
- useTitle("Rules - Mihari");
18
- },
19
- });
17
+ useTitle("Rules - Mihari")
18
+ }
19
+ })
20
20
  </script>
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { getHumanizedRelativeTime } from '@/utils'
4
+
5
+ describe('getHumanizedRelativeTime', () => {
6
+ it('returns a relative time in humanized format', () => {
7
+ expect(getHumanizedRelativeTime('1970-01-01 00:00:00')).toContain('years')
8
+ })
9
+ })
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
3
+ "include": [
4
+ "env.d.ts",
5
+ "src/**/*",
6
+ "src/**/*.vue",
7
+ "tests/**/*"
8
+ ],
9
+ "exclude": [
10
+ "src/**/__tests__/*"
11
+ ],
12
+ "compilerOptions": {
13
+ "composite": true,
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "@/*": [
17
+ "./src/*"
18
+ ]
19
+ }
20
+ }
21
+ }
@@ -1,40 +1,14 @@
1
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
- ]
2
+ "files": [],
3
+ "references": [
4
+ {
5
+ "path": "./tsconfig.node.json"
22
6
  },
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"
7
+ {
8
+ "path": "./tsconfig.app.json"
9
+ },
10
+ {
11
+ "path": "./tsconfig.vitest.json"
12
+ }
39
13
  ]
40
14
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "@tsconfig/node20/tsconfig.json",
3
+ "include": [
4
+ "vite.config.*",
5
+ ],
6
+ "compilerOptions": {
7
+ "composite": true,
8
+ "module": "ESNext",
9
+ "types": [
10
+ "node"
11
+ ]
12
+ }
13
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.app.json",
3
+ "exclude": [],
4
+ "compilerOptions": {
5
+ "composite": true,
6
+ "lib": [],
7
+ "types": [
8
+ "node",
9
+ "jsdom"
10
+ ]
11
+ }
12
+ }
@@ -0,0 +1,24 @@
1
+ import { fileURLToPath, URL } from "node:url"
2
+
3
+ import vue from "@vitejs/plugin-vue"
4
+ import { defineConfig } from "vite"
5
+
6
+ const env = process.env
7
+ const target = env.BACKEND_URL || "http://localhost:9292/"
8
+ const port = parseInt(env.port || "8080")
9
+
10
+ // https://vitejs.dev/config/
11
+ export default defineConfig({
12
+ plugins: [vue()],
13
+ server: {
14
+ port,
15
+ proxy: {
16
+ "/api": target
17
+ }
18
+ },
19
+ resolve: {
20
+ alias: {
21
+ "@": fileURLToPath(new URL("./src", import.meta.url))
22
+ }
23
+ }
24
+ })
@@ -0,0 +1,21 @@
1
+ import { fileURLToPath } from "node:url"
2
+
3
+ import { mergeConfig } from "vite"
4
+ import { configDefaults, defineConfig } from "vitest/config"
5
+
6
+ import viteConfig from "./vite.config"
7
+
8
+ export default mergeConfig(
9
+ viteConfig,
10
+ defineConfig({
11
+ test: {
12
+ environment: "jsdom",
13
+ exclude: [...configDefaults.exclude, "e2e/*"],
14
+ root: fileURLToPath(new URL("./", import.meta.url)),
15
+ transformMode: {
16
+ web: [/\.[jt]sx$/]
17
+ },
18
+ include: ["**/__tests__/**/*.?(c|m)[jt]s?(x)", "**/?(*.){test,spec}.?(c|m)[jt]s?(x)"]
19
+ }
20
+ })
21
+ )
data/lefthook.yml CHANGED
@@ -3,8 +3,10 @@ pre-commit:
3
3
  commands:
4
4
  standard:
5
5
  glob: "*.rb"
6
- run: bundle exec standardrb --fix {staged_files} && git add {staged_files}
6
+ run: bundle exec standardrb --fix {staged_files}
7
+ stage_fixed: true
7
8
  eslint:
8
9
  root: "frontend/"
9
10
  glob: "*.{js,ts,vue}"
10
- run: npx eslint --fix {staged_files} && git add {staged_files}
11
+ run: npx eslint --fix {staged_files}
12
+ stage_fixed: true
@@ -3,11 +3,45 @@
3
3
  module Mihari
4
4
  module Analyzers
5
5
  class Base
6
- extend Dry::Initializer
7
-
8
6
  include Mixins::Configurable
9
7
  include Mixins::Retriable
10
8
 
9
+ # @return [String]
10
+ attr_reader :query
11
+
12
+ # @return [Hash]
13
+ attr_reader :options
14
+
15
+ #
16
+ # @param [String] query
17
+ # @param [Hash, nil] options
18
+ #
19
+ def initialize(query, options: nil)
20
+ @query = query
21
+ @options = options || {}
22
+ end
23
+
24
+ #
25
+ # @return [Integer, nil]
26
+ #
27
+ def interval
28
+ @interval = options[:interval]
29
+ end
30
+
31
+ #
32
+ # @return [Integer]
33
+ #
34
+ def retry_interval
35
+ @retry_interval ||= options[:retry_interval] || DEFAULT_RETRY_INTERVAL
36
+ end
37
+
38
+ #
39
+ # @return [Integer]
40
+ #
41
+ def retry_times
42
+ @retry_times ||= options[:retry_times] || DEFAULT_RETRY_TIMES
43
+ end
44
+
11
45
  # @return [Array<String>, Array<Mihari::Artifact>]
12
46
  def artifacts
13
47
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
@@ -21,17 +55,14 @@ module Mihari
21
55
  # @return [Array<Mihari::Artifact>]
22
56
  #
23
57
  def normalized_artifacts
24
- retry_on_error do
58
+ retry_on_error(times: retry_times, interval: retry_interval) do
25
59
  @normalized_artifacts ||= artifacts.compact.sort.map do |artifact|
26
60
  # No need to set data_type manually
27
61
  # It is set automatically in #initialize
28
62
  artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
29
- artifact
30
- end.select(&:valid?).uniq(&:data).map do |artifact|
31
- # set source
32
63
  artifact.source = source
33
64
  artifact
34
- end
65
+ end.select(&:valid?).uniq(&:data)
35
66
  end
36
67
  end
37
68
 
@@ -51,16 +82,19 @@ module Mihari
51
82
  # @return [Mihari::Analyzers::Base]
52
83
  #
53
84
  def from_query(params)
54
- # get options and set default value as an empty hash
55
- options = params[:options] || {}
85
+ copied = params.deep_dup
86
+
87
+ # convert params into arguments for initialization
88
+ query = copied[:query]
56
89
 
57
- # set interval in the top level
58
- interval = options[:interval]
59
- params[:interval] = interval if interval
90
+ # delete analyzer and query
91
+ %i[analyzer query].each do |key|
92
+ copied.delete key
93
+ end
60
94
 
61
- query = params[:query]
95
+ copied[:options] = copied[:options] || nil
62
96
 
63
- new(query, **params)
97
+ new(query, **copied)
64
98
  end
65
99
 
66
100
  def inherited(child)