diff --git a/frontend/src/api-helper.ts b/frontend/src/api-helper.ts index b4d1c3d1c..a669293e3 100644 --- a/frontend/src/api-helper.ts +++ b/frontend/src/api-helper.ts @@ -4,8 +4,8 @@ import { API } from "@/api" import type { Alert, Alerts, + Artifact, Artifacts, - ArtifactWithTags, Config, CreateRule, IPInfo, @@ -42,8 +42,8 @@ export function generateDeleteTagTask(): Task { }) } -export function generateGetArtifactTask(): Task { - return useAsyncTask(async (_signal, id) => { +export function generateGetArtifactTask(): Task { + return useAsyncTask(async (_signal, id) => { return await API.getArtifact(id) }) } diff --git a/frontend/src/api.ts b/frontend/src/api.ts index a59fba999..143bb32a7 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -3,8 +3,8 @@ import axios from "axios" import type { Alert, Alerts, + Artifact, Artifacts, - ArtifactWithTags, Config, CreateRule, IPInfo, @@ -48,8 +48,8 @@ export const API = { return res.data }, - async getArtifact(id: number): Promise { - const res = await client.get(`/api/artifacts/${id}`) + async getArtifact(id: number): Promise { + const res = await client.get(`/api/artifacts/${id}`) return res.data }, diff --git a/frontend/src/components/alert/Alert.vue b/frontend/src/components/alert/Alert.vue index 5e0ae1de6..eff098662 100644 --- a/frontend/src/components/alert/Alert.vue +++ b/frontend/src/components/alert/Alert.vue @@ -31,7 +31,7 @@ Tags - + diff --git a/frontend/src/components/alert/AlertDetail.vue b/frontend/src/components/alert/AlertDetail.vue index d2f67fd40..6e5d5065c 100644 --- a/frontend/src/components/alert/AlertDetail.vue +++ b/frontend/src/components/alert/AlertDetail.vue @@ -29,7 +29,7 @@ Tags - + diff --git a/frontend/src/components/alert/AlertsWrapper.vue b/frontend/src/components/alert/AlertsWrapper.vue index ac91cf0c5..1b16c48fc 100644 --- a/frontend/src/components/alert/AlertsWrapper.vue +++ b/frontend/src/components/alert/AlertsWrapper.vue @@ -120,6 +120,13 @@ export default defineComponent({ await getAlerts() }) + watch(q, async () => { + window.scrollTo({ + top: 0, + behavior: "smooth" + }) + }) + return { getAlertsTask, page, diff --git a/frontend/src/components/artifact/Artifact.vue b/frontend/src/components/artifact/Artifact.vue index 6849af84b..d7ba8c279 100644 --- a/frontend/src/components/artifact/Artifact.vue +++ b/frontend/src/components/artifact/Artifact.vue @@ -50,6 +50,12 @@ Query {{ truncate(artifact.query || "N/A", 64) }} + + Tags + + + +

Created at: {{ artifact.createdAt }}

@@ -64,6 +70,7 @@ import { defineComponent, type PropType, ref } from "vue" import ActionButtons from "@/components/artifact/ActionButtons.vue" import ErrorMessage from "@/components/ErrorMessage.vue" import Message from "@/components/Message.vue" +import Tags from "@/components/tag/Tags.vue" import type { Artifact, QueueMessage } from "@/types" export default defineComponent({ @@ -74,7 +81,7 @@ export default defineComponent({ required: true } }, - components: { ErrorMessage, ActionButtons, Message }, + components: { ErrorMessage, ActionButtons, Message, Tags }, emits: ["delete"], setup(_, context) { const error = ref() diff --git a/frontend/src/components/artifact/ArtifactDetail.vue b/frontend/src/components/artifact/ArtifactDetail.vue index 10a37c150..1dd043503 100644 --- a/frontend/src/components/artifact/ArtifactDetail.vue +++ b/frontend/src/components/artifact/ArtifactDetail.vue @@ -60,7 +60,7 @@ Tags - +

Created at: {{ artifact.createdAt }}

@@ -113,20 +113,20 @@ import CPEs from "@/components/artifact/CPEs.vue" import DnsRecords from "@/components/artifact/DnsRecords.vue" import Ports from "@/components/artifact/Ports.vue" import ReverseDnsNames from "@/components/artifact/ReverseDnsNames.vue" -import Tags from "@/components/artifact/Tags.vue" import Vulnerabilities from "@/components/artifact/Vulnerabilities.vue" import WhoisRecord from "@/components/artifact/WhoisRecord.vue" import ErrorMessage from "@/components/ErrorMessage.vue" import Links from "@/components/link/Links.vue" import Message from "@/components/Message.vue" -import type { ArtifactWithTags, GCS, QueueMessage } from "@/types" +import Tags from "@/components/tag/Tags.vue" +import type { Artifact, GCS, QueueMessage } from "@/types" import { getGCSByCountryCode, getGCSByIPInfo } from "@/utils" export default defineComponent({ name: "ArtifactDetail", props: { artifact: { - type: Object as PropType, + type: Object as PropType, required: true } }, diff --git a/frontend/src/components/artifact/ArtifactsWrapper.vue b/frontend/src/components/artifact/ArtifactsWrapper.vue index 7dcb20242..ce1634330 100644 --- a/frontend/src/components/artifact/ArtifactsWrapper.vue +++ b/frontend/src/components/artifact/ArtifactsWrapper.vue @@ -108,6 +108,13 @@ export default defineComponent({ await getArtifacts() }) + watch(q, async () => { + window.scrollTo({ + top: 0, + behavior: "smooth" + }) + }) + return { getArtifactsTask, page, diff --git a/frontend/src/components/artifact/Tags.vue b/frontend/src/components/artifact/Tags.vue deleted file mode 100644 index 2828799f3..000000000 --- a/frontend/src/components/artifact/Tags.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/frontend/src/components/rule/ActionButtons.vue b/frontend/src/components/rule/ActionButtons.vue index 621eb0e24..bc1436661 100644 --- a/frontend/src/components/rule/ActionButtons.vue +++ b/frontend/src/components/rule/ActionButtons.vue @@ -7,10 +7,13 @@ Alerts: {{ getAlertsTask.last?.value?.total }} - + Artifacts: {{ getArtifactsTask.last?.value?.total }} - + JSON diff --git a/frontend/src/components/rule/Rule.vue b/frontend/src/components/rule/Rule.vue index 2ddb0a720..d77c4a1b2 100644 --- a/frontend/src/components/rule/Rule.vue +++ b/frontend/src/components/rule/Rule.vue @@ -43,7 +43,7 @@ Tags - + diff --git a/frontend/src/components/rule/RulesWrapper.vue b/frontend/src/components/rule/RulesWrapper.vue index f3882b9ce..3e79294e1 100644 --- a/frontend/src/components/rule/RulesWrapper.vue +++ b/frontend/src/components/rule/RulesWrapper.vue @@ -109,6 +109,13 @@ export default defineComponent({ await getRules() }) + watch(q, async () => { + window.scrollTo({ + top: 0, + behavior: "smooth" + }) + }) + return { getRulesTask, page, diff --git a/frontend/src/components/tag/Tag.vue b/frontend/src/components/tag/Tag.vue index 680769d1e..0431d5af7 100644 --- a/frontend/src/components/tag/Tag.vue +++ b/frontend/src/components/tag/Tag.vue @@ -5,7 +5,11 @@ v-on:mouseover="showDeleteButton" v-on:mouseleave="hideDeleteButton" > - {{ tag.name }} + {{ tag.name }} @@ -15,7 +19,7 @@ import { defineComponent, type PropType, ref } from "vue" import { generateDeleteTagTask } from "@/api-helper" -import type { Tag } from "@/types" +import type { NavigateTo, Tag } from "@/types" export default defineComponent({ name: "TagItem", @@ -27,6 +31,10 @@ export default defineComponent({ deletable: { type: Boolean, default: false + }, + navigateTo: { + type: String as PropType, + required: true } }, setup(props) { @@ -54,12 +62,17 @@ export default defineComponent({ isDeleteButtonEnabled.value = false } + const getQuery = (name: string) => { + return `tag:"${name}"` + } + return { isDeleted, deleteTag, showDeleteButton, hideDeleteButton, - isDeleteButtonEnabled + isDeleteButtonEnabled, + getQuery } } }) diff --git a/frontend/src/components/tag/Tags.vue b/frontend/src/components/tag/Tags.vue index 9c36a9c97..56415b815 100644 --- a/frontend/src/components/tag/Tags.vue +++ b/frontend/src/components/tag/Tags.vue @@ -5,6 +5,7 @@ :tag="tag" :key="tag.name" :deletable="deletable" + :navigate-to="navigateTo" > @@ -13,7 +14,7 @@ import { defineComponent, type PropType } from "vue" import TagComponent from "@/components/tag/Tag.vue" -import type { Tag } from "@/types" +import type { NavigateTo, Tag } from "@/types" export default defineComponent({ name: "TagsItem", @@ -28,6 +29,10 @@ export default defineComponent({ deletable: { type: Boolean, default: false + }, + navigateTo: { + type: String as PropType, + required: true } }, setup() {} diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 801e18f66..57ae509f1 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -91,9 +91,7 @@ export interface Artifact { cpes?: CPE[] ports?: Port[] vulnerabilities?: Vulnerability[] -} -export interface ArtifactWithTags extends Artifact { tags: Tag[] } @@ -187,3 +185,5 @@ export interface ErrorMessage extends Message { export interface QueueMessage extends Message { queued: boolean } + +export type NavigateTo = "Alerts" | "Rules" | "Artifacts" diff --git a/frontend/tests/utils.spec.ts b/frontend/tests/utils.spec.ts index b35459b87..bddb8d673 100644 --- a/frontend/tests/utils.spec.ts +++ b/frontend/tests/utils.spec.ts @@ -1,9 +1,9 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it } from "vitest" -import { getHumanizedRelativeTime } from '@/utils' +import { getHumanizedRelativeTime } from "@/utils" -describe('getHumanizedRelativeTime', () => { - it('returns a relative time in humanized format', () => { - expect(getHumanizedRelativeTime('1970-01-01 00:00:00')).toContain('years') +describe("getHumanizedRelativeTime", () => { + it("returns a relative time in humanized format", () => { + expect(getHumanizedRelativeTime("1970-01-01 00:00:00")).toContain("years") }) }) diff --git a/lib/mihari/entities/artifact.rb b/lib/mihari/entities/artifact.rb index d2507eb62..6e8d8c2fb 100644 --- a/lib/mihari/entities/artifact.rb +++ b/lib/mihari/entities/artifact.rb @@ -8,12 +8,12 @@ class BaseArtifact < Grape::Entity expose :data_type, documentation: { type: String, required: true }, as: :dataType expose :source, documentation: { type: String, required: true } expose :query, documentation: { type: String, required: false } - expose :metadata, documentation: { type: Hash } expose :created_at, documentation: { type: DateTime, required: true }, as: :createdAt + expose :tags, using: Entities::Tag, documentation: { type: Entities::Tag, is_array: true, required: true } end class Artifact < BaseArtifact - expose :tags, using: Entities::Tag, documentation: { type: Entities::Tag, is_array: true, required: true } + expose :metadata, documentation: { type: Hash } expose :autonomous_system, using: Entities::AutonomousSystem, documentation: { type: Entities::AutonomousSystem, required: false }, as: :autonomousSystem expose :geolocation, using: Entities::Geolocation, documentation: { type: Entities::Geolocation, required: false }