File: normalize_query.py

package info (click to toggle)
url-normalize 2.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 268 kB
  • sloc: python: 935; makefile: 16; sh: 8
file content (64 lines) | stat: -rw-r--r-- 1,740 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""URL query normalization."""

from __future__ import annotations

from .param_allowlist import get_allowed_params
from .tools import quote, unquote

QUERY_PARAM_SAFE_CHARS = "~:/?[]@!$'()*+,;"


def process_query_param(param: str) -> str:
    """Process a single query parameter.

    This function normalizes the parameter by quoting reserved characters
    and ensuring the parameter is in the correct format.

    Params:
        param: The query parameter to process.

    Returns:
        str: The normalized query parameter.

    """
    if not param:
        return ""
    return quote(unquote(param), QUERY_PARAM_SAFE_CHARS)


def normalize_query(
    query: str,
    *,  # Force keyword-only arguments
    host: str | None = None,
    filter_params: bool = False,
    param_allowlist: list | dict | None = None,
) -> str:
    """Normalize query while preserving parameter order.

    Params:
        query: URL query string (e.g. 'param1=val1&param2')
        host: Domain for allowlist checks
        filter_params: If True, removes non-allowlisted parameters
        param_allowlist: Optional override for default allowlist

    Returns:
        Normalized query string with original parameter order

    """
    if not query:
        return ""

    processed = []
    for param in query.split("&"):
        if not param:
            continue
        key, _, value = param.partition("=")
        key = process_query_param(key)
        if filter_params:
            allowed_params = get_allowed_params(host, param_allowlist)
            if key not in allowed_params:
                continue
        value = process_query_param(value)
        processed.append(f"{key}={value}" if value else key)

    return "&".join(processed)