From f6b590d27a40778fd7441979739e436e642702b6 Mon Sep 17 00:00:00 2001 From: Manabu Niseki Date: Sat, 2 Mar 2024 10:32:22 +0900 Subject: [PATCH 1/2] refactor: introduce Zod --- frontend/package-lock.json | 11 +- frontend/package.json | 3 +- frontend/src/api-helper.ts | 102 ++++---- frontend/src/api.ts | 142 ++++++----- frontend/src/components/ErrorMessage.vue | 6 +- frontend/src/components/Message.vue | 4 +- .../src/components/alert/ActionButtons.vue | 4 +- frontend/src/components/alert/Alert.vue | 4 +- frontend/src/components/alert/AlertDetail.vue | 4 +- frontend/src/components/alert/Alerts.vue | 4 +- .../components/alert/AlertsWithPagination.vue | 4 +- .../src/components/alert/AlertsWrapper.vue | 4 +- frontend/src/components/artifact/AS.vue | 4 +- .../src/components/artifact/ActionButtons.vue | 4 +- frontend/src/components/artifact/Artifact.vue | 8 +- .../components/artifact/ArtifactDetail.vue | 12 +- .../src/components/artifact/ArtifactTag.vue | 4 +- .../src/components/artifact/ArtifactTags.vue | 4 +- .../src/components/artifact/Artifacts.vue | 4 +- .../components/artifact/ArtifactsWrapper.vue | 4 +- frontend/src/components/artifact/CPEs.vue | 4 +- .../src/components/artifact/DnsRecords.vue | 4 +- frontend/src/components/artifact/Ports.vue | 4 +- .../components/artifact/ReverseDnsNames.vue | 4 +- .../components/artifact/Vulnerabilities.vue | 4 +- .../src/components/artifact/WhoisRecord.vue | 4 +- frontend/src/components/config/Configs.vue | 6 +- frontend/src/components/link/Link.vue | 4 +- frontend/src/components/link/Links.vue | 4 +- .../src/components/rule/ActionButtons.vue | 4 +- frontend/src/components/rule/EditRule.vue | 4 +- frontend/src/components/rule/LinkButtons.vue | 4 +- frontend/src/components/rule/Rule.vue | 8 +- frontend/src/components/rule/RuleDetail.vue | 8 +- frontend/src/components/rule/Rules.vue | 4 +- frontend/src/components/rule/RulesWrapper.vue | 4 +- frontend/src/components/tag/Tag.vue | 6 +- frontend/src/components/tag/Tags.vue | 6 +- frontend/src/countries.ts | 6 +- frontend/src/index.ts | 4 +- frontend/src/links/anyrun.ts | 6 +- frontend/src/links/censys.ts | 6 +- frontend/src/links/crtsh.ts | 6 +- frontend/src/links/dnslytics.ts | 10 +- frontend/src/links/emailrep.ts | 6 +- frontend/src/links/greynoise.ts | 6 +- frontend/src/links/index.ts | 4 +- frontend/src/links/intezer.ts | 6 +- frontend/src/links/otx.ts | 10 +- frontend/src/links/securitytrails.ts | 8 +- frontend/src/links/shodan.ts | 6 +- frontend/src/links/urlscan.ts | 10 +- frontend/src/links/virustotal.ts | 12 +- frontend/src/schemas.ts | 231 ++++++++++++++++++ frontend/src/types.ts | 189 -------------- frontend/src/utils.ts | 6 +- lib/mihari/entities/config.rb | 4 + lib/mihari/web/endpoints/configs.rb | 9 +- 58 files changed, 519 insertions(+), 448 deletions(-) create mode 100644 frontend/src/schemas.ts delete mode 100644 frontend/src/types.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9e0cc3a2d..de801be25 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,7 +28,8 @@ "vue-concurrency": "^5.0.0", "vue-json-pretty": "^2.3.0", "vue-router": "^4.2.5", - "vue3-ace-editor": "^2.2.4" + "vue3-ace-editor": "^2.2.4", + "zod": "^3.22.4" }, "devDependencies": { "@redocly/cli": "1.7.0", @@ -7809,6 +7810,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/frontend/package.json b/frontend/package.json index 360e6d9ad..8fdab4fa1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,7 +35,8 @@ "vue-concurrency": "^5.0.0", "vue-json-pretty": "^2.3.0", "vue-router": "^4.2.5", - "vue3-ace-editor": "^2.2.4" + "vue3-ace-editor": "^2.2.4", + "zod": "^3.22.4" }, "devDependencies": { "@redocly/cli": "1.7.0", diff --git a/frontend/src/api-helper.ts b/frontend/src/api-helper.ts index a669293e3..16e07483d 100644 --- a/frontend/src/api-helper.ts +++ b/frontend/src/api-helper.ts @@ -2,120 +2,120 @@ import { type Task, useAsyncTask } from "vue-concurrency" import { API } from "@/api" import type { - Alert, - Alerts, - Artifact, - Artifacts, - Config, - CreateRule, - IPInfo, - Message, - QueueMessage, - Rule, - Rules, - SearchParams, - Tags, - UpdateRule -} from "@/types" - -export function generateGetAlertsTask(): Task { - return useAsyncTask(async (_signal, params) => { + AlertsType, + AlertType, + ArtifactsType, + ArtifactType, + ConfigsType, + CreateRuleType, + IpInfoType, + MessageType, + QueueMessageType, + RulesType, + RuleType, + SearchParamsType, + TagsType, + UpdateRuleType +} from "@/schemas" + +export function generateGetAlertsTask(): Task { + return useAsyncTask(async (_signal, params) => { return await API.getAlerts(params) }) } -export function generateDeleteAlertTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateDeleteAlertTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.deleteAlert(id) }) } -export function generateGetTagsTask(): Task { - return useAsyncTask(async () => { +export function generateGetTagsTask(): Task { + return useAsyncTask(async () => { return await API.getTags() }) } -export function generateDeleteTagTask(): Task { - return useAsyncTask(async (_signal, tag) => { +export function generateDeleteTagTask(): Task { + return useAsyncTask(async (_signal, tag) => { return await API.deleteTag(tag) }) } -export function generateGetArtifactTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateGetArtifactTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.getArtifact(id) }) } -export function generateDeleteArtifactTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateDeleteArtifactTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.deleteArtifact(id) }) } -export function generateEnrichArtifactTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateEnrichArtifactTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.enrichArtifact(id) }) } -export function generateGetConfigsTask(): Task { - return useAsyncTask(async () => { +export function generateGetConfigsTask(): Task { + return useAsyncTask(async () => { return await API.getConfigs() }) } -export function generateGetIPTask(): Task { - return useAsyncTask(async (_signal, ipAddress: string) => { - return await API.getIPInfo(ipAddress) +export function generateGetIPTask(): Task { + return useAsyncTask(async (_signal, ipAddress: string) => { + return await API.getIpInfo(ipAddress) }) } -export function generateGetRulesTask(): Task { - return useAsyncTask(async (_signal, params: SearchParams) => { +export function generateGetRulesTask(): Task { + return useAsyncTask(async (_signal, params: SearchParamsType) => { return await API.getRules(params) }) } -export function generateGetRuleTask(): Task { - return useAsyncTask(async (_signal, id: string) => { +export function generateGetRuleTask(): Task { + return useAsyncTask(async (_signal, id: string) => { return await API.getRule(id) }) } -export function generateDeleteRuleTask(): Task { - return useAsyncTask(async (_signal, id: string) => { +export function generateDeleteRuleTask(): Task { + return useAsyncTask(async (_signal, id: string) => { return await API.deleteRule(id) }) } -export function generateSearchRuleTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateSearchRuleTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.searchRule(id) }) } -export function generateCreateRuleTask(): Task { - return useAsyncTask(async (_signal, payload) => { +export function generateCreateRuleTask(): Task { + return useAsyncTask(async (_signal, payload) => { return await API.createRule(payload) }) } -export function generateUpdateRuleTask(): Task { - return useAsyncTask(async (_signal, payload) => { +export function generateUpdateRuleTask(): Task { + return useAsyncTask(async (_signal, payload) => { return await API.updateRule(payload) }) } -export function generateGetArtifactsTask(): Task { - return useAsyncTask(async (_signal, params) => { +export function generateGetArtifactsTask(): Task { + return useAsyncTask(async (_signal, params) => { return await API.getArtifacts(params) }) } -export function generateGetAlertTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateGetAlertTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.getAlert(id) }) } diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 143bb32a7..d3845e9d0 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,116 +1,126 @@ import axios from "axios" -import type { - Alert, - Alerts, - Artifact, - Artifacts, - Config, - CreateRule, - IPInfo, - Message, - QueueMessage, - Rule, - Rules, - SearchParams, - Tags, - UpdateRule -} from "@/types" +import { + AlertSchema, + AlertsSchema, + type AlertsType, + type AlertType, + ArtifactSchema, + ArtifactsSchema, + type ArtifactsType, + type ArtifactType, + ConfigsSchema, + type ConfigsType, + type CreateRuleType, + type IpInfoType, + MessageSchema, + type MessageType, + QueueMessageSchema, + type QueueMessageType, + RuleSchema, + RulesSchema, + type RulesType, + type RuleType, + type SearchParamsType, + TagsSchema, + type TagsType, + type UpdateRuleType +} from "@/schemas" const client = axios.create() export const API = { - async getConfigs(): Promise { - const res = await client.get("/api/configs") - return res.data + async getConfigs(): Promise { + const res = await client.get("/api/configs") + return ConfigsSchema.parse(res.data) }, - async getAlerts(params: SearchParams): Promise { + async getAlerts(params: SearchParamsType): Promise { params.page = params.page || 1 - const res = await client.get("/api/alerts", { + const res = await client.get("/api/alerts", { params: params }) - return res.data + return AlertsSchema.parse(res.data) }, - async getAlert(id: number): Promise { - const res = await client.get(`/api/alerts/${id}`) - return res.data + async getAlert(id: number): Promise { + const res = await client.get(`/api/alerts/${id}`) + return AlertSchema.parse(res.data) }, - async getTags(): Promise { - const res = await client.get("/api/tags") - return res.data + async getTags(): Promise { + const res = await client.get("/api/tags") + return TagsSchema.parse(res.data) }, - async deleteAlert(id: number): Promise { - const res = await client.delete(`/api/alerts/${id}`) - return res.data + async deleteAlert(id: number): Promise { + const res = await client.delete(`/api/alerts/${id}`) + return MessageSchema.parse(res.data) }, - async getArtifact(id: number): Promise { - const res = await client.get(`/api/artifacts/${id}`) - return res.data + async getArtifact(id: number): Promise { + const res = await client.get(`/api/artifacts/${id}`) + return ArtifactSchema.parse(res.data) }, - async getArtifacts(params: SearchParams): Promise { + async getArtifacts(params: SearchParamsType): Promise { params.page = params.page || 1 - const res = await client.get("/api/artifacts", { + const res = await client.get("/api/artifacts", { params: params }) - return res.data + return ArtifactsSchema.parse(res.data) }, - async enrichArtifact(id: number): Promise { - const res = await client.post(`/api/artifacts/${id}/enrich`) - return res.data + async enrichArtifact(id: number): Promise { + const res = await client.post(`/api/artifacts/${id}/enrich`) + return QueueMessageSchema.parse(res.data) }, - async deleteArtifact(id: number): Promise { - const res = await client.delete(`/api/artifacts/${id}`) - return res.data + async deleteArtifact(id: number): Promise { + const res = await client.delete(`/api/artifacts/${id}`) + return MessageSchema.parse(res.data) }, - async getRules(params: SearchParams): Promise { + async getRules(params: SearchParamsType): Promise { params.page = params.page || 1 - const res = await client.get("/api/rules", { + const res = await client.get("/api/rules", { params: params }) - return res.data + return RulesSchema.parse(res.data) }, - async getRule(id: string): Promise { - const res = await client.get(`/api/rules/${id}`) - return res.data + async getRule(id: string): Promise { + const res = await client.get(`/api/rules/${id}`) + return RuleSchema.parse(res.data) }, - async searchRule(id: string): Promise { - const res = await client.post(`/api/rules/${id}/search`) - return res.data + async searchRule(id: string): Promise { + const res = await client.post(`/api/rules/${id}/search`) + return QueueMessageSchema.parse(res.data) }, - async createRule(payload: CreateRule): Promise { - const res = await client.post("/api/rules/", payload) - return res.data + async createRule(payload: CreateRuleType): Promise { + const res = await client.post("/api/rules/", payload) + return RuleSchema.parse(res.data) }, - async updateRule(payload: UpdateRule): Promise { - const res = await client.put("/api/rules/", payload) - return res.data + async updateRule(payload: UpdateRuleType): Promise { + const res = await client.put("/api/rules/", payload) + return RuleSchema.parse(res.data) }, - async deleteRule(id: string): Promise { - const res = await client.delete(`/api/rules/${id}`) - return res.data + async deleteRule(id: string): Promise { + const res = await client.delete(`/api/rules/${id}`) + return MessageSchema.parse(res.data) }, - async deleteTag(id: number): Promise { - const res = await client.delete(`/api/tags/${id}`) - return res.data + async deleteTag(id: number): Promise { + const res = await client.delete(`/api/tags/${id}`) + return MessageSchema.parse(res.data) }, - async getIPInfo(ipAddress: string): Promise { - const res = await client.get(`/api/ip_addresses/${ipAddress}`) + async getIpInfo(ipAddress: string): Promise { + const res = await client.get(`/api/ip_addresses/${ipAddress}`) return res.data } } diff --git a/frontend/src/components/ErrorMessage.vue b/frontend/src/components/ErrorMessage.vue index 6a95cfdd4..4226eabc6 100644 --- a/frontend/src/components/ErrorMessage.vue +++ b/frontend/src/components/ErrorMessage.vue @@ -18,7 +18,7 @@ import { AxiosError } from "axios" import { computed, defineComponent } from "vue" import VueJsonPretty from "vue-json-pretty" -import type { ErrorMessage } from "@/types" +import type { ErrorMessageType } from "@/schemas" export default defineComponent({ name: "ErrorItem", @@ -37,9 +37,9 @@ export default defineComponent({ }, emits: ["dispose"], setup(props, context) { - const data = computed(() => { + const data = computed(() => { if (props.error.response) { - return props.error.response?.data as ErrorMessage + return props.error.response?.data as ErrorMessageType } return undefined }) diff --git a/frontend/src/components/Message.vue b/frontend/src/components/Message.vue index 0166e72de..f4ce22e08 100644 --- a/frontend/src/components/Message.vue +++ b/frontend/src/components/Message.vue @@ -8,13 +8,13 @@