@checkstack/backend-api 0.7.0 → 0.8.1

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,50 @@
1
1
  # @checkstack/backend-api
2
2
 
3
+ ## 0.8.1
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/common@0.6.3
19
+ - @checkstack/queue-api@0.2.6
20
+ - @checkstack/healthcheck-common@0.8.3
21
+ - @checkstack/signal-common@0.1.7
22
+
23
+ ## 0.8.0
24
+
25
+ ### Minor Changes
26
+
27
+ - 869b4ab: ## Health Check Execution Improvements
28
+
29
+ ### Breaking Changes (backend-api)
30
+
31
+ - `HealthCheckStrategy.createClient()` now accepts `unknown` instead of `TConfig` due to TypeScript contravariance constraints. Implementations should use `this.config.validate(config)` to narrow the type.
32
+
33
+ ### Features
34
+
35
+ - **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.
36
+ - **Parallel collector execution**: Collectors now run in parallel using `Promise.allSettled()`, improving performance while ensuring all collectors complete regardless of individual failures.
37
+ - **Base strategy config schema**: All strategy configs now extend `baseStrategyConfigSchema` which provides a standardized `timeout` field with sensible defaults (30s, min 100ms).
38
+
39
+ ### Fixes
40
+
41
+ - Fixed HTTP and Jenkins strategies clearing timeouts before reading the full response body.
42
+ - Simplified registry type signatures by using default type parameters.
43
+
44
+ ### Patch Changes
45
+
46
+ - @checkstack/queue-api@0.2.5
47
+
3
48
  ## 0.7.0
4
49
 
5
50
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend-api",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "scripts": {
@@ -9,10 +9,10 @@
9
9
  "lint:code": "eslint . --max-warnings 0"
10
10
  },
11
11
  "dependencies": {
12
- "@checkstack/common": "0.6.1",
13
- "@checkstack/healthcheck-common": "0.8.1",
14
- "@checkstack/queue-api": "0.2.2",
15
- "@checkstack/signal-common": "0.1.5",
12
+ "@checkstack/common": "0.6.2",
13
+ "@checkstack/healthcheck-common": "0.8.2",
14
+ "@checkstack/queue-api": "0.2.5",
15
+ "@checkstack/signal-common": "0.1.6",
16
16
  "@orpc/client": "^1.13.2",
17
17
  "@orpc/openapi": "^1.13.2",
18
18
  "@orpc/server": "^1.13.2",
@@ -0,0 +1,26 @@
1
+ import z from "zod";
2
+ import { configNumber } from "./zod-config";
3
+
4
+ /**
5
+ * Base configuration schema that all strategy configs should extend.
6
+ * Provides the required `timeout` field with a sensible default.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const myConfigSchema = baseStrategyConfigSchema.extend({
11
+ * host: z.string().describe("Server hostname"),
12
+ * port: z.number().default(22),
13
+ * });
14
+ * ```
15
+ */
16
+ export const baseStrategyConfigSchema = z.object({
17
+ timeout: configNumber({})
18
+ .min(100)
19
+ .default(30_000)
20
+ .describe("Execution timeout in milliseconds"),
21
+ });
22
+
23
+ /**
24
+ * Base config type that all strategy configs must satisfy.
25
+ */
26
+ export type BaseStrategyConfig = z.infer<typeof baseStrategyConfigSchema>;
@@ -4,6 +4,7 @@ import type {
4
4
  VersionedAggregated,
5
5
  AggregatedResultShape,
6
6
  } from "./aggregated-result";
7
+ import type { BaseStrategyConfig } from "./base-strategy-config";
7
8
 
8
9
  /**
9
10
  * Health check result with typed metadata.
@@ -31,7 +32,7 @@ export interface HealthCheckRunForAggregation<
31
32
  * Connected transport client with cleanup capability.
32
33
  */
33
34
  export interface ConnectedClient<
34
- TClient extends TransportClient<unknown, unknown>,
35
+ TClient extends TransportClient<never, unknown>,
35
36
  > {
36
37
  /** The connected transport client */
37
38
  client: TClient;
@@ -46,18 +47,18 @@ export interface ConnectedClient<
46
47
  * and returns a transport client. The platform executor handles running
47
48
  * collectors and basic health check logic (connectivity test, latency measurement).
48
49
  *
49
- * @template TConfig - Configuration type for this strategy
50
+ * @template TConfig - Configuration type for this strategy (must include timeout)
50
51
  * @template TClient - Transport client type (e.g., SshTransportClient)
51
52
  * @template TResult - Per-run result type (for aggregation)
52
53
  * @template TAggregatedFields - Aggregated field definitions for VersionedAggregated
53
54
  */
54
55
  export interface HealthCheckStrategy<
55
- TConfig = unknown,
56
- TClient extends TransportClient<unknown, unknown> = TransportClient<
57
- unknown,
56
+ TConfig extends BaseStrategyConfig = BaseStrategyConfig,
57
+ TClient extends TransportClient<never, unknown> = TransportClient<
58
+ never,
58
59
  unknown
59
60
  >,
60
- TResult = Record<string, unknown>,
61
+ TResult = unknown,
61
62
  TAggregatedFields extends AggregatedResultShape = AggregatedResultShape,
62
63
  > {
63
64
  id: string;
@@ -77,11 +78,11 @@ export interface HealthCheckStrategy<
77
78
  * Create a connected transport client from the configuration.
78
79
  * The platform will use this client to execute collectors.
79
80
  *
80
- * @param config - Validated strategy configuration
81
+ * @param config - Strategy configuration (use config.validate() to narrow type)
81
82
  * @returns Connected client wrapper with close() method
82
83
  * @throws Error if connection fails (will be caught by executor)
83
84
  */
84
- createClient(config: TConfig): Promise<ConnectedClient<TClient>>;
85
+ createClient(config: unknown): Promise<ConnectedClient<TClient>>;
85
86
 
86
87
  /**
87
88
  * Incrementally merge new run data into an existing aggregate.
@@ -103,41 +104,15 @@ export interface HealthCheckStrategy<
103
104
  * A registered strategy with its owning plugin metadata and qualified ID.
104
105
  */
105
106
  export interface RegisteredStrategy {
106
- strategy: HealthCheckStrategy<
107
- unknown,
108
- TransportClient<unknown, unknown>,
109
- unknown,
110
- AggregatedResultShape
111
- >;
107
+ strategy: HealthCheckStrategy;
112
108
  ownerPluginId: string;
113
109
  qualifiedId: string;
114
110
  }
115
111
 
116
112
  export interface HealthCheckRegistry {
117
- register(
118
- strategy: HealthCheckStrategy<
119
- unknown,
120
- TransportClient<unknown, unknown>,
121
- unknown,
122
- AggregatedResultShape
123
- >,
124
- ): void;
125
- getStrategy(
126
- id: string,
127
- ):
128
- | HealthCheckStrategy<
129
- unknown,
130
- TransportClient<unknown, unknown>,
131
- unknown,
132
- AggregatedResultShape
133
- >
134
- | undefined;
135
- getStrategies(): HealthCheckStrategy<
136
- unknown,
137
- TransportClient<unknown, unknown>,
138
- unknown,
139
- AggregatedResultShape
140
- >[];
113
+ register<S extends HealthCheckStrategy>(strategy: S): void;
114
+ getStrategy(id: string): HealthCheckStrategy | undefined;
115
+ getStrategies(): HealthCheckStrategy[];
141
116
  /**
142
117
  * Get all registered strategies with their metadata (qualified ID, owner plugin).
143
118
  */
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./extension-point";
3
3
  export * from "./core-services";
4
4
  export * from "./plugin-system";
5
5
  export * from "./health-check";
6
+ export * from "./base-strategy-config";
6
7
  export * from "./auth-strategy";
7
8
  export * from "./zod-config";
8
9
  export * from "./encryption";
package/src/rpc.ts CHANGED
@@ -206,6 +206,23 @@ export const autoAuthMiddleware = os.middleware(
206
206
 
207
207
  // 5. Skip remaining checks for services - they are trusted
208
208
  if (user?.type === "service") {
209
+ // SECURITY: Check service-level scope restrictions
210
+ const serviceScope = meta?.serviceScope;
211
+ if (serviceScope && serviceScope.length > 0) {
212
+ const isAllowed = serviceScope.some((allowedPattern) => {
213
+ if (allowedPattern.endsWith("*")) {
214
+ const prefix = allowedPattern.slice(0, -1);
215
+ return user.pluginId.startsWith(prefix);
216
+ }
217
+ return allowedPattern === user.pluginId;
218
+ });
219
+
220
+ if (!isAllowed) {
221
+ throw new ORPCError("FORBIDDEN", {
222
+ message: `Service '${user.pluginId}' is not allowed to call this endpoint`,
223
+ });
224
+ }
225
+ }
209
226
  return next({});
210
227
  }
211
228
 
@@ -483,8 +500,8 @@ async function checkResourceAccessViaS2S({
483
500
  });
484
501
  return result.hasAccess;
485
502
  } catch {
486
- // If team access check fails (e.g., service not available), fall back to global access
487
- return hasGlobalAccess;
503
+ // SECURITY: Fail-Closed deny access when S2S check fails
504
+ return false;
488
505
  }
489
506
  }
490
507
 
@@ -520,8 +537,8 @@ async function getAccessibleResourceIdsViaS2S({
520
537
  hasGlobalAccess,
521
538
  });
522
539
  } catch {
523
- // If team access check fails, fall back to global access behavior
524
- return hasGlobalAccess ? resourceIds : [];
540
+ // SECURITY: Fail-Closed return empty set when S2S check fails
541
+ return [];
525
542
  }
526
543
  }
527
544