Repository: helidon-io/helidon (java)

Issue:
CORS "Internal exception in JAX-RS processing" with "null" Origin
## Environment Details
* Helidon Version: 4.0.6
* Helidon MP
* JDK version: Amazon Coretto 21.0.2 Hotspot
* OS: Windows 11

----------

I justed enabled CORS in the quickstart maven example ( add `helidon-microprofile-cors` dependency and set `cors.enabled=true`) ,then I opened a new Firefox window, opened a console and typed `fetch("http://localhost:8080/"`. This lead to a CORS security warning `Content-Security-Policy: .... ("connect-src")`. 

I wanted to take a look at the headers hence I copied the network request via Firefox into a curl command and executed it:
```
curl 'http://172.18.32.1:8080/' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0' -H 'Accept: */*' -H 'Accept-Language: de,en-US;q=0.7,en;q=0.3' -H 'Accept-Encoding: gzip, deflate, br' -H 'Origin: null'
```

This returns a `Internal exception in JAX-RS processing` and a lengthy exception in the server logs ending with:
```
Caused by: java.lang.StringIndexOutOfBoundsException: Range [0, -1) out of bounds for length 4
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
	at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4865)
	at java.base/java.lang.String.substring(String.java:2834)
	at io.helidon.cors.CorsSupportHelper.originLocation(CorsSupportHelper.java:425)
	at io.helidon.cors.CorsSupportHelper$RequestTypeInfo.create(CorsSupportHelper.java:390)
	at io.helidon.cors.CorsSupportHelper.requestType(CorsSupportHelper.java:375)
```

I hence would argue that the CorsSupportHelper::originLocation should be more robust to not cause these exceptions.


Pull request description:
Properly handle opaque origin ("null") in CORS processing
### Description
Resolves #8497 

If a browser (or other client) that can participate in CORS detects an untrusted origin, it can/might/must send the "opaque" origin (seemingly universally the string `null`) as the `Origin` header.

The Helidon CORS logic did not handle this case.  "Usual" origins have `scheme://host[:port]` so have at least one colon which terminates the scheme. The code expected this but `null` has no colon.

Includes a new test.

### Documentation
No impact.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e8de4b8f2..a10000d8e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,119 @@ For Helidon 2.x releases please see [Helidon 2.x CHANGELOG.md](https://github.co
 
 For Helidon 1.x releases please see [Helidon 1.x CHANGELOG.md](https://github.com/oracle/helidon/blob/helidon-1.x/CHANGELOG.md)
 
+## [4.0.6]
+
+This release contains important bugfixes and ehancements and is recommended for all users of Helidon 4. 
+
+Java 21 is required to use Helidon 4.0.6.
+
+### CHANGES
+
+- Builders: Fixes for problems discovered in service registry work [8403](https://github.com/helidon-io/helidon/pull/8403)
+- Config: Config encryption improvements [8440](https://github.com/helidon-io/helidon/pull/8440)
+- DBClient: Fix for #8344 - Incorrect SQL Type for null Parameters in JDBC Parameter Binding [8342](https://github.com/helidon-io/helidon/pull/8342)
+- DataSource: Adds support for injecting UniversalConnectionPool instances [8378](https://github.com/helidon-io/helidon/pull/8378)
+- DataSource: Installs a convenient default for connectionPoolName in UCPBackedDataSourceExtension when appropriate [8359](https://github.com/helidon-io/helidon/pull/8359)
+- HTTP: Enhance Status.java with Additional Standard HTTP Status Codes [8444](https://github.com/helidon-io/helidon/pull/8444)
+- LRA: Replace deprecated classInfo.classAnnotation(DotName) on classInfo.declaredAnnotation(DotName) [8417](https://github.com/helidon-io/helidon/pull/8417)
+- Media: Multipart fixes (streaming, consume) [8375](https://github.com/helidon-io/helidon/pull/8375)
+- Media: Replace deprecated MediaType.fullType() on MediaType.text() [8340](https://github.com/helidon-io/helidon/pull/8340)
+- Metrics: More carefully handle re-creation of global meter registry [8389](https://github.com/helidon-io/helidon/pull/8389)
+- Neo4J: Replace deprecated session.writeTransaction on session.executeWrite in Neo4jHealthCheck [8397](https://github.com/helidon-io/helidon/pull/8397)
+- OCI: Generalize the creation of the `OciMetricsSupport` instance from CDI [8431](https://github.com/helidon-io/helidon/pull/8431)
+- OIDC: OIDC hostname handling [8456](https://github.com/helidon-io/helidon/pull/8456)
+- OIDC: OIDC updates [8387](https://github.com/helidon-io/helidon/pull/8387)
+- OIDC: check file system support posix before setting permission [8296](https://github.com/helidon-io/helidon/pull/8296)
+- OIDC: refresh token optionality fix [8336](https://github.com/helidon-io/helidon/pull/8336)
+- Reactive: Replace deprecated  Awaitable.await(long, TimeUnit) on Awaitable.await(Duration) [8334](https://github.com/helidon-io/helidon/pull/8334)
+- Tracing: Add baggage API and allow access to baggage via `SpanContext` (as well as `Span`) [8320](https://github.com/helidon-io/helidon/pull/8320)
+- Tracing: Suppress Helidon handling of OpenTelemetry annotations if OTel Java agent is present [8360](https://github.com/helidon-io/helidon/pull/8360)
+- Tracing: Use OTel AgentDetector instead of just system property - review comments from earlier PR [8374](https://github.com/helidon-io/helidon/pull/8374)
+- WebServer: Adds support for sending websocket CLOSE frames without a payload [8408](https://github.com/helidon-io/helidon/pull/8408)
+- WebServer: Fix buffering of content in our Jersey integration. [8461](https://github.com/helidon-io/helidon/pull/8461)
+- WebServer: Include requested URI config in ListenerConfig; fix config keys [8371](https://github.com/helidon-io/helidon/pull/8371)
+- WebServer: Releasing entity count down latch even if connection is set to close. [8376](https://github.com/helidon-io/helidon/pull/8376)
+- WebServer: Return 500 when 204 with entity is sent from routing. [8357](https://github.com/helidon-io/helidon/pull/8357)
+- WebServer: TLS Revocation config [8425](https://github.com/helidon-io/helidon/pull/8425)
+- WebServer: TLS peer certs performance [8316](https://github.com/helidon-io/helidon/pull/8316)
+- Build:  Upgrade GitHub actions [8442](https://github.com/helidon-io/helidon/pull/8442)
+- Dependencies: Bump com.oracle.oci.sdk:oci-java-sdk-bom from 3.34.0 to 3.35.0 in /dependencies [8421](https://github.com/helidon-io/helidon/pull/8421)
+- Dependencies: PostgreSQL JDBC driver updated to 42.7.2. [8413](https://github.com/helidon-io/helidon/pull/8413)
+- Dependencies: Upgrades Oracle database libraries to version 21.9.0.0. Adds tests under datasource-ucp CDI integration. [8221](https://github.com/helidon-io/helidon/pull/8221)
+- Docs: Externalize and compile documentation java snippets [8294](https://github.com/helidon-io/helidon/pull/8294)
+- Docs: Replace deprecated session.readTransaction on session.executeRead [8420](https://github.com/helidon-io/helidon/pull/8420)
+- Docs: Update `context-root` config documentation [8427](https://github.com/helidon-io/helidon/pull/8427)
+- Docs: Update javadocs of Meter builders to cross reference MeterRegistry.getOrCreate() [8381](https://github.com/helidon-io/helidon/pull/8381)
+- Docs: doc examples snippets cleanup [8439](https://github.com/helidon-io/helidon/pull/8439)
+- Examples: Align README.md vs functionality [8380](https://github.com/helidon-io/helidon/pull/8380)
+- Examples: Archetype - Fix sql script for oracle database [8426](https://github.com/helidon-io/helidon/pull/8426)
+- Examples: Fix WebServer Mutual TLS example [8409](https://github.com/helidon-io/helidon/pull/8409)
+- Examples: archetype cleanup  [8445](https://github.com/helidon-io/helidon/pull/8445)
+- Examples: examples cleanup [8433](https://github.com/helidon-io/helidon/pull/8433)
+- Examples: generate a .gitignore files from archetype [8401](https://github.com/helidon-io/helidon/pull/8401)
+- Tests: Also catch UncheckedIOException in test to handle broken pipes [8462](https://github.com/helidon-io/helidon/pull/8462)
+- Tests: Fix OTel agent detector test that was polluting the JVM for later tests [8449](https://github.com/helidon-io/helidon/pull/8449)
+- Tests: Improve examples test coverage [8338](https://github.com/helidon-io/helidon/pull/8338)
+- Tests: Make test execution conditional to IPv6 being configured for localhost [8388](https://github.com/helidon-io/helidon/pull/8388)
+- Tests: Replace deprecated API for configuring ObjectMapper [8395](https://github.com/helidon-io/helidon/pull/8395)
+- Tests: Replace deprecated Multi.from(Stream) on Multi.create(Stream) [8424](https://github.com/helidon-io/helidon/pull/8424)
+- Tests: Replace deprecated OutputStreamMultiBuilder.timeout(long,TimeUnit) on OutputStreamMultiBuilder.timeout(Duration) [8404](https://github.com/helidon-io/helidon/pull/8404)
+- Tests: Replace deprecated java.util.Locale constructors on factory methods [8355](https://github.com/helidon-io/helidon/pull/8355)
+- Tests: Restfull tck 3.1.5 [8368](https://github.com/helidon-io/helidon/pull/8368)
+- Tests: Updates test to handle IOException if connection was closed [8457](https://github.com/helidon-io/helidon/pull/8457)
+
+
+## [4.0.5]
+
+This release contains important bugfixes and ehancements and is recommended for all users of Helidon 4. 
+
+Java 21 is required to use Helidon 4.0.5.
+
+### CHANGES
+
+- Builders: Use vararg instead of optional (as a name). [8301](https://github.com/helidon-io/helidon/pull/8301)
+- HTTP: Fixes a few problems handling character encodings in URIs [8327](https://github.com/helidon-io/helidon/pull/8327)
+- Metrics: Remove clear-out of registries from extension; do it in TCK-only shutdown observer; update doc [8292](https://github.com/helidon-io/helidon/pull/8292)
+- Security: OIDC improvement [8323](https://github.com/helidon-io/helidon/pull/8323)
+- Tracing: Add null check for resource path in filter [8312](https://github.com/helidon-io/helidon/pull/8312)
+- Tracing: Fix unwrap methods to return cast delegates rather than cast 'this' [8298](https://github.com/helidon-io/helidon/pull/8298)
+- Tracing: Set incoming baggage from parent on Span [8303](https://github.com/helidon-io/helidon/pull/8303)
+- Dependencies: Upgrade OCI SDK to 3.31.1 [8304](https://github.com/helidon-io/helidon/pull/8304)
+- Test: Fix KafkaSeTest on Windows [8322](https://github.com/helidon-io/helidon/pull/8322)
+- Test: Fix CachedHandlerTest.testFsFromInMemory on Windows [8318](https://github.com/helidon-io/helidon/pull/8318)
+
+## [4.0.4]
+
+This release contains bugfixes and ehancements and is recommended for all users of Helidon 4. In particular
+this release contains a fix for issue [8250](https://github.com/helidon-io/helidon/issues/8250) which can occur
+when running previous releases of Helidon 4 on Java 21.0.2 or newer.
+
+Java 21 is required to use Helidon 4.0.4.
+
+### CHANGES
+
+- JPA: Ensures that the enabled flag is honored in JpaExtension's observer methods [8235](https://github.com/helidon-io/helidon/pull/8235)
+- Media: JSON unicode detection without UTF-32 workaround [8253](https://github.com/helidon-io/helidon/pull/8253)
+- RestClient: TLS replace in HelidonConnector fix [8247](https://github.com/helidon-io/helidon/pull/8247)
+- Security: Authorized set to false fix [8295](https://github.com/helidon-io/helidon/pull/8295)
+- Security: Security propagation is now disabled with not configured [8239](https://github.com/helidon-io/helidon/pull/8239)
+- Tracing: Guard against NPE during early invocation of Span.current() [8257](https://github.com/helidon-io/helidon/pull/8257)
+- Tracing: Slight clean-up of recent baggage and span scope fix [8258](https://github.com/helidon-io/helidon/pull/8258)
+- WebClient: Cleans up and simplifies logic to determine which type of IP addresses to consider [8280](https://github.com/helidon-io/helidon/pull/8280)
+- WebClient: Http client protocol creation not honoring disabled service discovery [8284](https://github.com/helidon-io/helidon/pull/8284)
+- WebClient: Set weight for `WebClientTracing` provider greater than that for `WebClientSecurity` provider [8274](https://github.com/helidon-io/helidon/pull/8274)
+- WebServer: Fix for buffer data when created with an offset. [8251](https://github.com/helidon-io/helidon/pull/8251)
+- WebServer: Some minor optimizations in WebServer [8242](https://github.com/helidon-io/helidon/pull/8242)
+- Build: Change backport action to accept target versions for porting via checkboxes and select current issue's version from a list [8236](https://github.com/helidon-io/helidon/pull/8236)
+- Dependencies: Upgrade Yasson to 3.0.3 [8272](https://github.com/helidon-io/helidon/pull/8272)
+- Dependencies: Upgrading to latest Tyrus 2.1.5 [8278](https://github.com/helidon-io/helidon/pull/8278)
+- Docs: broken link fix [8259](https://github.com/helidon-io/helidon/pull/8259)
+- Docs: change WebServer.Builder to WebServerConfig.Builder. [8261](https://github.com/helidon-io/helidon/pull/8261)
+- Docs: fix broken documentation links Part 2 [8300](https://github.com/helidon-io/helidon/pull/8300)
+- Examples: Remove unnecessary GreetingProvider from example custom liveness health check [8184](https://github.com/helidon-io/helidon/pull/8184)
+- Tests: Add back span name test containing an explicit Application bean [8238](https://github.com/helidon-io/helidon/pull/8238)
+- Tests: Fix vault tests [8279](https://github.com/helidon-io/helidon/pull/8279)
+
 ## [4.0.3]
 
 This release contains bugfixes and ehancements and is recommended for all users of Helidon 4.
@@ -981,6 +1094,9 @@ Helidon 4.0.0 is a major release that includes significant new features and fixe
 - MicroProfile: MP path based static content should use index.html (4.x) [4737](https://github.com/oracle/helidon/pull/4737)
 - Build: 4.0 version and poms [4655](https://github.com/oracle/helidon/pull/4655)
 
+[4.0.6]: https://github.com/oracle/helidon/compare/4.0.5...4.0.6
+[4.0.5]: https://github.com/oracle/helidon/compare/4.0.4...4.0.5
+[4.0.4]: https://github.com/oracle/helidon/compare/4.0.3...4.0.4
 [4.0.3]: https://github.com/oracle/helidon/compare/4.0.2...4.0.3
 [4.0.2]: https://github.com/oracle/helidon/compare/4.0.1...4.0.2
 [4.0.1]: https://github.com/oracle/helidon/compare/4.0.0...4.0.1
diff --git a/archetypes/archetypes/src/main/archetype/common/files/app.yaml.mustache b/archetypes/archetypes/src/main/archetype/common/files/app.yaml.mustache
index 9a51f58cd6..4178b288e0 100644
--- a/archetypes/archetypes/src/main/archetype/common/files/app.yaml.mustache
+++ b/archetypes/archetypes/src/main/archetype/common/files/app.yaml.mustache
@@ -5,13 +5,14 @@ metadata:
   labels:
     app: {{artifactId}}
 spec:
-  type: NodePort
+  type: ClusterIP
   selector:
     app: {{artifactId}}
   ports:
-  - port: 8080
-    targetPort: 8080
-    name: http
+    - name: tcp
+      port: 8080
+      protocol: TCP
+      targetPort: 8080
 ---
 kind: Deployment
 apiVersion: apps/v1
diff --git a/archetypes/archetypes/src/main/archetype/common/packaging.xml b/archetypes/archetypes/src/main/archetype/common/packaging.xml
index 847256e769..cd501ad680 100644
--- a/archetypes/archetypes/src/main/archetype/common/packaging.xml
+++ b/archetypes/archetypes/src/main/archetype/common/packaging.xml
@@ -37,8 +37,7 @@
                     </templates>
                     <model>
                     <list key="readme-sections">
-                    <value order="50" template="mustache">
-                        <![CDATA[
+                    <value order="50" template="mustache"><![CDATA[
 ## Run the application in Kubernetes
 
 If you don’t have access to a Kubernetes cluster, you can [install one](https://helidon.io/docs/latest/#/about/kubernetes) on your desktop.
@@ -59,20 +58,20 @@ kubectl get pods                            # Verify connectivity to cluster
 ### Deploy the application to Kubernetes
 
 ```
-kubectl create -f app.yaml                  # Deploy application
-kubectl get pods                            # Wait for quickstart pod to be RUNNING
-kubectl get service  {{artifactId}}         # Get service info
+kubectl create -f app.yaml                              # Deploy application
+kubectl get pods                                        # Wait for quickstart pod to be RUNNING
+kubectl get service  {{artifactId}}                     # Get service info
+kubectl port-forward service/{{artifactId}} 8081:8080   # Forward service port to 8081
 ```
 
-Note the PORTs. You can now exercise the application as you did before but use the second
-port number (the NodePort) instead of 8080.
+You can now exercise the application as you did before but use the port number 8081.
 
 After you’re done, cleanup.
 
 ```
 kubectl delete -f app.yaml
 ```
-                                ]]>
+]]>
                             </value>
                         </list>
                     </model>
diff --git a/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java b/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java
index 55da8e5327..ed4c9207f2 100644
--- a/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java
+++ b/cors/src/main/java/io/helidon/cors/CorsSupportHelper.java
@@ -61,8 +61,11 @@ public class CorsSupportHelper<Q, R> {
 
     static final Logger LOGGER = Logger.getLogger(CorsSupportHelper.class.getName());
 
+    static final String OPAQUE_ORIGIN = "null"; // browsers might send this as Origin header if origin info is untrusted
+
     private static final Supplier<Optional<CrossOriginConfig>> EMPTY_SECONDARY_SUPPLIER = Optional::empty;
 
+
     private final String name;
 
     /**
@@ -392,7 +395,7 @@ public class CorsSupportHelper<Q, R> {
 
             return new RequestTypeInfo(originLocation,
                                        hostLocation,
-                                       originLocation.equals(hostLocation));
+                                       originLocation.equals(hostLocation) || originLocation.equals(OPAQUE_ORIGIN));
         }
     }
 
@@ -405,6 +408,10 @@ public class CorsSupportHelper<Q, R> {
             LogHelper.logIsRequestTypeNormalNoOrigin(silent, requestAdapter);
             return true;
         }
+        if (isOriginOpaque(originOpt.get())) {
+            LogHelper.logOpaqueOrigin(silent, requestAdapter);
+            // Do not return. Continue processing having noted the opaque origin.
+        }
 
         RequestTypeInfo result = requestType(originOpt.get(), requestAdapter.requestedUri());
         LogHelper.logIsRequestTypeNormal(result.isNormal,
@@ -416,12 +423,16 @@ public class CorsSupportHelper<Q, R> {
         return result.isNormal;
     }
 
+    static boolean isOriginOpaque(String origin) {
+        return origin.equals(OPAQUE_ORIGIN);
+    }
+
     private static String originLocation(String origin) {
         int originEndOfScheme = origin.indexOf(':');
         int originLastColon = origin.lastIndexOf(':');
 
         return origin + (
-                (originEndOfScheme == originLastColon)
+                (originEndOfScheme == originLastColon && originEndOfScheme >= 0)
                         ? ":" + portForScheme(origin.substring(0, originEndOfScheme))
                         : "");
     }
diff --git a/cors/src/main/java/io/helidon/cors/LogHelper.java b/cors/src/main/java/io/helidon/cors/LogHelper.java
index 638d5e7898..3b121a8387 100644
--- a/cors/src/main/java/io/helidon/cors/LogHelper.java
+++ b/cors/src/main/java/io/helidon/cors/LogHelper.java
@@ -79,6 +79,16 @@ class LogHelper {
                                                    List.of("header " + HeaderNames.ORIGIN + " is absent")));
     }
 
+    static <T> void logOpaqueOrigin(boolean silent, CorsRequestAdapter<T> requestAdapter) {
+        if (silent || !CorsSupportHelper.LOGGER.isLoggable(DECISION_LEVEL)) {
+            return;
+        }
+        CorsSupportHelper.LOGGER.log(DECISION_LEVEL,
+                                     String.format("Request %s specifies opaque origin: %s",
+                                                   requestAdapter,
+                                                   List.of("header " + HeaderNames.ORIGIN)));
+    }
+
     static <T> void logIsRequestTypeNormal(boolean result, boolean silent, CorsRequestAdapter<T> requestAdapter,
             Optional<String> originOpt, String effectiveOrigin, String host) {
         if (silent || !CorsSupportHelper.LOGGER.isLoggable(DECISION_LEVEL)) {


Test patch:
diff --git a/cors/src/test/java/io/helidon/cors/CorsSupportHelperTest.java b/cors/src/test/java/io/helidon/cors/CorsSupportHelperTest.java
index 22d7dffba7..3b01fbc441 100644
--- a/cors/src/test/java/io/helidon/cors/CorsSupportHelperTest.java
+++ b/cors/src/test/java/io/helidon/cors/CorsSupportHelperTest.java
@@ -75,6 +75,17 @@ class CorsSupportHelperTest {
                    is(false));
     }
 
+    @Test
+    void opaqueOrigin() {
+        assertThat("Opaque origin",
+                   CorsSupportHelper.isOriginOpaque(CorsSupportHelper.OPAQUE_ORIGIN),
+                   is(true));
+
+        assertThat("Non-opaque origin",
+                   CorsSupportHelper.isOriginOpaque("http://ok.com"),
+                   is(false));
+    }
+
     @Test
     void sameNodeSamePort() {
         assertThat("Default origin port",