Amon2-Plugin-Web-CpanelJSON

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

cpanfile
lib/Amon2/Plugin/Web/CpanelJSON.pm
minil.toml
t/00_compile.t
t/01_render_json.t
t/10-config/json.t
t/10-config/json_escape_filter.t
t/10-config/name.t
t/10-config/secure_headers.t
t/10-config/status_code_field.t
t/10-config/unbless_object.t
t/20-compatibility/007_json.t
t/20-compatibility/007_json_default_encoding.t
t/20-compatibility/007_json_hijacking.t
t/20-compatibility/007_json_keysort.t
t/20-compatibility/007_json_x_api_status.t
t/30_integration/JSON-UnblessObject/unbless_object.t
t/99_synopsis.t
META.yml
MANIFEST

README.md  view on Meta::CPAN

    ```perl
    '+' => '\\u002b',
    '<' => '\\u003c',
    '>' => '\\u003e',
    ```

- name

    Name of method. Default: 'render\_json'

- unbless\_object

    Default: undef

    This option is preprocessing coderef encoding an blessed object in JSON.
    For example, the code using [Object::UnblessWithJSONSpec](https://metacpan.org/pod/Object%3A%3AUnblessWithJSONSpec) is as follows:

    ```perl
    use Object::UnblessWithJSONSpec ();

    __PACKAGE__->load_plugins(
        'Web::CpanelJSON' => {
            unbless_object => \&Object::UnblessWithJSONSpec::unbless_with_json_spec,
        }
    );

    ...

    package Some::Object {
        use Mouse;

        has message => (
            is => 'ro',

lib/Amon2/Plugin/Web/CpanelJSON.pm  view on Meta::CPAN

package Amon2::Plugin::Web::CpanelJSON;
use strict;
use warnings;

use Amon2::Util ();
use Cpanel::JSON::XS ();
use Scalar::Util qw(blessed);
use HTTP::SecureHeaders;

our $VERSION = "0.01";

my %DEFAULT_CONFIG = (
    name => 'render_json',

    # for security
    # refs https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers
    secure_headers => {

lib/Amon2/Plugin/Web/CpanelJSON.pm  view on Meta::CPAN

        '<' => '\\u003c', # do not eval as HTML
        '>' => '\\u003e', # ditto.
    },

    # JSON config
    json => {
        ascii => !!1, # for security
    },

    # for convenience
    unbless_object    => undef,
    status_code_field => undef,

    # for compatibility options
    defence_json_hijacking_for_legacy_browser => !!0,
);


sub init {
    my ($class, $c, $conf) = @_;

lib/Amon2/Plugin/Web/CpanelJSON.pm  view on Meta::CPAN

    my $escape_target = '';
    for my $key (keys %{$escape_filter}) {
        if ($escape_filter->{$key}) {
            $escape_target .= $key
        }
    }

    return sub {
        my ($data, $spec) = @_;

        if (my $unbless_object = $conf->{unbless_object}) {
            if (blessed($data)) {
                $data = $unbless_object->($data, $spec);
            }
        }

        my $output = $json->encode($data, $spec);

        if ($escape_target && $escape_filter) {
            $output =~ s!([$escape_target])!$escape_filter->{$1}!g;
        }

        return $output;

lib/Amon2/Plugin/Web/CpanelJSON.pm  view on Meta::CPAN

Escapes JSON to prevent XSS. Default is as follows:

    '+' => '\\u002b',
    '<' => '\\u003c',
    '>' => '\\u003e',

=item name

Name of method. Default: 'render_json'

=item unbless_object

Default: undef

This option is preprocessing coderef encoding an blessed object in JSON.
For example, the code using L<Object::UnblessWithJSONSpec> is as follows:

    use Object::UnblessWithJSONSpec ();

    __PACKAGE__->load_plugins(
        'Web::CpanelJSON' => {
            unbless_object => \&Object::UnblessWithJSONSpec::unbless_with_json_spec,
        }
    );

    ...

    package Some::Object {
        use Mouse;

        has message => (
            is => 'ro',

t/10-config/unbless_object.t  view on Meta::CPAN

{
    package MyApp::Web::Default;
    use parent qw(Amon2 Amon2::Web);
    __PACKAGE__->load_plugins(
        'Web::CpanelJSON',
    );
    sub encoding { 'utf-8' }
}

{
    package MyApp::Web::UnblessObject;
    use parent qw(Amon2 Amon2::Web);
    __PACKAGE__->load_plugins(
        'Web::CpanelJSON', {
            unbless_object => sub {
                my ($object, $spec) = @_;
                {
                    hello => $object->hello,
                }
            },
        }
    );
    sub encoding { 'utf-8' }
}

{
    package Foo;
    sub new { bless {}, $_[0] }
    sub hello { 'HELLO!' }
}

subtest 'unblessed hashref' => sub {
    my $src = {hello => 'world'};
    my $c = MyApp::Web::Default->new(request => Amon2::Web::Request->new({}));
    my $res = $c->render_json($src);
    is $res->code, 200;
    is $res->content, '{"hello":"world"}';
    is_deeply decode_json($res->content), $src;
};

subtest 'blessed object and no spec' => sub {
    my $src = Foo->new;

    my $c = MyApp::Web::Default->new(request => Amon2::Web::Request->new({}));
    eval { $c->render_json($src) };
    like $@, qr/encountered object/;
};

subtest 'blessed object and spec' => sub {
    my $src = Foo->new;
    my $spec = { hello => JSON_TYPE_STRING };

    my $c = MyApp::Web::Default->new(request => Amon2::Web::Request->new({}));
    eval { $c->render_json($src, $spec) };
    like $@, qr/encountered object/;
};

subtest 'with unbless_object' => sub {
    my $src = Foo->new;
    my $spec = { hello => JSON_TYPE_STRING };

    my $c = MyApp::Web::UnblessObject->new(request => Amon2::Web::Request->new({}));
    my $res = $c->render_json($src, $spec);
    is $res->code, 200;
    is $res->content, '{"hello":"HELLO!"}';
    is_deeply decode_json($res->content), { hello => 'HELLO!' };
};

subtest 'unblessed hashref and spec' => sub {
    my $src = {hello => 'world'};
    my $spec = { hello => JSON_TYPE_STRING };

    my $c = MyApp::Web::UnblessObject->new(request => Amon2::Web::Request->new({}));
    my $res = $c->render_json($src, $spec);
    is $res->code, 200;
    is $res->content, '{"hello":"world"}';
    is_deeply decode_json($res->content), $src;
};


done_testing;

t/30_integration/JSON-UnblessObject/unbless_object.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use Test::Requires 'JSON::UnblessObject';

use Cpanel::JSON::XS qw(decode_json);
use Cpanel::JSON::XS::Type;

use JSON::UnblessObject;

{
    package MyApp::Web;
    use parent qw(Amon2 Amon2::Web);
    __PACKAGE__->load_plugins(
        'Web::CpanelJSON', {
            json => {
                canonical => 1,
            },
            unbless_object => \&JSON::UnblessObject::unbless_object,
        }
    );
    sub encoding { 'utf-8' }
}

{
    package Foo;
    sub new { bless {}, $_[0] }
    sub hello { 'HELLO!' }
    sub world { 'WORLD!' }
}


my $src = Foo->new;

my $c = MyApp::Web->new(request => Amon2::Web::Request->new({}));

subtest 'empty spec' => sub {



( run in 1.975 second using v1.01-cache-2.11-cpan-b32c08c6d1a )