fix(platform): close remaining audit findings from security review

Harden preview/deploy flows, OTP generation, zip extraction, and multi-replica billing races; document full remediation status in AUDIT-STATUS.fa.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-07-03 12:30:22 +03:30
parent 6d9cd89cc5
commit 8163665c86
8 changed files with 294 additions and 87 deletions
+28 -41
View File
@@ -313,7 +313,9 @@ export class KubernetesService implements OnModuleInit {
const domain = this.configService.get('platform.domain');
const previewRootDomain = this.configService.get<string>('platform.previewRootDomain') || domain;
const namespacePrefix = userIdSlug(app.userId);
const previewHost = previewNumber && !app.customDomain ? `${namespacePrefix}-${previewNumber}.${previewRootDomain}` : '';
const previewHost = previewNumber && !this.hasVerifiedCustomDomain(app)
? `${namespacePrefix}-${previewNumber}.${previewRootDomain}`
: '';
const pullRegistryUrl = this.registryService.getRegistryUrl();
const isWordPress = app.runtime === AppRuntime.WORDPRESS;
const hasDb = app.databaseType !== DatabaseType.NONE;
@@ -2347,6 +2349,8 @@ export class KubernetesService implements OnModuleInit {
}
}
await this.deleteTemporaryAccessServicesForApp(app);
return snapshot;
}
@@ -2720,6 +2724,11 @@ export class KubernetesService implements OnModuleInit {
return userNamespace(userId);
}
/** Preview URL stays available until the custom domain is verified (not merely requested). */
private hasVerifiedCustomDomain(app: Application): boolean {
return !!(app.customDomain && app.customDomainStatus === CustomDomainStatus.VERIFIED);
}
private getClusterHostIp(kc: k8s.KubeConfig): string {
const clusterServer = kc.getCurrentCluster()?.server || '';
try {
@@ -2955,7 +2964,7 @@ export class KubernetesService implements OnModuleInit {
/**
* Get preview info for a deployed application.
* Patches the service to NodePort if needed, and returns the access URL.
* Returns ingress URL when available; only reads an existing NodePort (never patches ClusterIP).
*/
async getPreviewInfo(
app: Application,
@@ -2966,48 +2975,11 @@ export class KubernetesService implements OnModuleInit {
host: string;
ingressUrl?: string;
}> {
const { coreApi, networkingApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId);
const { coreApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId);
const namespace = this.getUserNamespace(app.userId);
const domain = this.configService.get('platform.domain');
const hostIp = this.getClusterHostIp(kc);
// Read current service
let nodePort = 0;
try {
const svcResponse = await coreApi.readNamespacedService({
name: app.name,
namespace,
});
const svc = svcResponse;
if (svc.spec?.type === 'NodePort') {
// Already NodePort, read the assigned port
nodePort = svc.spec.ports?.[0]?.nodePort || 0;
} else {
// Patch ClusterIP → NodePort so we can access from outside
const patchBody = {
spec: {
type: 'NodePort',
ports: [
{
port: 80,
targetPort: app.port,
protocol: 'TCP',
},
],
},
};
const patchedResponse = await coreApi.patchNamespacedService({ name: app.name, namespace, body: patchBody }, k8s.setHeaderOptions('Content-Type', 'application/strategic-merge-patch+json'));
nodePort = patchedResponse.spec?.ports?.[0]?.nodePort || 0;
this.logger.log(`Patched service ${app.name} to NodePort: ${nodePort}`);
}
} catch (e: any) {
this.logger.warn(`Failed to get/patch service for ${app.name}: ${e.message}`);
throw new Error(`Service not found for "${app.name}". Make sure the app is deployed.`);
}
// Build ingress URL (main / custom domain / preview host)
const subdomain = app.subdomain || app.name;
const verifiedCustomDomain = app.customDomain && app.customDomainStatus === CustomDomainStatus.VERIFIED ? app.customDomain : null;
const previewRootDomain = this.configService.get<string>('platform.previewRootDomain') || domain;
@@ -3020,8 +2992,23 @@ export class KubernetesService implements OnModuleInit {
ingressUrl = `https://${namespacePrefix}-${previewNumber}.${previewRootDomain}`;
}
let nodePort = 0;
try {
const svcResponse = await coreApi.readNamespacedService({
name: app.name,
namespace,
});
if (svcResponse.spec?.type === 'NodePort') {
nodePort = svcResponse.spec.ports?.[0]?.nodePort || 0;
}
} catch (e: any) {
this.logger.warn(`Failed to read service for ${app.name}: ${e.message}`);
}
const url = ingressUrl || (nodePort > 0 ? `http://${hostIp}:${nodePort}` : '');
return {
url: `http://${hostIp}:${nodePort}`,
url,
nodePort,
host: hostIp,
ingressUrl,