refactor: comprehensive code review fixes (security, performance, QA)
## Security Improvements - Fix timing attack in verifyApiKey with fixed 256-byte buffer - Fix sortOrder SQL injection with whitelist validation - Fix rate limiting bypass for non-Cloudflare traffic (fail-closed) - Remove stack trace exposure in error responses - Add request_id for audit trail (X-Request-ID header) - Sanitize origin header to prevent log injection - Add content-length validation for /sync endpoint (10KB limit) - Replace Math.random() with crypto.randomUUID() for sync IDs - Expand sensitive data masking patterns (8 → 18) ## Performance Improvements - Reduce rate limiter KV reads from 3 to 1 per request (66% reduction) - Increase sync batch size from 100 to 500 (80% fewer batches) - Fix health check N+1 query with efficient JOINs - Fix COUNT(*) Cartesian product with COUNT(DISTINCT) - Implement shared logger cache pattern across repositories - Add CacheService singleton pattern in recommend.ts - Add composite index for recommendation queries - Implement Anvil pricing query batching (100 per chunk) ## QA Improvements - Add BATCH_SIZE bounds validation (1-1000) - Add pagination bounds (page >= 1, MAX_OFFSET = 100000) - Add min/max range consistency validation - Add DB reference validation for singleton services - Add type guards for database result validation - Add timeout mechanism for external API calls (10-60s) - Use SUPPORTED_PROVIDERS constant instead of hardcoded list ## Removed - Remove Vault integration (using Wrangler secrets) - Remove 6-hour pricing cron (daily sync only) ## Configuration - Add idx_instance_types_specs_filter composite index - Add CORS Access-Control-Expose-Headers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import type { VaultClient } from './vault';
|
||||
import type { VaultCredentials, RegionInput, InstanceTypeInput } from '../types';
|
||||
import type { RegionInput, InstanceTypeInput } from '../types';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
/**
|
||||
@@ -123,126 +122,7 @@ export class RateLimiter {
|
||||
}
|
||||
|
||||
/**
|
||||
* CloudConnector - Abstract base class for cloud provider connectors
|
||||
*
|
||||
* Implements common authentication, rate limiting, and data fetching patterns.
|
||||
* Each provider (Linode, Vultr, etc.) extends this class and implements
|
||||
* provider-specific API calls and data normalization.
|
||||
*
|
||||
* @abstract
|
||||
*
|
||||
* @example
|
||||
* class LinodeConnector extends CloudConnector {
|
||||
* provider = 'linode';
|
||||
*
|
||||
* async fetchRegions() {
|
||||
* await this.rateLimiter.waitForToken();
|
||||
* // Fetch regions from Linode API
|
||||
* }
|
||||
*
|
||||
* normalizeRegion(raw: RawRegion): RegionInput {
|
||||
* // Transform Linode region data to standard format
|
||||
* }
|
||||
* }
|
||||
* Note: CloudConnector base class has been deprecated.
|
||||
* Each connector now uses Env directly for credentials instead of Vault.
|
||||
* See LinodeConnector, VultrConnector, AWSConnector for current implementation patterns.
|
||||
*/
|
||||
export abstract class CloudConnector {
|
||||
/**
|
||||
* Provider identifier (e.g., 'linode', 'vultr', 'aws')
|
||||
* Must be implemented by subclass
|
||||
*/
|
||||
abstract provider: string;
|
||||
|
||||
/**
|
||||
* Cached credentials from Vault
|
||||
* Populated after calling authenticate()
|
||||
*/
|
||||
protected credentials: VaultCredentials | null = null;
|
||||
|
||||
/**
|
||||
* Rate limiter for API requests
|
||||
* Configured with provider-specific limits
|
||||
*/
|
||||
protected rateLimiter: RateLimiter;
|
||||
|
||||
/**
|
||||
* Create a new cloud connector
|
||||
*
|
||||
* @param vault - VaultClient instance for credential management
|
||||
*/
|
||||
constructor(protected vault: VaultClient) {
|
||||
// Default rate limiter: 10 requests, refill 2 per second
|
||||
this.rateLimiter = new RateLimiter(10, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with provider using Vault credentials
|
||||
* Fetches and caches credentials for API calls
|
||||
*
|
||||
* @throws ConnectorError if authentication fails
|
||||
*/
|
||||
async authenticate(): Promise<void> {
|
||||
try {
|
||||
logger.info('[CloudConnector] Authenticating', { provider: this.provider });
|
||||
|
||||
this.credentials = await this.vault.getCredentials(this.provider);
|
||||
|
||||
if (!this.credentials || !this.credentials.api_token) {
|
||||
throw new ConnectorError(
|
||||
this.provider,
|
||||
'authenticate',
|
||||
undefined,
|
||||
'Invalid credentials received from Vault'
|
||||
);
|
||||
}
|
||||
|
||||
logger.info('[CloudConnector] Authentication successful', { provider: this.provider });
|
||||
} catch (error) {
|
||||
logger.error('[CloudConnector] Authentication failed', { provider: this.provider, error });
|
||||
|
||||
throw new ConnectorError(
|
||||
this.provider,
|
||||
'authenticate',
|
||||
undefined,
|
||||
`Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch raw region data from provider API
|
||||
* Must be implemented by subclass
|
||||
*
|
||||
* @returns Array of raw region objects from provider API
|
||||
* @throws ConnectorError on API failure
|
||||
*/
|
||||
abstract fetchRegions(): Promise<RawRegion[]>;
|
||||
|
||||
/**
|
||||
* Fetch raw instance type data from provider API
|
||||
* Must be implemented by subclass
|
||||
*
|
||||
* @returns Array of raw instance type objects from provider API
|
||||
* @throws ConnectorError on API failure
|
||||
*/
|
||||
abstract fetchInstanceTypes(): Promise<RawInstanceType[]>;
|
||||
|
||||
/**
|
||||
* Normalize raw region data to standard format
|
||||
* Transforms provider-specific region structure to RegionInput
|
||||
* Must be implemented by subclass
|
||||
*
|
||||
* @param raw - Raw region data from provider API
|
||||
* @returns Normalized region data ready for database insertion
|
||||
*/
|
||||
abstract normalizeRegion(raw: RawRegion): RegionInput;
|
||||
|
||||
/**
|
||||
* Normalize raw instance type data to standard format
|
||||
* Transforms provider-specific instance structure to InstanceTypeInput
|
||||
* Must be implemented by subclass
|
||||
*
|
||||
* @param raw - Raw instance type data from provider API
|
||||
* @returns Normalized instance type data ready for database insertion
|
||||
*/
|
||||
abstract normalizeInstance(raw: RawInstanceType): InstanceTypeInput;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user