Merge branch 'main' into bugfix/traces

This commit is contained in:
吴晟 Wu Sheng 2025-07-17 06:25:22 +02:00 committed by GitHub
commit a351cb62fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 136 additions and 46 deletions

74
package-lock.json generated
View File

@ -18,7 +18,7 @@
"vis-timeline": "^7.5.1",
"vue": "^3.2.45",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.14.3",
"vue-i18n": "^9.14.5",
"vue-router": "^4.1.6",
"vue-types": "^4.1.1"
},
@ -1966,13 +1966,12 @@
"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
},
"node_modules/@intlify/core-base": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.3.tgz",
"integrity": "sha512-nbJ7pKTlXFnaXPblyfiH6awAx1C0PWNNuqXAR74yRwgi5A/Re/8/5fErLY0pv4R8+EHj3ZaThMHdnuC/5OBa6g==",
"license": "MIT",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
"dependencies": {
"@intlify/message-compiler": "9.14.3",
"@intlify/shared": "9.14.3"
"@intlify/message-compiler": "9.14.5",
"@intlify/shared": "9.14.5"
},
"engines": {
"node": ">= 16"
@ -1982,12 +1981,11 @@
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.3.tgz",
"integrity": "sha512-ANwC226BQdd+MpJ36rOYkChSESfPwu3Ss2Faw0RHTOknYLoHTX6V6e/JjIKVDMbzs0/H/df/rO6yU0SPiWHqNg==",
"license": "MIT",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
"dependencies": {
"@intlify/shared": "9.14.3",
"@intlify/shared": "9.14.5",
"source-map-js": "^1.0.2"
},
"engines": {
@ -1998,10 +1996,9 @@
}
},
"node_modules/@intlify/shared": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.3.tgz",
"integrity": "sha512-hJXz9LA5VG7qNE00t50bdzDv8Z4q9fpcL81wj4y4duKavrv0KM8YNLTwXNEFINHjTsfrG9TXvPuEjVaAvZ7yWg==",
"license": "MIT",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
"engines": {
"node": ">= 16"
},
@ -13254,13 +13251,12 @@
}
},
"node_modules/vue-i18n": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.3.tgz",
"integrity": "sha512-C+E0KE8ihKjdYCQx8oUkXX+8tBItrYNMnGJuzEPevBARQFUN2tKez6ZVOvBrWH0+KT5wEk3vOWjNk7ygb2u9ig==",
"license": "MIT",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
"dependencies": {
"@intlify/core-base": "9.14.3",
"@intlify/shared": "9.14.3",
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
@ -14956,27 +14952,27 @@
"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
},
"@intlify/core-base": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.3.tgz",
"integrity": "sha512-nbJ7pKTlXFnaXPblyfiH6awAx1C0PWNNuqXAR74yRwgi5A/Re/8/5fErLY0pv4R8+EHj3ZaThMHdnuC/5OBa6g==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
"requires": {
"@intlify/message-compiler": "9.14.3",
"@intlify/shared": "9.14.3"
"@intlify/message-compiler": "9.14.5",
"@intlify/shared": "9.14.5"
}
},
"@intlify/message-compiler": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.3.tgz",
"integrity": "sha512-ANwC226BQdd+MpJ36rOYkChSESfPwu3Ss2Faw0RHTOknYLoHTX6V6e/JjIKVDMbzs0/H/df/rO6yU0SPiWHqNg==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
"requires": {
"@intlify/shared": "9.14.3",
"@intlify/shared": "9.14.5",
"source-map-js": "^1.0.2"
}
},
"@intlify/shared": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.3.tgz",
"integrity": "sha512-hJXz9LA5VG7qNE00t50bdzDv8Z4q9fpcL81wj4y4duKavrv0KM8YNLTwXNEFINHjTsfrG9TXvPuEjVaAvZ7yWg=="
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ=="
},
"@jridgewell/gen-mapping": {
"version": "0.3.8",
@ -23097,12 +23093,12 @@
}
},
"vue-i18n": {
"version": "9.14.3",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.3.tgz",
"integrity": "sha512-C+E0KE8ihKjdYCQx8oUkXX+8tBItrYNMnGJuzEPevBARQFUN2tKez6ZVOvBrWH0+KT5wEk3vOWjNk7ygb2u9ig==",
"version": "9.14.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
"requires": {
"@intlify/core-base": "9.14.3",
"@intlify/shared": "9.14.3",
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@vue/devtools-api": "^6.5.0"
}
},

View File

@ -28,7 +28,7 @@
"vis-timeline": "^7.5.1",
"vue": "^3.2.45",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.14.3",
"vue-i18n": "^9.14.5",
"vue-router": "^4.1.6",
"vue-types": "^4.1.1"
},

View File

@ -150,6 +150,7 @@ $theme-background: var(--theme-background);
$active-background: var(--el-color-primary);
$font-size-smaller: 12px;
$font-size-normal: 14px;
$error-color: #e66;
.opt:hover {
background-color: var(--sw-list-hover) !important;
@ -208,9 +209,9 @@ div.vis-tooltip {
}
.vis-item.Error {
background-color: #e66;
background-color: $error-color;
opacity: 0.8;
border-color: #e66;
border-color: $error-color;
color: var(--sw-event-vis-selected) !important;
}

View File

@ -13,13 +13,14 @@ limitations under the License. -->
<template>
<div class="item">
<span class="label">{{ t("iframeSrc") }}</span>
<el-input class="input" v-model="url" size="small" @change="changeConfig({ url: encodeURIComponent(url) })" />
<el-input class="input" v-model="url" size="small" @change="handleUrlChange" :class="{ error: urlError }" />
<div v-if="urlError" class="error-message">{{ urlError }}</div>
</div>
<div class="footer">
<el-button size="small" @click="cancelConfig">
{{ t("cancel") }}
</el-button>
<el-button size="small" type="primary" @click="applyConfig">
<el-button size="small" type="primary" @click="applyConfig" :disabled="!!urlError">
{{ t("apply") }}
</el-button>
</div>
@ -28,24 +29,89 @@ limitations under the License. -->
import { useI18n } from "vue-i18n";
import { ref } from "vue";
import { useDashboardStore } from "@/store/modules/dashboard";
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const originConfig = dashboardStore.selectedGrid;
const widget = originConfig.widget || {};
const url = ref(widget.url || "");
const urlError = ref("");
// URL validation function to prevent XSS
function validateAndSanitizeUrl(inputUrl: string): { isValid: boolean; sanitizedUrl: string; error: string } {
if (!inputUrl.trim()) {
return { isValid: true, sanitizedUrl: "", error: "" };
}
try {
// Create URL object to validate the URL format
const urlObj = new URL(inputUrl);
// Only allow HTTP and HTTPS protocols to prevent XSS
if (!["http:", "https:"].includes(urlObj.protocol)) {
return {
isValid: false,
sanitizedUrl: "",
error: "Only HTTP and HTTPS URLs are allowed",
};
}
// Additional security checks
const dangerousProtocols = ["javascript:", "data:", "vbscript:", "le:"];
const lowerUrl = inputUrl.toLowerCase();
for (const protocol of dangerousProtocols) {
if (lowerUrl.includes(protocol)) {
return {
isValid: false,
sanitizedUrl: "",
error: "Dangerous protocols are not allowed",
};
}
}
// Return the sanitized URL (using the URL object to normalize it)
return {
isValid: true,
sanitizedUrl: urlObj.href,
error: "",
};
} catch (error) {
return {
isValid: false,
sanitizedUrl: "",
error: "Please enter a valid URL",
};
}
}
function handleUrlChange() {
const validation = validateAndSanitizeUrl(url.value);
urlError.value = validation.error;
if (validation.isValid) {
changeConfig({ url: validation.sanitizedUrl });
}
}
function changeConfig(param: { [key: string]: string }) {
const key = Object.keys(param)[0];
if (!key) {
return;
}
const { selectedGrid } = dashboardStore;
const widget = {
...dashboardStore.selectedGrid.widget,
[key]: decodeURIComponent(param[key]),
[key]: param[key], // Use the sanitized URL directly, no need for decodeURIComponent
};
dashboardStore.selectWidget({ ...selectedGrid, widget });
}
function applyConfig() {
if (urlError.value) {
return; // Don't apply if there's a validation error
}
dashboardStore.setConfigPanel(false);
dashboardStore.setConfigs(dashboardStore.selectedGrid);
}
@ -76,6 +142,18 @@ limitations under the License. -->
margin-bottom: 10px;
}
.url-input.error {
:deep(.el-input__inner) {
border-color: $error-color;
}
}
.error-message {
color: $error-color;
font-size: 12px;
margin-top: 4px;
}
.footer {
position: fixed;
bottom: 0;

View File

@ -37,6 +37,8 @@ limitations under the License. -->
height="100%"
scrolling="no"
style="border: none"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
referrerpolicy="no-referrer"
></iframe>
<div v-else class="tips">{{ t("iframeWidgetTip") }}</div>
</div>

View File

@ -69,6 +69,7 @@ limitations under the License. -->
type: { type: String, default: TraceGraphType.LIST },
headerType: { type: String, default: "" },
});
const emits = defineEmits(["select"]);
const appStore = useAppStoreWithOut();
const loading = ref<boolean>(false);
const showDetail = ref<boolean>(false);
@ -130,6 +131,7 @@ limitations under the License. -->
refParentSpans.value = [];
if (props.type === TraceGraphType.TABLE) {
currentSpan.value = i;
emits("select", i);
} else {
currentSpan.value = i.data;
}

View File

@ -12,7 +12,13 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="trace-table-charts">
<Graph :data="data" :traceId="traceId" :type="TraceGraphType.TABLE" :headerType="headerType" />
<Graph
:data="data"
:traceId="traceId"
:type="TraceGraphType.TABLE"
:headerType="headerType"
@select="getSelectedSpan"
/>
</div>
</template>
<script lang="ts" setup>
@ -26,6 +32,11 @@ limitations under the License. -->
traceId: { type: String, default: "" },
headerType: { type: String, default: "" },
});
const emits = defineEmits(["select"]);
function getSelectedSpan(span: Span) {
emits("select", span);
}
</script>
<style lang="scss" scoped>
.trace-table-charts {