diff --git a/.vscodeignore b/.vscodeignore
index 1db9423f..8d41cd70 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -11,6 +11,11 @@ node_modules
server
test/
img/
+docs/
+CLAUDE.md
+build-api.js
+qcumber.q
+secure-processes.md
qcumber.sh
esbuild.mjs
sonar-project.properties
diff --git a/docs/index.md b/docs/index.md
index ec25778c..af2fe894 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,23 +1,27 @@
---
type: Index
title: kdb VS Code Extension Docs
-description: In-tree documentation for the kdb VS Code extension, split into user notes and developer notes.
+description:
+ In-tree documentation for the kdb VS Code extension, split into user notes and
+ developer notes.
tags: [kdb, vscode, documentation]
timestamp: 2026-07-10
---
-# kdb VS Code Extension Docs
+# kdb VS Code Extension Wiki
-Welcome to the developer and user documentation for the
-[kdb VS Code extension](https://marketplace.visualstudio.com/items?itemName=KX.kdb).
+This is the in-repo wiki for the
+[kdb VS Code extension](https://marketplace.visualstudio.com/items?itemName=KX.kdb),
+covering developer and user notes.
-These docs follow the [Open Knowledge Format](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing/):
+These pages follow the
+[Open Knowledge Format](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing/):
plain markdown files with YAML frontmatter, `index.md` files for navigation, and
relative cross-links. They were ported from the project's
[GitHub wiki](https://github.com/KxSystems/kx-vscode/wiki).
> The end-user product documentation is hosted separately at
-> .
+> .
## User Notes
diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts
index 5a3ead24..febe8202 100644
--- a/src/commands/serverCommand.ts
+++ b/src/commands/serverCommand.ts
@@ -88,6 +88,7 @@ import {
workspaceHas,
} from "../utils/workspace";
import {
+ validateInsightsServerUrl,
validateServerAlias,
validateServerName,
validateServerPort,
@@ -120,8 +121,14 @@ export async function addInsightsConnection(
notify(aliasValidation, MessageKind.ERROR, { logger });
return;
}
+ const serverValidation = validateInsightsServerUrl(insightsData.server);
+ if (serverValidation) {
+ notify(serverValidation, MessageKind.ERROR, { logger });
+ return;
+ }
+ const server = insightsData.server!.trim();
if (insightsData.alias === undefined || insightsData.alias === "") {
- const host = new url.URL(insightsData.server!);
+ const host = new url.URL(server);
insightsData.alias = host.host;
}
@@ -138,10 +145,6 @@ export async function addInsightsConnection(
return;
} else {
const key = insightsData.alias;
- let server = insightsData.server || "";
- if (!/^https?:\/\//i.exec(server)) {
- server = "https://" + server;
- }
if (insights === undefined) {
insights = {
key: {
@@ -201,6 +204,12 @@ export async function editInsightsConnection(
notify(aliasValidation, MessageKind.ERROR, { logger });
return;
}
+ const serverValidation = validateInsightsServerUrl(insightsData.server);
+ if (serverValidation) {
+ notify(serverValidation, MessageKind.ERROR, { logger });
+ return;
+ }
+ insightsData.server = insightsData.server.trim();
const isConnectedConn = isConnected(oldAlias);
await disconnect(oldAlias);
if (insightsData.alias === undefined || insightsData.alias === "") {
diff --git a/src/services/dataSourceEditorProvider.ts b/src/services/dataSourceEditorProvider.ts
index 86a51d66..dd6f61ff 100644
--- a/src/services/dataSourceEditorProvider.ts
+++ b/src/services/dataSourceEditorProvider.ts
@@ -105,7 +105,7 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider {
} catch {
notify(
"No database running in this Insights connection.",
- MessageKind.ERROR,
+ MessageKind.WARNING,
{ logger },
);
meta = Promise.resolve({});
diff --git a/src/services/kdbInsights/codeFlowLogin.ts b/src/services/kdbInsights/codeFlowLogin.ts
index 9736d929..0a187fa3 100644
--- a/src/services/kdbInsights/codeFlowLogin.ts
+++ b/src/services/kdbInsights/codeFlowLogin.ts
@@ -48,7 +48,7 @@ async function getAuthPrefix(
insightsUrl,
).toString(),
{
- maxRedirects: 0,
+ maxRedirects: 5,
validateStatus: () => true,
transformResponse: (res) => res,
responseType: "json",
diff --git a/src/validators/kdbValidator.ts b/src/validators/kdbValidator.ts
index f66d1f7c..304dba7c 100644
--- a/src/validators/kdbValidator.ts
+++ b/src/validators/kdbValidator.ts
@@ -96,6 +96,18 @@ export function validateServerPassword(
return undefined;
}
+export function validateInsightsServerUrl(
+ input: string | undefined,
+): string | undefined {
+ if (input === undefined || input.trim() === "") {
+ return "Insights connection address is required.";
+ }
+ if (!/^https:\/\//i.exec(input.trim())) {
+ return "Insights connections require a secure https:// URL (for example, https://myinsights.example.com).";
+ }
+ return undefined;
+}
+
export function validateTls(input: string | undefined): string | undefined {
if (input !== undefined && input !== "") {
if (!validateUtils.isBoolean(input)) {
diff --git a/src/webview/components/kdbNewConnectionView.ts b/src/webview/components/kdbNewConnectionView.ts
index f0f30576..182ac612 100644
--- a/src/webview/components/kdbNewConnectionView.ts
+++ b/src/webview/components/kdbNewConnectionView.ts
@@ -231,7 +231,7 @@ export class KdbNewConnectionView extends LitElement {
class="text-field larger option-title"
placeholder="${serverType === ServerType.KDB
? "127.0.0.1 or localhost"
- : `myinsights.clouddeploy.com`}"
+ : `https://myinsights.example.com`}"
value="${live(
serverType === ServerType.KDB
? this.kdbServer.serverName
diff --git a/test/suite/commands/serverCommand.test.ts b/test/suite/commands/serverCommand.test.ts
index 2ffd2063..9033b807 100644
--- a/test/suite/commands/serverCommand.test.ts
+++ b/test/suite/commands/serverCommand.test.ts
@@ -195,6 +195,45 @@ describe("serverCommand", () => {
sinon.match.any,
);
});
+
+ it("should reject an insecure http:// Insights URL", async () => {
+ getInsightsStub.returns({});
+ insightsData.server = "http://insightsservername.com/";
+ await serverCommand.addInsightsConnection(insightsData);
+ sinon.assert.notCalled(updateInsightsStub);
+ sinon.assert.calledWithExactly(
+ notifyStub,
+ sinon.match(/require a secure https:\/\/ URL/),
+ sinon.match.any,
+ sinon.match.any,
+ );
+ });
+
+ it("should reject an Insights URL with no protocol", async () => {
+ getInsightsStub.returns({});
+ insightsData.server = "insightsservername.com";
+ await serverCommand.addInsightsConnection(insightsData);
+ sinon.assert.notCalled(updateInsightsStub);
+ sinon.assert.calledWithExactly(
+ notifyStub,
+ sinon.match(/require a secure https:\/\/ URL/),
+ sinon.match.any,
+ sinon.match.any,
+ );
+ });
+
+ it("should reject an empty Insights URL", async () => {
+ getInsightsStub.returns({});
+ insightsData.server = "";
+ await serverCommand.addInsightsConnection(insightsData);
+ sinon.assert.notCalled(updateInsightsStub);
+ sinon.assert.calledWithExactly(
+ notifyStub,
+ "Insights connection address is required.",
+ sinon.match.any,
+ sinon.match.any,
+ );
+ });
});
describe("addKdbConnection", () => {
diff --git a/test/suite/webPanels/webViews/kdbNewConnectionView.test.ts b/test/suite/webPanels/webViews/kdbNewConnectionView.test.ts
index 0ce2db09..4474b1d4 100644
--- a/test/suite/webPanels/webViews/kdbNewConnectionView.test.ts
+++ b/test/suite/webPanels/webViews/kdbNewConnectionView.test.ts
@@ -181,7 +181,7 @@ describe("KdbNewConnectionView", () => {
it("should render connection address for Insights", () => {
const result = view.renderConnAddress(ServerType.INSIGHTS);
assert.strictEqual(
- JSON.stringify(result).includes("myinsights.clouddeploy.com"),
+ JSON.stringify(result).includes("https://myinsights.example.com"),
true,
);
});