##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################

"""
Wiring test for the XSS fix in the RDS verify_credentials flow.

A failing AWS STS validation returns an exception string that echoes the
caller-supplied access_key verbatim. The /rds/verify_credentials/
endpoint propagates that string into the JSON `info` field, which the
frontend renders. The fix routes the string through sanitize_external_text
before it crosses into the JSON; this test exercises the view function
with RDS.validate_credentials patched to return an HTML payload, and
asserts the JSON `info` is HTML-escaped (entities) rather than raw markup.

A full HTTP-level integration test would need either real AWS network
access (the IncompleteSignature error is generated by the AWS STS
endpoint, not client-side) or a complete boto3 stub. This wiring test
covers the contract that matters — the response sanitises external text —
without that overhead.
"""

import json
import unittest
from unittest.mock import patch

from pgadmin.utils.route import BaseTestGenerator


XSS_PAYLOAD = '<iframe/src=http://attacker.example/>'
AWS_LIKE_ERROR = (
    "An error occurred (IncompleteSignature) when calling the "
    "GetCallerIdentity operation: Credential must have exactly 5 "
    "slash-delimited elements, e.g. keyid/date/region/service/term, got "
    "'" + XSS_PAYLOAD + "/20260510/us-east-1/sts/aws4_request'"
)


class _SkipServerSetUpMixin:
    """Mixin: skip BaseTestGenerator's Postgres connection — this is a
    pure-Python wiring test."""

    def setUp(self):
        unittest.TestCase.setUp(self)


class TestRdsVerifyCredentialsXssEscaped(
        _SkipServerSetUpMixin, BaseTestGenerator):
    """When AWS STS validation fails with an exception string that quotes
    back attacker-controlled input, the JSON `info` field must be
    HTML-escaped to inert entities — not the raw markup."""

    scenarios = [
        ('iframe payload in access_key is HTML-escaped in info', dict()),
    ]

    def runTest(self):
        from flask import Flask, session
        from pgadmin.misc.cloud import rds as rds_mod

        app = Flask(__name__)
        app.secret_key = 'test'

        request_payload = {
            'cloud': 'aws',
            'secret': {
                'region': 'us-east-1',
                'access_key': XSS_PAYLOAD,
                'secret_access_key': 'any',
                'session_token': 'any',
                'is_valid_cred': False,
            },
        }

        with app.test_request_context(
                '/rds/verify_credentials/',
                method='POST',
                data=json.dumps(request_payload),
                content_type='application/json'):
            session['aws'] = {}
            with patch.object(rds_mod.RDS, 'validate_credentials',
                              return_value=(False, AWS_LIKE_ERROR)):
                # __wrapped__ unwraps pga_login_required so the test can
                # exercise the view directly without a full Flask-Login /
                # MFA stack.
                response = rds_mod.verify_credentials.__wrapped__()

        self.assertEqual(response.status_code, 200)
        body = json.loads(response.data)
        info = body.get('info') or ''

        # The connection itself failed (validation returned False).
        self.assertEqual(body.get('success'), False)

        # The AWS-shaped error must have made it through (wrapping isn't
        # silently dropping the message).
        self.assertIn('IncompleteSignature', info)

        # The raw HTML payload must NOT survive verbatim.
        self.assertNotIn('<iframe', info)
        self.assertNotIn('<script', info)

        # The entity-encoded form must be present.
        self.assertIn('&lt;iframe', info)
        # The single quotes around the access_key in the AWS message must
        # be encoded too — proves quote=True was honoured.
        self.assertIn('&#x27;', info)


class TestRdsVerifyCredentialsHappyPathUnchanged(
        _SkipServerSetUpMixin, BaseTestGenerator):
    """Sanitisation must not alter the success-path 'verified' string."""

    scenarios = [('default', dict())]

    def runTest(self):
        from flask import Flask, session
        from pgadmin.misc.cloud import rds as rds_mod

        app = Flask(__name__)
        app.secret_key = 'test'

        request_payload = {
            'cloud': 'aws',
            'secret': {
                'region': 'us-east-1',
                'access_key': 'AKIAEXAMPLE',
                'secret_access_key': 'any',
                'session_token': 'any',
                'is_valid_cred': True,
            },
        }

        with app.test_request_context(
                '/rds/verify_credentials/',
                method='POST',
                data=json.dumps(request_payload),
                content_type='application/json'):
            session['aws'] = {}
            with patch.object(rds_mod.RDS, 'validate_credentials',
                              return_value=(True, {'Account': '123'})):
                # __wrapped__ unwraps pga_login_required so the test can
                # exercise the view directly without a full Flask-Login /
                # MFA stack.
                response = rds_mod.verify_credentials.__wrapped__()

        body = json.loads(response.data)
        self.assertEqual(body.get('success'), True)
        self.assertEqual(body.get('info'), 'verified')
