Commit bee7de78 authored by Tomas Vik (OOO back on 2026-08-31)'s avatar Tomas Vik (OOO back on 2026-08-31) 🌴
Browse files

feat: show webview resources on http instances

parent e0d6d879
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ describe('GitLabChatView', () => {
        webview.webview,
        context,
        'gitlab_duo_chat',
        undefined,
        {
          slashCommands: defaultSlashCommands,
        },
+1 −0
Original line number Diff line number Diff line
@@ -173,6 +173,7 @@ export class GitLabChatView {
      this.#chatView.webview,
      this.#context,
      'gitlab_duo_chat',
      undefined,
      initialState,
    );

+29 −4
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ describe('prepareWebViewSource', () => {
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="Content-Security-Policy" content="img-src vscode-resource: https:; script-src 'nonce-{{nonce}}';">
      <meta http-equiv="Content-Security-Policy" content="img-src vscode-resource: data: https:{{httpProtocol}}; script-src 'nonce-{{nonce}}';">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>GitLab Workflow</title>
      <script crossorigin type="module" src="/gitlab_duo_chat/assets/app.js"></script>
@@ -28,7 +28,7 @@ describe('prepareWebViewSource', () => {
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="Content-Security-Policy" content="img-src vscode-resource: https:; script-src 'nonce-123';">
      <meta http-equiv="Content-Security-Policy" content="img-src vscode-resource: data: https:; script-src 'nonce-123';">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>GitLab Workflow</title>
      <script nonce="123" crossorigin type="module" src="file:///foo/bar/webviews/gitlab_duo_chat/assets/app.js"></script>
@@ -58,15 +58,40 @@ describe('prepareWebViewSource', () => {
  });

  it('returns WebView source with inserted nonce and assets', async () => {
    const result = await prepareWebviewSource(webview, context, 'gitlab_duo_chat');
    const result = await prepareWebviewSource(
      webview,
      context,
      'gitlab_duo_chat',
      'https://gitlab.example.com',
    );
    expect(result).toStrictEqual(expectedHTML);
  });

  it('returns WebView source with HTTP and HTTPS CSP for HTTP GitLab instance', async () => {
    const result = await prepareWebviewSource(
      webview,
      context,
      'gitlab_duo_chat',
      'http://gitlab.example.com',
    );
    const expectedHtmlWithHttp = expectedHTML.replace(
      'img-src vscode-resource: data: https:;',
      'img-src vscode-resource: data: https: http:;',
    );
    expect(result).toStrictEqual(expectedHtmlWithHttp);
  });

  it('returns WebView source with initial app state set', async () => {
    const initState = {
      foo: 'bar',
    };
    const result = await prepareWebviewSource(webview, context, 'gitlab_duo_chat', initState);
    const result = await prepareWebviewSource(
      webview,
      context,
      'gitlab_duo_chat',
      'https://gitlab.example.com',
      initState,
    );
    const expectedHtmlString = expectedHTML.replace(
      "data-initial-state=''",
      `data-initial-state='${JSON.stringify(initState)}'`,
+17 −2
Original line number Diff line number Diff line
@@ -7,8 +7,6 @@ const webviewResourcePaths = {
  styleUri: 'assets/index.css',
} as const;

type WebviewResources = Record<keyof typeof webviewResourcePaths, vscode.Uri>;

const getWebviewUri = (
  path: string,
  webview: vscode.Webview,
@@ -29,13 +27,29 @@ const getWebviewResources = (
  return mapValues(webviewResourcePaths, path => getWebviewUri(path, webview, context, webviewKey));
};

/**
 * Generates HTTP protocol part for CSP based on GitLab instance protocol
 * @param gitlabInstanceUrl The GitLab instance URL to check protocol for
 * @returns ' http:' if HTTP instance, empty string otherwise
 */
const generateHttpProtocol = (gitlabInstanceUrl?: string): string => {
  try {
    const url = new URL(gitlabInstanceUrl || '');
    return url.protocol === 'http:' ? ' http:' : '';
  } catch {
    return '';
  }
};

export const prepareWebviewSource = async (
  webview: vscode.Webview,
  context: vscode.ExtensionContext,
  webviewKey: string,
  instanceUrl: string | undefined,
  initialState?: object,
): Promise<string> => {
  const nonce = generateSecret();
  const httpProtocol = generateHttpProtocol(instanceUrl);

  const { appScriptUri, styleUri } = getWebviewResources(webview, context, webviewKey);
  const fileUri = vscode.Uri.joinPath(context.extensionUri, 'webviews', webviewKey, 'index.html');
@@ -45,6 +59,7 @@ export const prepareWebviewSource = async (

  return fileContent
    .replace(/{{nonce}}/gm, nonce)
    .replace(/{{httpProtocol}}/gm, httpProtocol)
    .replace(/<script /g, `<script nonce="${nonce}" `)
    .replace(`/${webviewKey}/${webviewResourcePaths.styleUri}`, styleUri.toString())
    .replace(`/${webviewKey}/${webviewResourcePaths.appScriptUri}`, appScriptUri.toString())
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ export class SecurityFindingWebviewController implements RepositoryRootWebviewPr
      panel.webview,
      this.#context,
      'security_finding',
      projectInRepository.account.instanceUrl,
    );

    panel.repositoryRoot = projectInRepository.pointer.repository.rootFsPath;
Loading