-
Notifications
You must be signed in to change notification settings - Fork 34
Closed
Labels
Description
A bug in Retrofit2Helper causes a crash when the server sends multiple headers with the same name. This happens because Collectors.toMap is used without a merge function to handle duplicate keys.
Steps to reproduce:
- Use the Nextcloud Notes app that uses the SSO library.
- Perform an action that results in an HTTP response with duplicate headers (e.g.,
X-Robots-Tag). - The app will crash with an
IllegalStateException: Duplicate key.
Error in Android App:
10-06 16:24:17.514 7178 9545 E NotesRepository: java.lang.Exception: Duplicate key X-Robots-Tag (attempted merging values noindex, nofollow and noindex, nofollow)
at java.util.stream.Collectors.duplicateKeyException(Collectors.java:135)
at java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:182)
at java.util.stream.Collectors$$ExternalSyntheticLambda1.accept(D8$$SyntheticClass:0)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1725)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:503)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:236)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:705)
at com.nextcloud.android.sso.helper.Retrofit2Helper$1.lambda$execute$0(Retrofit2Helper.java:57)
at com.nextcloud.android.sso.helper.Retrofit2Helper$1$$ExternalSyntheticLambda2.apply(D8$$SyntheticClass:0)
at java.util.Optional.map(Optional.java:260)
at com.nextcloud.android.sso.helper.Retrofit2Helper$1.execute(Retrofit2Helper.java:55)
at it.niedermann.owncloud.notes.persistence.NotesServerSyncTask.pushLocalChanges(NotesServerSyncTask.java:130)
at it.niedermann.owncloud.notes.persistence.NotesServerSyncTask.run(NotesServerSyncTask.java:99)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:524)
at java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1156)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:651)
at java.lang.Thread.run(Thread.java:1119)
Http request:
Server: nginx
Date: XXX
Content-Type: application/json; charset=utf-8
Content-Length: 502
Connection: close
X-Request-Id: XXX
Cache-Control: no-cache, no-store, must-revalidate
Content-Security-Policy: default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'
Feature-Policy: autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'
X-Robots-Tag: noindex, nofollow
Content-Encoding: gzip
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Robots-Tag: noindex, nofollow
X-XSS-Protection: 1; mode=block