mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-08-02 08:51:55 +00:00
Merge branch 'main' into bugfix/traces
This commit is contained in:
commit
a351cb62fd
74
package-lock.json
generated
74
package-lock.json
generated
@ -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"
|
||||
}
|
||||
},
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user