Security Advisories (1)
CVE-2026-75870 (2026-08-22)

Punk versions before 0.18 for Perl allow session cookie forgery via an empty default HMAC key when a session is declared without a secret. The session keyword freezes its options onto the application as given: it does not require a secret, warn, or refuse to start when one is absent. The cookie read and the write-back both default that key to the empty string, so a declaration with no secret option, or with an undefined or empty one, signs and verifies with a zero-length HMAC-SHA256 key. An attacker who knows the cookie format can then mint one offline carrying any contents the session holds, such as a user identifier or a role. Nothing marks the misconfiguration at runtime: cookies are well formed and sessions round-trip as expected.

NAME

Punk::Mount::OpenAPI - the api mount: spec-first operations

SYNOPSIS

my $v1  = under '/v1' => 'API::Auth#bearer';
my $api = $v1->api('openapi.json' => {
    controller_ns => 'MyApp::Controller::API',   # the default
    under         => { '/books/manage' => 'API::Auth#admin' },
    security      => { session => 'API::Auth#session' },
});

DESCRIPTION

An api mount compiles an OpenAPI 3.1 document through Open::API and dispatches each operation to the controller method named after its operationId. Everything resolves at to_app:

  • every class under controller_ns (default MyApp::Controller::API) is loaded and operationIds mapped to the one class implementing each - two implementers croak, none croaks (unless stub => 1, which answers 501 per request instead); the handlers option overrides per operationId with a coderef or target string;

  • the spec's security requirements compile to a generated guard per operation from the security => { scheme => checker } map - alternatives are OR, schemes within one are AND, results land in $c->stash->{auth}, failure answers 401 before any validation. A required scheme with no checker croaks at boot;

  • the under option assigns extra guards by spec-path prefix (longest prefix wins), appended after the mount scope's own guards.

Per request the pipeline is: route ($api->match, one C call), guard chain (a 401/redirect costs no body read), max_body_size check (413), $api->validate_request (one C call; failures answer 400 with the same {errors=>[...]} shape as Open::API::Plack), then the controller with $c->openapi holding the typed parameters and $c->param reading them first.

METHODS

The mount is an internal object - the api keyword returns it and the framework drives it. new(spec => ..., opts => ..., prefix => ..., guards => ...) constructs one; compile($app, $resolve) resolves it at boot; dispatch($c, $before, $op_id, $caps) runs one matched operation per request. prefix, api (the compiled Open::API), ops, max_body_size and on_error are read-only accessors onto what compile froze.

OPTIONS

controller_ns, handlers, under, security, max_body_size (default 1048576), on_error (mount-level override), stub.

SEE ALSO

Punk, Open::API, Open::API::Plack.

AUTHOR

LNATION <email@lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION <email@lnation.org>.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)