Redact.java

package com.shengzhiai.yugu.internal;

import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

/** Redaction for logs: secrets, signatures and tokens never reach a log line. */
public final class Redact {
    private Redact() {
    }

    /**
     * @param appKey app key
     * @return the first 4 characters followed by {@code ***}; {@code ***} for short or null keys
     */
    public static String appKey(String appKey) {
        if (appKey == null || appKey.length() <= 4) {
            return "***";
        }
        return appKey.substring(0, 4) + "***";
    }

    /**
     * Redacts a header value by name.
     *
     * @param name  header name
     * @param value header value
     * @return safe value
     */
    public static String header(String name, String value) {
        String n = name.toLowerCase(Locale.ROOT);
        switch (n) {
            case "x-signature":
                return "***";
            case "authorization":
                return value != null && value.regionMatches(true, 0, "Bearer ", 0, 7) ? "Bearer ***" : "***";
            case "x-app-key":
                return appKey(value);
            default:
                return value;
        }
    }

    /**
     * @param headers headers
     * @return redacted copy, sorted by name
     */
    public static Map<String, String> headers(Map<String, String> headers) {
        Map<String, String> out = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        for (Map.Entry<String, String> e : headers.entrySet()) {
            out.put(e.getKey(), header(e.getKey(), e.getValue()));
        }
        return out;
    }

    /**
     * Redacts the query of a URL: {@code signature} and {@code token} become {@code ***}, {@code appKey}
     * keeps its first 4 characters.
     *
     * @param url URL
     * @return safe URL
     */
    public static String url(String url) {
        int q = url.indexOf('?');
        if (q < 0) {
            return url;
        }
        StringBuilder sb = new StringBuilder(url.substring(0, q + 1));
        String[] parts = url.substring(q + 1).split("&");
        for (int i = 0; i < parts.length; i++) {
            String p = parts[i];
            int eq = p.indexOf('=');
            String k = eq < 0 ? p : p.substring(0, eq);
            String v = eq < 0 ? "" : p.substring(eq + 1);
            if (i > 0) {
                sb.append('&');
            }
            sb.append(k).append('=');
            if ("signature".equals(k) || "token".equals(k)) {
                sb.append("***");
            } else if ("appKey".equals(k)) {
                sb.append(appKey(PercentEncoding.decode(v)));
            } else {
                sb.append(v);
            }
        }
        return sb.toString();
    }
}