{"record":{"id":"b5936cbf9d6e761d","repo":"microsoft/aspire","slug":"secret-store-storename-references-a-manifest-at-manifestpath-b5936c","errorCode":null,"errorMessage":"Secret store '{storeName}' references a manifest at '{manifestPath}' that contains top-level plaintext Kubernetes Secret fields ('data' or 'stringData'). Seal those values under spec.encryptedData instead.","messagePattern":"Secret store '(.+?)' references a manifest at '(.+?)' that contains top-level plaintext Kubernetes Secret fields \\('data' or 'stringData'\\)\\. Seal those values under spec\\.encryptedData instead\\.","errorType":"validation","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"src/Aspire.Hosting.Radius/Secrets/SealedSecretManifest.cs","lineNumber":188,"sourceCode":"                $\"Bitnami SealedSecret (expected 'kind: SealedSecret' and 'apiVersion: {SupportedApiVersion}'; found \" +\n                $\"kind '{kind ?? \"<none>\"}', apiVersion '{apiVersion ?? \"<none>\"}'). Diagnostic: ASPIRERADIUS044.\");\n        }\n\n        if (TryGetNode(root, \"data\", out _) || TryGetNode(root, \"stringData\", out _))\n        {\n            throw CreateInvalidManifestException(\n                storeName,\n                manifestPath,\n                \"contains top-level plaintext Kubernetes Secret fields ('data' or 'stringData'). Seal those values under spec.encryptedData instead.\");\n        }\n\n        if (TryGetNode(root, \"spec\", out var specNode) &&\n            specNode is YamlMappingNode spec &&\n            TryGetNode(spec, \"template\", out var templateNode) &&\n            templateNode is YamlMappingNode template &&\n            (ContainsPlaintextTemplateData(template, \"data\") || ContainsPlaintextTemplateData(template, \"stringData\")))\n        {\n            throw CreateInvalidManifestException(\n                storeName,\n                manifestPath,\n                \"contains plaintext-capable spec.template.data or spec.template.stringData values. Seal secret material under spec.encryptedData instead.\");\n        }\n\n        // A `kubectl.kubernetes.io/last-applied-configuration` annotation records the full JSON of a\n        // previously-applied object. Unlike spec.encryptedData it is NOT encrypted, so a plaintext\n        // Secret embedded there (top-level metadata, or the templated Secret's metadata) would be\n        // copied verbatim into publish artifacts and re-applied — defeating sealing. Reject it.\n        RejectPlaintextLastAppliedAnnotation(storeName, manifestPath, root);\n\n        // Runs after the leak gates above so a manifest that both leaks cleartext and has a malformed\n        // payload still reports the more specific ASPIRERADIUS063/plaintext diagnostic.\n        ValidateEncryptedData(storeName, manifestPath, root);\n\n        if (!TryGetNode(root, \"metadata\", out var metadataNode) || metadataNode is not YamlMappingNode metadata)\n        {\n            throw CreateInvalidManifestException(","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Radius/Secrets/SealedSecretManifest.cs#L170-L206","documentation":"The manifest is a valid SealedSecret, but plaintext-capable Secret fields were found: either top-level 'data'/'stringData', or plaintext-capable 'spec.template.data'/'spec.template.stringData' entries. Including cleartext in a SealedSecret defeats the sealing guarantee, so the loader rejects it to prevent leaking secrets into the published manifest.","triggerScenarios":"ReadMetadataFromRoot finds a 'data' or 'stringData' node at the root, or a spec.template mapping where ContainsPlaintextTemplateData(template, \"data\"|\"stringData\") is true.","commonSituations":"Copy-pasting a plain Secret's data block into the sealed manifest; kubeseal versions/workflows that emit template.data; manually merging a Secret template with encryptedData and leaving plaintext keys; partial sealing where some values were left unencrypted.","solutions":["Remove top-level 'data'/'stringData' from the manifest entirely.","Seal all secret values into 'spec.encryptedData' with kubeseal instead of template.data/stringData.","Regenerate the manifest from the original Secret using kubeseal so no plaintext fields survive.","Review spec.template — keep only non-secret metadata (labels, annotations, type), no data fields."],"exampleFix":"# before: plaintext left in manifest\napiVersion: bitnami.com/v1alpha1\nkind: SealedSecret\nspec:\n  template:\n    data:\n      password: plaintext\n  encryptedData:\n    password: AgB...\n# after\napiVersion: bitnami.com/v1alpha1\nkind: SealedSecret\nspec:\n  encryptedData:\n    password: AgB...","handlingStrategy":"validation","validationCode":"// Fail CI on plaintext leak\nbool leaks = root[\"data\"] != null || root[\"stringData\"] != null\n    || root[\"spec\"]?[\"template\"]?[\"data\"] != null\n    || root[\"spec\"]?[\"template\"]?[\"stringData\"] != null;\nif (leaks) throw new InvalidOperationException(\"Manifest contains plaintext secret fields\");","typeGuard":null,"tryCatchPattern":"catch (Exception ex) when (ex.Message.Contains(\"plaintext\"))\n{\n    // remove data/stringData fields and re-seal values into spec.encryptedData\n}","preventionTips":["Never paste a plain Secret's data block into a sealed manifest.","Keep spec.template limited to non-secret metadata.","Scan manifests for 'data:'/'stringData:' in CI as a leak gate.","Always generate sealed manifests via kubeseal, not by hand."],"tags":["sealed-secrets","security","secret-leak"],"backgroundTag":"schema-validation-failed","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}