Please do not open a public issue for security reports.
Report privately through GitHub:
Report a vulnerability (Security → Advisories → Report a vulnerability)
If you cannot use GitHub advisories, email [email protected] with SECURITY in the
subject.
Helpful to include, as far as you have it:
- The package and version (
CodoMetis.TypeKit,.Analyzers,.Generators,.EntityFrameworkCore,.AspNetCore) - The value object declaration involved, and whether the value came from application code or from parsed input (JSON, route, query string, database)
- A minimal reproduction
- What you expected instead
- Why you consider it security-relevant, i.e. the impact you have in mind
This is a single-maintainer, non-commercial open-source project. There is no SLA, and the following is a good-faith intention rather than a guarantee:
| Acknowledgement | within 7 days |
| Initial assessment | within 30 days |
| Fix or documented decision not to fix | best effort, tracked in the advisory |
Coordinated disclosure. The intent is to publish a fix and a GitHub Security Advisory together, and to keep the report private until then. Ninety days from acknowledgement is the default ceiling for going public, whatever the fix status. Reporters are credited unless they ask not to be.
Security fixes land on the latest minor version of the latest major version, 1.x at present. The five packages share one version, so a fix in any of them is a release of all five.
This is a single-maintainer project, and that is the honest continuity risk. The signal would be unambiguous: the repository archived, the packages marked deprecated on nuget.org, and a note here. Published versions stay on nuget.org regardless. The code is MIT-licensed and the release path needs nothing but this repository, so forking is the intended continuity mechanism, not a fallback.
There is one publishing path, .github/workflows/release.yml, run by pushing a v* tag:
- No stored credential. Packages are pushed through nuget.org Trusted Publishing: GitHub signs a short-lived token for that workflow in this repository, and nuget.org exchanges it for a key valid for one hour. No API key exists in the repository or its secrets.
- Tested before anyone approves. The full test suite and a smoke test that installs the packed packages into throwaway projects run unattended. Only then does the push wait for a maintainer's approval, and the job that can mint the token can neither build nor write to the repository.
- What was tested is what ships. The push downloads the packages the verify job built; nothing is rebuilt after the tests.
- An SBOM per package. Each GitHub release carries a CycloneDX SBOM for every package, listing what a consumer of that package actually receives. Build-only references are excluded: the analyzer's Roslyn compiler packages, which run inside your own compiler, and the packages the SDK adds to this repository's own build, such as the trimming tasks. A test reads the restore output and keeps that list complete.
- Source and symbols. Every library package has Source Link and a symbol package, so the code you step into is the code that was built. The analyzer package ships only the analyzer, which runs inside your compiler.
- No license needed. Metalama builds without a license; CI and the release run without one.
Part of this package family runs inside your build, and the rest in your request path.
- Build time.
CodoMetis.TypeKit.Generatorsruns Metalama aspects, andCodoMetis.TypeKit.Analyzers(which arrives withCodoMetis.TypeKit) runs Roslyn analyzers, inside your compiler process. Referencing.GeneratorsmakesMetalama.Frameworkflow into every project that references yours.CodoMetis.TypeKitalone brings no Metalama. - Validation.
IValidatedValuetypes are often used as the boundary check on untrusted input. The generatedTryFrom, JSON converter,IParsableandTypeConvertermust all apply the sameCreaterules. - Materialisation. Two paths rebuild value objects without validation, by design, and
trust that the application wrote what they read: the EF Core satellite for columns, and
StoredJsonConverterFactoryfor JSON the application stored itself, where it is registered on that store'sJsonSerializerOptionsand nowhere else. The EF satellite's read path isValueObjectConverter<,>.Materialize, public because EF's compiled model calls it from code it generates in the application; the factory reaches the generated converter's materializing mode throughIStoredJsonConverterSource, which hands it out to the factory only. The analyzer reports a call toMaterializeanywhere else as an error (CMTK0004).
- A generated entry point that bypasses validation. A JSON converter,
Parse,TryParse, model binder, type converter ordefaultpath that produces anIValidatedValueinstanceCreatewould have refused. - A generated refusal that quotes the input: a message, or an inner exception, of a generated
Parse, type converter or JSON read that contains the text it refused. - An
OptionorResultthat reads as success when it is not, such as adefaultinstance that passes the analyzer, a serialization path that writes one as{}or reads one back asdefaultwithout raising, or any path that exposes the value of aNoneor an error. - A validation-free path reachable from input. The materializer, the stored-JSON mode of the
generated converter, or anything else that skips
Create, becoming reachable other than through the EF satellite or an explicitly registeredStoredJsonConverterFactory. - A path that behaves differently when trimmed or compiled with Native AOT in a way that skips
Createor reads as success, such as a converter that silently falls back to validation-free behaviour, or a satellite that stops recognising value objects. - An analyzer that silently stops enforcing its rule where it is the only guard against
default(T)of a validated value. - Denial of service through parsing or JSON out of proportion to input length.
- Supply-chain integrity issues in the packages or the release workflow.
- Vulnerabilities in Metalama, EF Core, ASP.NET Core or Roslyn themselves. Report those upstream.
- Rules you wrote in your own
Create. The package guarantees they are applied consistently, not that they are sufficient. - Your value object's own members calling its generated private constructor, as
Createdoes. Code inside the type is trusted to apply its rules; a hand-written constructor is a build error (CMTK1009), so every other way in is generated or goes throughFromorCreate. - Anything that fails loudly: a refused value, a
FormatException, a build error. - A property absent from a JSON document. System.Text.Json calls no converter for it, so it stays
default, which no entry point of this package sees;requiredorRespectRequiredConstructorParametersmakes the serializer refuse it (the ASP.NET Core package's README shows both). - Messages ASP.NET Core or the configuration binder write themselves, such as MVC's "The value '…'
is not valid.", which quote the input for a value object as they do for a
Guid, and theJsonException.PathSystem.Text.Json sets for an unreadable dictionary key, which contains the key. The package's own messages, and the exceptions it throws for text the wrapped type cannot read, never quote the input. - The wrapped type's own members reached through the value object's explicit
IConvertible, such asConvert.ToInt32on a value object over astring, which fail with the string's own message. - Values the application itself wrote and reads back, from a column or from a store whose options
register
StoredJsonConverterFactory: materialisation trusts them by contract. Registering the factory on options that read input is a misuse, not a vulnerability in the package.