@checkstack/backend 0.4.12 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # @checkstack/backend
2
2
 
3
+ ## 0.4.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 0ebbe56: Security Vulnerability Remediation completed:
8
+ - Refactored core authorization to Fail-Closed architecture with secure defaults.
9
+ - Implemented `assertTeamManagementAccess` to resolve BOLA in Teams Management.
10
+ - Protected internal S2S capabilities via explicit wildcard `serviceScope` definitions.
11
+ - Disarmed OS Command Injection in DiskCollector via strict regex validation and bash escaping.
12
+ - Re-architected inline script processing executing scripts in sandboxed Web Worker contexts.
13
+ - Isolated subprocess environment scopes in PingStrategy limiting variable leakage.
14
+ - Enforced strict token/API Key parsing with URLSearchParams checking.
15
+ - Explicitly fail-fast on missing DATABASE_URL configuration across independent backend clusters.
16
+ - Activated strict HTTP Security Headers (HSTS, CSP, X-Frame-Options) across the API automatically.
17
+ - Updated dependencies [0ebbe56]
18
+ - @checkstack/auth-common@0.5.6
19
+ - @checkstack/backend-api@0.8.1
20
+ - @checkstack/common@0.6.3
21
+ - @checkstack/queue-api@0.2.6
22
+ - @checkstack/signal-backend@0.1.12
23
+ - @checkstack/api-docs-common@0.1.7
24
+ - @checkstack/signal-common@0.1.7
25
+
26
+ ## 0.4.13
27
+
28
+ ### Patch Changes
29
+
30
+ - 869b4ab: ## Health Check Execution Improvements
31
+
32
+ ### Breaking Changes (backend-api)
33
+
34
+ - `HealthCheckStrategy.createClient()` now accepts `unknown` instead of `TConfig` due to TypeScript contravariance constraints. Implementations should use `this.config.validate(config)` to narrow the type.
35
+
36
+ ### Features
37
+
38
+ - **Platform-level hard timeout**: The executor now wraps the entire health check execution (connection + all collectors) in a single timeout, ensuring checks never hang indefinitely.
39
+ - **Parallel collector execution**: Collectors now run in parallel using `Promise.allSettled()`, improving performance while ensuring all collectors complete regardless of individual failures.
40
+ - **Base strategy config schema**: All strategy configs now extend `baseStrategyConfigSchema` which provides a standardized `timeout` field with sensible defaults (30s, min 100ms).
41
+
42
+ ### Fixes
43
+
44
+ - Fixed HTTP and Jenkins strategies clearing timeouts before reading the full response body.
45
+ - Simplified registry type signatures by using default type parameters.
46
+
47
+ - Updated dependencies [869b4ab]
48
+ - @checkstack/backend-api@0.8.0
49
+ - @checkstack/queue-api@0.2.5
50
+ - @checkstack/signal-backend@0.1.11
51
+
3
52
  ## 0.4.12
4
53
 
5
54
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "bun --env-file=../../.env --watch src/index.ts",
@@ -10,14 +10,14 @@
10
10
  "lint:code": "eslint . --max-warnings 0"
11
11
  },
12
12
  "dependencies": {
13
- "@checkstack/api-docs-common": "0.1.5",
14
- "@checkstack/auth-common": "0.5.4",
15
- "@checkstack/backend-api": "0.5.2",
16
- "@checkstack/common": "0.6.1",
13
+ "@checkstack/api-docs-common": "0.1.6",
14
+ "@checkstack/auth-common": "0.5.5",
15
+ "@checkstack/backend-api": "0.8.0",
16
+ "@checkstack/common": "0.6.2",
17
17
  "@checkstack/drizzle-helper": "0.0.3",
18
- "@checkstack/queue-api": "0.2.2",
19
- "@checkstack/signal-backend": "0.1.8",
20
- "@checkstack/signal-common": "0.1.5",
18
+ "@checkstack/queue-api": "0.2.5",
19
+ "@checkstack/signal-backend": "0.1.11",
20
+ "@checkstack/signal-common": "0.1.6",
21
21
  "@hono/zod-validator": "^0.7.6",
22
22
  "@orpc/client": "^1.13.2",
23
23
  "@orpc/openapi": "^1.13.2",
@@ -37,6 +37,6 @@
37
37
  "@types/bun": "latest",
38
38
  "@checkstack/tsconfig": "0.0.3",
39
39
  "@checkstack/scripts": "0.1.1",
40
- "@checkstack/test-utils-backend": "0.1.8"
40
+ "@checkstack/test-utils-backend": "0.1.11"
41
41
  }
42
42
  }
@@ -46,7 +46,7 @@ describe("HealthCheck Plugin Integration", () => {
46
46
  description: "A test strategy for integration testing",
47
47
  config: new Versioned({
48
48
  version: 1,
49
- schema: z.object({}),
49
+ schema: z.object({ timeout: z.number().default(30_000) }),
50
50
  }),
51
51
  result: new Versioned({
52
52
  version: 1,
package/src/index.ts CHANGED
@@ -60,6 +60,15 @@ app.use(
60
60
  );
61
61
  app.use("*", logger());
62
62
 
63
+ // SECURITY: Add missing standard security headers across all API responses
64
+ app.use("/api/*", async (c, next) => {
65
+ await next();
66
+ c.res.headers.set("X-Content-Type-Options", "nosniff");
67
+ c.res.headers.set("X-Frame-Options", "DENY");
68
+ c.res.headers.set("Content-Security-Policy", "default-src 'none'; frame-ancestors 'none'");
69
+ c.res.headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
70
+ });
71
+
63
72
  // Runtime config endpoint - returns BASE_URL for frontend
64
73
  app.get("/api/config", (c) => {
65
74
  const baseUrl = process.env.BASE_URL || "http://localhost:3000";
@@ -176,9 +176,12 @@ export function registerCoreServices({
176
176
  });
177
177
  const authClient = rpcClient.forPlugin(AuthApi);
178
178
  return await authClient.checkResourceTeamAccess(params);
179
- } catch {
180
- // Fall back to global access on error
181
- return { hasAccess: params.hasGlobalAccess };
179
+ } catch (error) {
180
+ // SECURITY: Fail-Closed deny access when auth service is unavailable
181
+ rootLogger.error(
182
+ `[auth] checkResourceTeamAccess: S2S call failed for resource ${params.resourceType}:${params.resourceId}. Denying access (Fail-Closed). Error: ${error}`,
183
+ );
184
+ return { hasAccess: false };
182
185
  }
183
186
  },
184
187
 
@@ -189,9 +192,12 @@ export function registerCoreServices({
189
192
  });
190
193
  const authClient = rpcClient.forPlugin(AuthApi);
191
194
  return await authClient.getAccessibleResourceIds(params);
192
- } catch {
193
- // Fall back to global access on error
194
- return params.hasGlobalAccess ? params.resourceIds : [];
195
+ } catch (error) {
196
+ // SECURITY: Fail-Closed return empty set when auth service is unavailable
197
+ rootLogger.error(
198
+ `[auth] getAccessibleResourceIds: S2S call failed for resource type ${params.resourceType}. Denying access (Fail-Closed). Error: ${error}`,
199
+ );
200
+ return [];
195
201
  }
196
202
  },
197
203
  };
@@ -30,7 +30,7 @@ describe("CoreHealthCheckRegistry", () => {
30
30
  description: "A test strategy",
31
31
  config: new Versioned({
32
32
  version: 1,
33
- schema: z.object({}),
33
+ schema: z.object({ timeout: z.number().default(30_000) }),
34
34
  }),
35
35
  result: new Versioned({
36
36
  version: 1,
@@ -52,7 +52,7 @@ describe("CoreHealthCheckRegistry", () => {
52
52
  description: "Another test strategy",
53
53
  config: new Versioned({
54
54
  version: 1,
55
- schema: z.object({}),
55
+ schema: z.object({ timeout: z.number().default(30_000) }),
56
56
  }),
57
57
  result: new Versioned({
58
58
  version: 1,
@@ -25,17 +25,21 @@ export class CoreHealthCheckRegistry {
25
25
  * Register a strategy with its owning plugin.
26
26
  * The strategy ID is stored as: ownerPluginId.strategyId
27
27
  */
28
- registerWithOwner(
29
- strategy: HealthCheckStrategy,
30
- ownerPlugin: PluginMetadata
28
+ registerWithOwner<S extends HealthCheckStrategy>(
29
+ strategy: S,
30
+ ownerPlugin: PluginMetadata,
31
31
  ): void {
32
32
  const qualifiedId = `${ownerPlugin.pluginId}.${strategy.id}`;
33
33
  if (this.strategies.has(qualifiedId)) {
34
34
  rootLogger.warn(
35
- `HealthCheckStrategy '${qualifiedId}' is already registered. Overwriting.`
35
+ `HealthCheckStrategy '${qualifiedId}' is already registered. Overwriting.`,
36
36
  );
37
37
  }
38
- this.strategies.set(qualifiedId, { strategy, ownerPlugin, qualifiedId });
38
+ this.strategies.set(qualifiedId, {
39
+ strategy: strategy as HealthCheckStrategy,
40
+ ownerPlugin,
41
+ qualifiedId,
42
+ });
39
43
  rootLogger.debug(`✅ Registered HealthCheckStrategy: ${qualifiedId}`);
40
44
  }
41
45
 
@@ -75,10 +79,10 @@ export class CoreHealthCheckRegistry {
75
79
  */
76
80
  export function createScopedHealthCheckRegistry(
77
81
  globalRegistry: CoreHealthCheckRegistry,
78
- ownerPlugin: PluginMetadata
82
+ ownerPlugin: PluginMetadata,
79
83
  ): HealthCheckRegistry {
80
84
  return {
81
- register(strategy: HealthCheckStrategy) {
85
+ register<S extends HealthCheckStrategy>(strategy: S) {
82
86
  globalRegistry.registerWithOwner(strategy, ownerPlugin);
83
87
  },
84
88
  getStrategy(id: string) {