Conversation
Document critical gotchas discovered when running integration tests against a local k3d cluster: - CoreDNS fix for host.k3d.internal DNS resolution from pods (S3/MinIO pre-signed URLs are unreachable from pods without this override) - Anaconda Python version conflict with deploy.py - Disk pressure thresholds causing pod scheduling failures - CLI venv setup: keyrings.alt installation and PATH configuration - Updated step-by-step instructions with correct Helm deploy flags - Added troubleshooting section for common failure modes Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
…eration This is a proof of concept demonstrating CDK8s (TypeScript) as a replacement for Helm chart templating. The CDK8s implementation generates functionally identical Kubernetes manifests, validated by passing 92/98 integration tests (the 6 failures are pre-existing issues unrelated to the migration). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
This PR may be related to: #5952 (Alternatives to Helm) |
| } | ||
| current = current[parts[i]]; | ||
| } | ||
| current[parts[parts.length - 1]] = value; |
Check warning
Code scanning / CodeQL
Prototype-polluting function Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 month ago
In general, prototype pollution in deep-assignment functions like setNestedValue is fixed by blocking dangerous property names (__proto__, constructor, prototype) in the property chain, or by ensuring that assignments never target an object’s prototype (for example, by using Object.create(null) so there is no prototype). The minimal, behavior-preserving change here is to keep using plain objects but reject or ignore path segments that could pollute prototypes.
The best fix for this code is to add a guard that detects and rejects dangerous keys before they are used to index into current. Specifically:
- Define a small helper, e.g.
isSafeKey(key: string): boolean, that returnsfalsefor"__proto__","constructor", and"prototype". - In
setNestedValue, before using anyparts[i]as a property name (both when creating intermediate objects and when assigning the final value), checkisSafeKey. If any segment is unsafe, we should skip the assignment altogether to avoid creating any prototype-polluting structures. That preserves existing behavior for all normal keys and just ignores malicious or invalid paths. - All changes are confined to
kubernetes/cdk8s/src/values.ts, within the shown snippet: we add the helper function nearsetNestedValueand adjust the loop insetNestedValueto call it. No imports are needed; we only use basic string comparisons.
This approach avoids changing how normal configuration keys are handled and adds a straightforward, well-known protection against prototype pollution.
| @@ -323,16 +323,32 @@ | ||
| return value; | ||
| } | ||
|
|
||
| function isSafeKey(key: string): boolean { | ||
| // Prevent prototype pollution by disallowing dangerous keys | ||
| return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; | ||
| } | ||
|
|
||
| function setNestedValue(obj: any, path: string, value: any): void { | ||
| const parts = path.split('.'); | ||
| let current = obj; | ||
| // Build intermediate objects, guarding against prototype-polluting keys | ||
| for (let i = 0; i < parts.length - 1; i++) { | ||
| if (!(parts[i] in current) || typeof current[parts[i]] !== 'object') { | ||
| current[parts[i]] = {}; | ||
| const part = parts[i]; | ||
| if (!isSafeKey(part)) { | ||
| // Unsafe path segment; do not perform this assignment | ||
| return; | ||
| } | ||
| current = current[parts[i]]; | ||
| if (!(part in current) || typeof current[part] !== 'object') { | ||
| current[part] = {}; | ||
| } | ||
| current = current[part]; | ||
| } | ||
| current[parts[parts.length - 1]] = value; | ||
| const lastKey = parts[parts.length - 1]; | ||
| if (!isSafeKey(lastKey)) { | ||
| // Unsafe final key; do not perform this assignment | ||
| return; | ||
| } | ||
| current[lastKey] = value; | ||
| } | ||
|
|
||
| export function deepMerge(target: any, source: any): any { |
The dev server test workflow runs deploy.py config which now uses CDK8s instead of Helm for manifest generation. Add Node.js setup and CDK8s npm ci before config generation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete all 46 Helm template files from kubernetes/loculus/templates/ - Delete Chart.yaml, Chart.lock, CONTRIBUTING.md - Delete helm-schema-lint.yaml workflow - Remove helm lint from pre-commit config - Remove setup-helm from website dev test workflow (not needed for config gen) - Rename deploy.py subcommand: helm -> deploy - Rename HELM_CHART_DIR -> VALUES_DIR in deploy.py - Update all references in workflows, scripts, READMEs, and AGENTS.md Values files (values.yaml, values_e2e_and_dev.yaml, values_preview_server.yaml, values.schema.json) are retained as they are consumed by CDK8s. Helm itself is still installed in CI for the secret-generator dependency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…efault' Replace hardcoded `const ns = 'default'` in ingress.ts and lapis.ts with the actual deployment namespace. Add --namespace/-n CLI flag to main.ts and thread it through LoculusChart -> values.releaseNamespace. This matches Helm's $.Release.Namespace behavior so deployments to non-default namespaces generate correct Traefik middleware references. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…construct Export commonMetadataFields() from config-generation.ts and import it in silo.ts instead of duplicating 158 lines. No circular dependency exists despite the previous comment. Reduces silo.ts from 360 to 200 lines. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
This is a proof of concept demonstrating the use of CDK8s (Cloud Development Kit for Kubernetes) as a replacement for Helm chart templating in Loculus.
CDK8s lets us define Kubernetes manifests in TypeScript instead of Go templates, giving us type safety, IDE support, and the full power of a programming language for configuration logic. The generated YAML is functionally identical to what Helm produces.
What's included
kubernetes/cdk8s/— Full CDK8s implementation (~2,500 lines of TypeScript) with constructs for all Loculus components: backend, website, keycloak, database, LAPIS/SILO, preprocessing, ingest, ENA submission, MinIO, ingress, secrets, and docsdeploy.py— Updated to synthesize manifests vianpx ts-nodeinstead ofhelm template, and apply viakubectl apply --server-side.github/workflows/integration-tests.yml— CI updated to install Node.js and CDK8s dependencies before the deploy stepValidation
valuekeys vs Helm emittingvalue:null, which is semantically equivalent for Kubernetes)"Only singleReference organisms are supported currently"bug, unrelated to deployment methodNot yet included
kubernetes/loculus/templates/)helm-schema-lint.yaml)🤖 Generated with Claude Code
🚀 Preview: Add
previewlabel to enable