# 값 타입의 컴팩트 생성자가 하는 검사 전부 22: public WebPushSubscriptionValue { 23: Objects.requireNonNull(endpoint, "endpoint"); 24: Objects.requireNonNull(p256dh, "p256dh"); 25: Objects.requireNonNull(authSecret, "authSecret"); 26: Objects.requireNonNull(vapidKeyId, "vapidKeyId"); 27: if (!isSecureOrLoopback(endpoint)) { 28: throw new IllegalArgumentException("endpoint must be https outside the loopback interface"); 29: } 30: if (p256dh.length != P256DH_LENGTH) { 31: throw new IllegalArgumentException("p256dh must be an uncompressed P-256 point"); 32: } 33: if (authSecret.length != AUTH_SECRET_LENGTH) { 34: throw new IllegalArgumentException("authSecret must be 16 bytes"); 35: } 36: if (vapidKeyId.isBlank()) { 37: throw new IllegalArgumentException("vapidKeyId"); 38: } # 다섯째 검사의 구현 — 같은 파일 안의 private 메서드 50: private static boolean isSecureOrLoopback(java.net.URI endpoint) { 51: String scheme = endpoint.getScheme() == null ? "" : endpoint.getScheme(); 52: if ("https".equalsIgnoreCase(scheme)) { 53: return true; 54: } 55: String host = 56: endpoint.getHost() == null ? "" : endpoint.getHost().toLowerCase(java.util.Locale.ROOT); 57: return "http".equalsIgnoreCase(scheme) 58: && ("127.0.0.1".equals(host) || "::1".equals(host) || "localhost".equals(host)); 59: } # 그 private 사본이 베낀 원본. 같은 모듈의 공개 함수다. 24: public static URI requireSecureOrLoopback(URI endpoint, String name) { 25: Objects.requireNonNull(endpoint, name); 26: String scheme = 27: endpoint.getScheme() == null ? "" : endpoint.getScheme().toLowerCase(Locale.ROOT); 28: if ("https".equals(scheme)) { 29: return endpoint; 30: } 31: String host = endpoint.getHost() == null ? "" : endpoint.getHost().toLowerCase(Locale.ROOT); 32: if ("http".equals(scheme) && LOOPBACK_HOSTS.contains(host)) { 33: return endpoint; 34: } 35: throw new IllegalArgumentException(name + " must use https outside the loopback interface"); # 같은 파일의 다른 공개 함수가 자기가 누구를 위한 것인지 적는다 47: *

{@link #requireSecureOrLoopback} checks the scheme and nothing else, so any HTTPS URL was 48: * accepted — including {@code https://169.254.169.254/}, the cloud metadata service, and any RFC 49: * 1918 address. Web Push endpoints and webhook targets are supplied by clients, which makes this 50: * a server-side request forgery primitive: the platform will happily fetch an internal address 51: * and, for a webhook, deliver the message body there. # 그 함수가 실제로 하는 검사 65: public static URI requireExternallyRoutable(URI endpoint, String name, boolean allowLoopback) { 66: Objects.requireNonNull(endpoint, name); 67: requireSecureOrLoopback(endpoint, name); 68: if (endpoint.getUserInfo() != null) { 71: throw new IllegalArgumentException(name + " must not carry userinfo"); 72: } 73: String host = endpoint.getHost(); 74: if (host == null || host.isBlank()) { 75: throw new IllegalArgumentException(name + " has no host"); 76: } 77: if (allowLoopback && isLoopback(endpoint)) { 78: return endpoint; 79: } 81: java.net.InetAddress[] resolved; 82: try { 83: resolved = java.net.InetAddress.getAllByName(host); 84: } catch (java.net.UnknownHostException unresolvable) { 85: throw new IllegalArgumentException(name + " does not resolve", unresolvable); 86: } 87: if (resolved.length == 0) { 88: throw new IllegalArgumentException(name + " does not resolve"); 89: } 90: for (java.net.InetAddress address : resolved) { 94: if (isInternal(address)) { 95: throw new IllegalArgumentException( 96: name + " resolves to an address inside the deployment's own network"); 97: } 98: } 99: return endpoint; # 강한 쪽의 프로덕션 호출처 SesProviderProperties.java:27: NotificationEndpoints.requireExternallyRoutable(endpoint, "SES endpoint", true); WebhookSubscription.java:49: NotificationEndpoints.requireExternallyRoutable(target, "webhook target", trusted); # 그중 웹푸시: 0 # 값 타입이 두 공개 함수 중 하나라도 부르는 줄: 0 # 호출처 도달을 고정하려고 만든 테스트가, 무엇을 목록으로 들고 있는가 17: *

{@code EndpointRoutabilityTest} already proves {@code requireExternallyRoutable} rejects the 18: * metadata service, RFC 1918, link-local and the rest. It proved that for months while the function 19: * had no caller: both sites it was written for — a webhook target and an SES endpoint — kept 20: * calling {@code requireSecureOrLoopback}, which reads the scheme and nothing else. A green test on 21: * a control nothing invokes is the shape this repository keeps finding, and testing the helper 22: * again would not have caught it. 37: @DisplayName("a webhook target on the cloud metadata service is refused") 45: @DisplayName("a webhook target inside the deployment's own network is refused") 54: @DisplayName("a webhook target carrying userinfo is refused") 68: @DisplayName("an SES endpoint inside the deployment's own network is refused") 82: @DisplayName("a client-supplied webhook target on the loopback interface is refused") 95: @DisplayName("a client-supplied webhook target on 127.0.0.1 is refused") 106: @DisplayName("loopback stays available, because local and contract profiles address it") # 그 파일에서 WebPush 를 언급하는 줄: 0