Skip to content

Commit 64f1d4c

Browse files
committed
Add Content Security Policy for Jaeger panel
Move vscode API instance from the global scope
1 parent 1b0788d commit 64f1d4c

File tree

9 files changed

+250
-144
lines changed

9 files changed

+250
-144
lines changed

jaegerUi/static/js/1.65ef2ae1.chunk.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jaegerUi/static/js/1.fa29228e.chunk.js.map renamed to jaegerUi/static/js/1.65ef2ae1.chunk.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jaegerUi/static/js/1.fa29228e.chunk.js

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jaegerUi/static/js/main.7706e4a8.chunk.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jaegerUi/static/js/main.cd5ed381.chunk.js.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

jaegerUi/static/js/vscode.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(function() {
2+
const vscode = acquireVsCodeApi();
3+
4+
window.sendMessageToVSCode = (message) => {
5+
vscode.postMessage(message);
6+
}
7+
}())
Lines changed: 224 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,245 @@
11
import * as vscode from "vscode";
2+
import { AnalyticsProvider } from "../../../services/analyticsProvider";
3+
import { EditorHelper } from "../../../services/EditorHelper";
4+
import { SpanSearch } from "../InsightListView/Common/SpanSearch";
5+
6+
interface Message {
7+
command: string;
8+
// TODO: change to unknown and use typeGuards
9+
data: any;
10+
}
11+
12+
interface GoToSpanLocationMessage extends Message {
13+
data: {
14+
name: string;
15+
instrumentationLibrary: string;
16+
};
17+
}
18+
19+
interface GetTraceSpansLocationsMessage extends Message {
20+
data: {
21+
id: string;
22+
name: string;
23+
instrumentationLibrary: string;
24+
}[];
25+
}
26+
27+
type SetSpansWithResolvedLocationMessageData = Record<
28+
string,
29+
{ importance?: number }
30+
>;
231

332
export class JaegerPanel {
4-
constructor() {}
33+
private _panel: vscode.WebviewPanel;
34+
private _spanSearch: SpanSearch;
35+
private _editorHelper: EditorHelper;
36+
private _analyticsProvider: AnalyticsProvider;
37+
constructor(
38+
panel: vscode.WebviewPanel,
39+
spanSearch: SpanSearch,
40+
editorHelper: EditorHelper,
41+
analyticsProvider: AnalyticsProvider
42+
) {
43+
this._panel = panel;
44+
this._spanSearch = spanSearch;
45+
this._editorHelper = editorHelper;
46+
this._analyticsProvider = analyticsProvider;
47+
48+
this._panel.webview.onDidReceiveMessage(async (message: Message) => {
49+
switch (message.command) {
50+
case "goToSpanLocation":
51+
await this.onGoToSpanLocation(message);
52+
break;
53+
case "getTraceSpansLocations":
54+
await this.onGetTraceSpansLocations(message);
55+
break;
56+
}
57+
});
58+
}
59+
60+
private async onGoToSpanLocation(message: GoToSpanLocationMessage) {
61+
const span = message.data;
62+
const spanLocation = await this._spanSearch.searchForSpans([
63+
{
64+
name: span.name,
65+
instrumentationLibrary: span.instrumentationLibrary,
66+
},
67+
]);
68+
69+
if (spanLocation[0]) {
70+
const codeUri = spanLocation[0].documentUri;
71+
const lineNumber = spanLocation[0].range.end.line + 1;
72+
73+
if (codeUri && lineNumber) {
74+
const doc = await this._editorHelper.openTextDocumentFromUri(
75+
vscode.Uri.parse(codeUri.toString())
76+
);
77+
this._editorHelper.openFileAndLine(doc, lineNumber);
78+
}
79+
}
80+
}
81+
82+
private async onGetTraceSpansLocations(
83+
message: GetTraceSpansLocationsMessage
84+
) {
85+
console.log("onGetTraceSpansLocations received");
86+
const spans = message.data;
87+
const spanLocations = await this._spanSearch.searchForSpans(spans);
88+
const spanWithLocations = spans.filter((span, i) => spanLocations[i]);
89+
const spanCodeObjectIds = spanWithLocations.map(
90+
(span) => `span:${span.instrumentationLibrary}$_$${span.name}`
91+
);
92+
93+
const insights = await this._analyticsProvider.getInsights(
94+
spanCodeObjectIds,
95+
true
96+
);
97+
const insightGroups = insights.groupBy((x) => x.codeObjectId);
98+
99+
const spansInfo = spanWithLocations.reduce(
100+
(acc: SetSpansWithResolvedLocationMessageData, span) => {
101+
const insightGroup =
102+
insightGroups[`${span.instrumentationLibrary}$_$${span.name}`];
5103

6-
public getHtml(traceIds: string[], traceIdLabels :string[]|undefined, span: string, jaegerAddress: string, jaegerUri: vscode.Uri): string {
104+
let importance;
105+
if (insightGroup) {
106+
const importanceArray: number[] = insightGroup.map(
107+
(insight) => insight.importance
108+
);
109+
importance = Math.min(...importanceArray);
110+
}
111+
112+
acc[span.id] = {
113+
importance,
114+
};
115+
116+
return acc;
117+
},
118+
{}
119+
);
120+
121+
this._panel.webview.postMessage({
122+
command: "setSpansWithResolvedLocation",
123+
data: spansInfo,
124+
});
125+
}
126+
127+
public getHtml(
128+
traceIds: string[],
129+
traceIdLabels: string[] | undefined,
130+
span: string,
131+
jaegerAddress: string,
132+
jaegerUri: vscode.Uri
133+
): string {
134+
const webview = this._panel.webview;
7135
const staticPath = `${jaegerUri}/static`;
8136

9137
let startPath = "";
10138
if (traceIds.length === 1) {
11139
startPath = `/trace/${traceIds[0]}`;
12140
} else if (traceIds.length === 2 && traceIdLabels != null) {
13-
const trace1 = traceIds[0].toLocaleLowerCase();
14-
const trace2 = traceIds[1].toLocaleLowerCase();
141+
const trace1 = traceIds[0].toLocaleLowerCase();
142+
const trace2 = traceIds[1].toLocaleLowerCase();
15143
startPath = `/trace/${trace1}...${trace2}?cohort=${trace1}&cohort=${trace2}`;
16144
}
17145

18-
const html = `<!doctype html>
19-
<html lang="en">
20-
21-
<head>
22-
<meta charset="utf-8">
23-
<meta name="viewport" content="width=device-width,initial-scale=1">
24-
<base href="/" data-inject-target="BASE_URL" />
25-
<title>Jaeger UI</title>
26-
<script>// Jaeger UI config data is embedded by the query-service via search-replace.
27-
// This is later merged with defaults into the redux \`state.config\` via
28-
// src/utils/config/get-config.js.
29-
// JAEGER_CONFIG_JS
30-
// the line above may be replaced by user-provided JS file that should define a UIConfig function.
31-
function getJaegerUiConfig() {
32-
if (typeof window.UIConfig === 'function') {
33-
return UIConfig();
34-
}
35-
const DEFAULT_CONFIG = null;
36-
const JAEGER_CONFIG = DEFAULT_CONFIG;
37-
return JAEGER_CONFIG;
38-
}
39-
// Jaeger version data is embedded by the query-service via search/replace.
40-
function getJaegerVersion() {
41-
const DEFAULT_VERSION = { "gitCommit": "", "gitVersion": "", "buildDate": "" };
42-
const JAEGER_VERSION = DEFAULT_VERSION;
43-
return JAEGER_VERSION;
44-
}
45-
</script>
46-
<script>
47-
// Variables for injection by VS Code extension
48-
var VS_CODE_SETTINGS = {
49-
apiBaseUrl: "${jaegerAddress}",
50-
startPath: "${startPath}",
51-
staticPath: "${jaegerUri}/",
52-
embeddedMode: true
53-
};
146+
const nonce = getNonce();
54147

55-
// Store list of trace spans with resolved location globally
56-
var spansWithResolvedLocation = {};
57-
var pendingOperationsCount = 0;
58-
59-
window.addEventListener("message", (e) => {
60-
const message = e.data;
61-
switch (message.command) {
62-
case "setSpansWithResolvedLocation":
63-
spansWithResolvedLocation = message.data;
64-
pendingOperationsCount--;
65-
}
66-
});
67-
68-
var vscode = acquireVsCodeApi();
69-
</script>
70-
<!-- Reset default styles set by VS Code -->
71-
<style>
72-
body {
73-
padding: 0;
74-
}
75-
</style>
76-
<link href="${staticPath}/css/1.b0b1393a.chunk.css" rel="stylesheet">
77-
<link href="${staticPath}/css/main.27c048eb.chunk.css" rel="stylesheet">
78-
</head>
148+
const html = `
149+
<!doctype html>
150+
<html lang="en">
151+
152+
<head>
153+
<meta charset="utf-8">
154+
<meta name="viewport" content="width=device-width,initial-scale=1">
155+
<meta http-equiv="Content-Security-Policy"
156+
content="
157+
default-src 'none';
158+
style-src ${webview.cspSource} 'unsafe-inline';
159+
img-src ${webview.cspSource};
160+
script-src ${webview.cspSource} 'nonce-${nonce}';
161+
connect-src ${jaegerAddress};
162+
"
163+
>
164+
<base href="/" data-inject-target="BASE_URL" />
165+
<title>Jaeger UI</title>
166+
<script nonce="${nonce}">// Jaeger UI config data is embedded by the query-service via search-replace.
167+
// This is later merged with defaults into the redux \`state.config\` via
168+
// src/utils/config/get-config.js.
169+
// JAEGER_CONFIG_JS
170+
// the line above may be replaced by user-provided JS file that should define a UIConfig function.
171+
function getJaegerUiConfig() {
172+
if (typeof window.UIConfig === 'function') {
173+
return UIConfig();
174+
}
175+
const DEFAULT_CONFIG = null;
176+
const JAEGER_CONFIG = DEFAULT_CONFIG;
177+
return JAEGER_CONFIG;
178+
}
179+
// Jaeger version data is embedded by the query-service via search/replace.
180+
function getJaegerVersion() {
181+
const DEFAULT_VERSION = { "gitCommit": "", "gitVersion": "", "buildDate": "" };
182+
const JAEGER_VERSION = DEFAULT_VERSION;
183+
return JAEGER_VERSION;
184+
}
185+
</script>
186+
<script nonce="${nonce}">
187+
// Variables for injection by VS Code extension
188+
var VS_CODE_SETTINGS = {
189+
apiBaseUrl: "${jaegerAddress}",
190+
startPath: "${startPath}",
191+
staticPath: "${jaegerUri}/",
192+
embeddedMode: true
193+
};
194+
// Store list of trace spans with resolved location globally
195+
var spansWithResolvedLocation = {};
196+
var pendingOperationsCount = 0;
79197
80-
<body>
81-
<!-- TODO: Remove these images after resolving the HTTP 404 problem with dynamically loaded assets -->
82-
<img src="${staticPath}/media/jaeger-logo.a7093b12.svg" style="display: none;">
83-
<img src="${staticPath}/media/monitor.c9164c96.png" style="display: none;">
84-
<img src="${staticPath}/media/code.351c1388.svg" style="display: none;">
198+
window.addEventListener("message", (e) => {
199+
const message = e.data;
200+
switch (message.command) {
201+
case "setSpansWithResolvedLocation":
202+
spansWithResolvedLocation = message.data;
203+
pendingOperationsCount--;
204+
}
205+
});
206+
</script>
207+
<!-- Reset default styles set by VS Code -->
208+
<style>
209+
body {
210+
padding: 0;
211+
}
212+
</style>
213+
<link href="${staticPath}/css/1.b0b1393a.chunk.css" rel="stylesheet">
214+
<link href="${staticPath}/css/main.27c048eb.chunk.css" rel="stylesheet">
215+
</head>
85216
86-
<div id="jaeger-ui-root"></div>
87-
<script>!function (l) { function e(e) { for (var r, t, n = e[0], o = e[1], u = e[2], f = 0, i = []; f < n.length; f++)t = n[f], p[t] && i.push(p[t][0]), p[t] = 0; for (r in o) Object.prototype.hasOwnProperty.call(o, r) && (l[r] = o[r]); for (s && s(e); i.length;)i.shift()(); return c.push.apply(c, u || []), a() } function a() { for (var e, r = 0; r < c.length; r++) { for (var t = c[r], n = !0, o = 1; o < t.length; o++) { var u = t[o]; 0 !== p[u] && (n = !1) } n && (c.splice(r--, 1), e = f(f.s = t[0])) } return e } var t = {}, p = { 2: 0 }, c = []; function f(e) { if (t[e]) return t[e].exports; var r = t[e] = { i: e, l: !1, exports: {} }; return l[e].call(r.exports, r, r.exports, f), r.l = !0, r.exports } f.m = l, f.c = t, f.d = function (e, r, t) { f.o(e, r) || Object.defineProperty(e, r, { enumerable: !0, get: t }) }, f.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, f.t = function (r, e) { if (1 & e && (r = f(r)), 8 & e) return r; if (4 & e && "object" == typeof r && r && r.__esModule) return r; var t = Object.create(null); if (f.r(t), Object.defineProperty(t, "default", { enumerable: !0, value: r }), 2 & e && "string" != typeof r) for (var n in r) f.d(t, n, function (e) { return r[e] }.bind(null, n)); return t }, f.n = function (e) { var r = e && e.__esModule ? function () { return e.default } : function () { return e }; return f.d(r, "a", r), r }, f.o = function (e, r) { return Object.prototype.hasOwnProperty.call(e, r) }, f.p = "./"; var r = window.webpackJsonp = window.webpackJsonp || [], n = r.push.bind(r); r.push = e, r = r.slice(); for (var o = 0; o < r.length; o++)e(r[o]); var s = n; a() }([])</script>
88-
<script src="${staticPath}/js/1.fa29228e.chunk.js"></script>
89-
<script src="${staticPath}/js/main.cd5ed381.chunk.js"></script>
90-
</body>
217+
<body>
218+
<!-- TODO: Remove these images after resolving the HTTP 404 problem with dynamically loaded assets -->
219+
<img src="${staticPath}/media/jaeger-logo.a7093b12.svg" style="display: none;">
220+
<img src="${staticPath}/media/monitor.c9164c96.png" style="display: none;">
221+
<img src="${staticPath}/media/code.351c1388.svg" style="display: none;">
91222
92-
</html>`;
223+
<div id="jaeger-ui-root"></div>
224+
<script src="${staticPath}/js/vscode.js"></script>
225+
<script nonce="${nonce}">!function (l) { function e(e) { for (var r, t, n = e[0], o = e[1], u = e[2], f = 0, i = []; f < n.length; f++)t = n[f], p[t] && i.push(p[t][0]), p[t] = 0; for (r in o) Object.prototype.hasOwnProperty.call(o, r) && (l[r] = o[r]); for (s && s(e); i.length;)i.shift()(); return c.push.apply(c, u || []), a() } function a() { for (var e, r = 0; r < c.length; r++) { for (var t = c[r], n = !0, o = 1; o < t.length; o++) { var u = t[o]; 0 !== p[u] && (n = !1) } n && (c.splice(r--, 1), e = f(f.s = t[0])) } return e } var t = {}, p = { 2: 0 }, c = []; function f(e) { if (t[e]) return t[e].exports; var r = t[e] = { i: e, l: !1, exports: {} }; return l[e].call(r.exports, r, r.exports, f), r.l = !0, r.exports } f.m = l, f.c = t, f.d = function (e, r, t) { f.o(e, r) || Object.defineProperty(e, r, { enumerable: !0, get: t }) }, f.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, f.t = function (r, e) { if (1 & e && (r = f(r)), 8 & e) return r; if (4 & e && "object" == typeof r && r && r.__esModule) return r; var t = Object.create(null); if (f.r(t), Object.defineProperty(t, "default", { enumerable: !0, value: r }), 2 & e && "string" != typeof r) for (var n in r) f.d(t, n, function (e) { return r[e] }.bind(null, n)); return t }, f.n = function (e) { var r = e && e.__esModule ? function () { return e.default } : function () { return e }; return f.d(r, "a", r), r }, f.o = function (e, r) { return Object.prototype.hasOwnProperty.call(e, r) }, f.p = "./"; var r = window.webpackJsonp = window.webpackJsonp || [], n = r.push.bind(r); r.push = e, r = r.slice(); for (var o = 0; o < r.length; o++)e(r[o]); var s = n; a() }([])</script>
226+
<script src="${staticPath}/js/1.65ef2ae1.chunk.js"></script>
227+
<script src="${staticPath}/js/main.7706e4a8.chunk.js"></script>
228+
</body>
229+
230+
</html>
231+
`;
93232

94233
return html;
95234
}
96235
}
236+
237+
function getNonce() {
238+
let text = "";
239+
const possible =
240+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
241+
for (let i = 0; i < 32; i++) {
242+
text += possible.charAt(Math.floor(Math.random() * possible.length));
243+
}
244+
return text;
245+
}

0 commit comments

Comments
 (0)