App-Netdisco

 view release on metacpan or  search on metacpan

xt/32-swagger-ui-guard.t  view on Meta::CPAN

#!/usr/bin/env perl

use strict;
use warnings;

use Test::More 0.88;
use File::Spec::Functions qw/catfile updir/;
use FindBin;

# This file asserts the CONTENT of the shipped Swagger UI page, not its
# behaviour, and the distinction matters when reading a pass.
#
# The failure it guards is a re-vendor: dropping in a fresh upstream index.html
# reverts every local change below at once and silently, which is exactly how the
# petstore default arrived in the first place. A content assertion catches that.
#
# It cannot prove the page is safe, because it shares its input with the code it
# checks. The behavioural evidence is a browser driven against the page with
# off-host requests blocked and recorded, and it does not live in this suite,
# because the distribution has no working JavaScript test path.

my $page = catfile( $FindBin::Bin, updir(),
    'share', 'public', 'swagger-ui', 'index.html' );

my $html = do {
    open my $fh, '<', $page or die "cannot read $page: $!";
    local $/;
    <$fh>;
};

subtest 'swagger_ui_index__shipped_page__names_no_external_spec_host' => sub {
    unlike $html, qr{https?://[^"'\s]*\bswagger\.io}i,
        'no swagger.io host appears anywhere in the page';
};

subtest 'swagger_ui_index__shipped_page__defaults_to_own_swagger_json' => sub {
    # Two halves, because the default is no longer a literal in the constructor.
    # The page sanitises any ?url= into specUrl and falls back to its own
    # definition; asserting the literal alone would pass on a page that computed
    # the value and then handed the bundle something else.
    #
    # Relative, so it resolves under a path-prefixed deployment and from both
    # /swagger-ui/ and /swagger-ui/index.html.
    like $html, qr{\|\|\s*["']\.\./swagger\.json["']},
        'the fallback spec URL is ../swagger.json';
    like $html, qr{\burl:\s*specUrl\b},
        'the bundle is given the sanitised value, not the raw parameter';
};

subtest 'swagger_ui_index__shipped_page__disables_the_online_validator' => sub {
    # On by default, and it sends the definition URL to a Swagger-hosted service
    # on every page load, which for most deployments publishes an internal
    # hostname. It cannot be caught in local testing: the badge suppresses itself
    # when the URL contains localhost or 127.0.0.1.
    like $html, qr{\bvalidatorUrl:\s*null\b},
        'validatorUrl is null';
};

subtest 'swagger_ui_index__shipped_page__leaves_query_configuration_off' => sub {
    # This is what the whole arrangement rests on from 4.1.3 onward. With
    # queryConfigEnabled the bundle reads ?url= itself, and ?configUrl= and every
    # other key with it, at which point the sanitiser below is decoration: the
    # bundle has already taken the attacker's value. The default is off, so the
    # assertion is that nothing turns it on.
    unlike $html, qr{queryConfigEnabled\s*:\s*true},
        'query configuration is not enabled';
};

subtest 'swagger_ui_index__shipped_page__sanitises_the_query_before_construction' => sub {
    # Ordering is the whole mechanism, not an incidental detail: the value has to
    # be judged before it reaches the constructor, since the bundle fetches
    # whatever url it is handed. Anchored on the CALL rather than the bare name,
    # because the page's own comments discuss the sanitiser and an earlier
    # version of this test matched a comment, so deleting the real call left it
    # passing.
    my $sanitise  = $html =~ m{specUrl\s*=\s*sameOriginSpecUrl\(} ? $-[0] : -1;
    my $construct = index $html, 'SwaggerUIBundle({';

    cmp_ok $sanitise,  '>', -1, 'the parameter is passed through the sanitiser';



( run in 0.776 second using v1.01-cache-2.11-cpan-800906f7e73 )