Skip to content

Commit c431600

Browse files
committed
fix(server): clear SonarCloud gate — close resources + harden CI curl/npm
The SonarCloud quality gate failed on exactly two conditions (Reliability E and Security C on new code), driven by 2 Blocker bugs + 25 Major security findings. (The ~45 other annotations are maintainability code smells that do not affect the gate.) Reliability (java:S2095, OpenAiCompatServer.main L888/L889): Sonar did not trace that the LlamaModel and OpenAiCompatServer are closed by the shutdown hook, so it flagged them as never closed. Refactor main() to hold both in a try-with-resources; a two-latch shutdown keeps termination graceful and race-free (the hook signals stopRequested then waits on cleanedUp, so the JVM — which blocks until shutdown hooks return — does not halt until the close has run). This also closes the model if server startup throws, which the previous code did not. Security (.github/workflows/publish.yml): - npm ci -> npm ci --ignore-scripts in the build-webui job, so dependency lifecycle scripts do not run during install (the WebUI build still runs via `npm run build`). - Every curl model-download now passes --proto =https --proto-redir =https, so neither the URL nor any redirect can downgrade to cleartext HTTP (the URLs are already https; this enforces it). 31 invocations hardened. These are exactly the 2 reliability + 25 security issues SonarCloud listed, so both ratings should return to A. Verified: mvn spotless:apply + compile clean; publish.yml parses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdLpWD8nedY7LwNnHefZLF
1 parent 75cae60 commit c431600

2 files changed

Lines changed: 59 additions & 42 deletions

File tree

.github/workflows/publish.yml

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ jobs:
124124
HF_UI_VERSION: ${{ steps.tag.outputs.tag }}
125125
LLAMA_BUILD_NUMBER: ${{ steps.tag.outputs.tag }}
126126
run: |
127-
npm ci
127+
npm ci --ignore-scripts
128128
npm run build
129129
test -f dist/index.html
130130
- name: Embed assets into ui.cpp / ui.h (gzip parity with upstream)
@@ -529,19 +529,19 @@ jobs:
529529
name: Linux-x86_64-libraries
530530
path: ${{ github.workspace }}/src/main/resources/net/ladenthin/llama/
531531
- name: Download text generation model
532-
run: curl -L --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
532+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
533533
- name: Download reranking model
534-
run: curl -L --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
534+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
535535
- name: Download draft model
536-
run: curl -L --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
536+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
537537
- name: Download reasoning model
538-
run: curl -L --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
538+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
539539
- name: Download nomic embedding model (issue #98 regression)
540-
run: curl -L --fail --retry 5 --retry-all-errors ${NOMIC_EMBED_MODEL_URL} --create-dirs -o models/${NOMIC_EMBED_MODEL_NAME}
540+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${NOMIC_EMBED_MODEL_URL} --create-dirs -o models/${NOMIC_EMBED_MODEL_NAME}
541541
- name: Download vision model (issues #103 / #34)
542-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
542+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
543543
- name: Download vision mmproj
544-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
544+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
545545
- name: List files in models directory
546546
run: ls -l models/
547547
- name: Validate model files
@@ -650,17 +650,17 @@ jobs:
650650
name: macos-14-libraries
651651
path: ${{ github.workspace }}/src/main/resources/net/ladenthin/llama/
652652
- name: Download text generation model
653-
run: curl -L --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
653+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
654654
- name: Download reranking model
655-
run: curl -L --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
655+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
656656
- name: Download draft model
657-
run: curl -L --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
657+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
658658
- name: Download reasoning model
659-
run: curl -L --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
659+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
660660
- name: Download vision model (issues #103 / #34)
661-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
661+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
662662
- name: Download vision mmproj
663-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
663+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
664664
- name: List files in models directory
665665
run: ls -l models/
666666
- name: Validate model files
@@ -714,17 +714,17 @@ jobs:
714714
name: macos-15-libraries
715715
path: ${{ github.workspace }}/src/main/resources/net/ladenthin/llama/
716716
- name: Download text generation model
717-
run: curl -L --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
717+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
718718
- name: Download reranking model
719-
run: curl -L --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
719+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
720720
- name: Download draft model
721-
run: curl -L --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
721+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
722722
- name: Download reasoning model
723-
run: curl -L --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
723+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
724724
- name: Download vision model (issues #103 / #34)
725-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
725+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
726726
- name: Download vision mmproj
727-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
727+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
728728
- name: List files in models directory
729729
run: ls -l models/
730730
- name: Validate model files
@@ -778,17 +778,17 @@ jobs:
778778
name: macos-15-metal-libraries
779779
path: ${{ github.workspace }}/src/main/resources/net/ladenthin/llama/
780780
- name: Download text generation model
781-
run: curl -L --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
781+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${MODEL_URL} --create-dirs -o models/${MODEL_NAME}
782782
- name: Download reranking model
783-
run: curl -L --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
783+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${RERANKING_MODEL_URL} --create-dirs -o models/${RERANKING_MODEL_NAME}
784784
- name: Download draft model
785-
run: curl -L --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
785+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${DRAFT_MODEL_URL} --create-dirs -o models/${DRAFT_MODEL_NAME}
786786
- name: Download reasoning model
787-
run: curl -L --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
787+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${REASONING_MODEL_URL} --create-dirs -o models/${REASONING_MODEL_NAME}
788788
- name: Download vision model (issues #103 / #34)
789-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
789+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MODEL_URL} --create-dirs -o models/${VISION_MODEL_NAME}
790790
- name: Download vision mmproj
791-
run: curl -L --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
791+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors ${VISION_MMPROJ_URL} --create-dirs -o models/${VISION_MMPROJ_NAME}
792792
- name: List files in models directory
793793
run: ls -l models/
794794
- name: Validate model files
@@ -845,17 +845,17 @@ jobs:
845845
name: Windows-x86_64-libraries
846846
path: ${{ github.workspace }}/src/main/resources/net/ladenthin/llama/
847847
- name: Download text generation model
848-
run: curl -L --fail --retry 5 --retry-all-errors $env:MODEL_URL --create-dirs -o models/$env:MODEL_NAME
848+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors $env:MODEL_URL --create-dirs -o models/$env:MODEL_NAME
849849
- name: Download reranking model
850-
run: curl -L --fail --retry 5 --retry-all-errors $env:RERANKING_MODEL_URL --create-dirs -o models/$env:RERANKING_MODEL_NAME
850+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors $env:RERANKING_MODEL_URL --create-dirs -o models/$env:RERANKING_MODEL_NAME
851851
- name: Download draft model
852-
run: curl -L --fail --retry 5 --retry-all-errors $env:DRAFT_MODEL_URL --create-dirs -o models/$env:DRAFT_MODEL_NAME
852+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors $env:DRAFT_MODEL_URL --create-dirs -o models/$env:DRAFT_MODEL_NAME
853853
- name: Download reasoning model
854-
run: curl -L --fail --retry 5 --retry-all-errors $env:REASONING_MODEL_URL --create-dirs -o models/$env:REASONING_MODEL_NAME
854+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors $env:REASONING_MODEL_URL --create-dirs -o models/$env:REASONING_MODEL_NAME
855855
- name: Download vision model (issues #103 / #34)
856-
run: curl -L --fail --retry 5 --retry-all-errors $env:VISION_MODEL_URL --create-dirs -o models/$env:VISION_MODEL_NAME
856+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors $env:VISION_MODEL_URL --create-dirs -o models/$env:VISION_MODEL_NAME
857857
- name: Download vision mmproj
858-
run: curl -L --fail --retry 5 --retry-all-errors $env:VISION_MMPROJ_URL --create-dirs -o models/$env:VISION_MMPROJ_NAME
858+
run: curl -L --proto =https --proto-redir =https --fail --retry 5 --retry-all-errors $env:VISION_MMPROJ_URL --create-dirs -o models/$env:VISION_MMPROJ_NAME
859859
- name: List files in models directory
860860
run: ls -l models/
861861
- name: Validate model files

src/main/java/net/ladenthin/llama/server/OpenAiCompatServer.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.io.OutputStream;
1818
import java.net.InetSocketAddress;
1919
import java.nio.charset.StandardCharsets;
20+
import java.util.concurrent.CountDownLatch;
2021
import java.util.concurrent.ExecutorService;
2122
import java.util.concurrent.Executors;
2223
import java.util.concurrent.ScheduledExecutorService;
@@ -885,21 +886,37 @@ public static void main(String[] args) throws IOException {
885886
}
886887

887888
OpenAiServerConfig config = options.toServerConfig();
888-
LlamaModel model = new LlamaModel(options.toModelParameters());
889-
OpenAiCompatServer server = new OpenAiCompatServer(model, config);
889+
890+
// The server runs on daemon threads, so the main thread blocks until the JVM is asked to
891+
// shut down (Ctrl-C / SIGTERM); the try-with-resources then closes the server and model.
892+
// Two latches keep that shutdown graceful and race-free: the hook signals stopRequested and
893+
// then waits on cleanedUp, so the JVM — which blocks until shutdown hooks return — does not
894+
// halt until the close has actually run.
895+
final CountDownLatch stopRequested = new CountDownLatch(1);
896+
final CountDownLatch cleanedUp = new CountDownLatch(1);
890897
Runtime.getRuntime()
891898
.addShutdownHook(new Thread(
892899
() -> {
893-
server.close();
894-
model.close();
900+
stopRequested.countDown();
901+
try {
902+
cleanedUp.await();
903+
} catch (InterruptedException e) {
904+
Thread.currentThread().interrupt();
905+
}
895906
},
896907
"jllama-openai-shutdown"));
897-
server.start();
898-
printReady(config, server.getPort());
899-
try {
900-
Thread.currentThread().join();
901-
} catch (InterruptedException e) {
902-
Thread.currentThread().interrupt();
908+
909+
try (LlamaModel model = new LlamaModel(options.toModelParameters());
910+
OpenAiCompatServer server = new OpenAiCompatServer(model, config)) {
911+
server.start();
912+
printReady(config, server.getPort());
913+
try {
914+
stopRequested.await();
915+
} catch (InterruptedException e) {
916+
Thread.currentThread().interrupt();
917+
}
918+
} finally {
919+
cleanedUp.countDown();
903920
}
904921
}
905922

0 commit comments

Comments
 (0)