diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5cc2737b45f56462bd9e7819ac2cff439dcf7863..37d7b15f7405d27ab859519f9c0af80032507d93 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,9 +8,16 @@ unit-tests:
   script:
     - "nix-shell --run 'nix-build nixos/unit-tests.nix' && cat result"
 
+vulnerability-scan:
+  stage: "test"
+  script:
+    - "ci-tools/vulnerability-scan security-report.txt"
+  artifacts:
+    paths:
+      - "security-report.txt"
+
 system-tests:
   stage: "test"
   timeout: "3 hours"
   script:
     - "nix-shell --run 'nix-build nixos/system-tests.nix'"
-
diff --git a/ci-tools/vulnerability-scan b/ci-tools/vulnerability-scan
new file mode 100755
index 0000000000000000000000000000000000000000..355fd31cbca1643d82fe71260ecc11632e9a7a11
--- /dev/null
+++ b/ci-tools/vulnerability-scan
@@ -0,0 +1,25 @@
+#!/usr/bin/env sh
+
+#
+# `morph build ...` output is like
+#
+#   Selected 2/2 hosts (name filter:-0, limits:-0):
+#             0: xx.xx.xx.xx (secrets: 1, health checks: 0)
+#             1: yy.yy.yy.yy (secrets: 2, health checks: 0)
+#
+#   /nix/store/d7spc457nnzh0rnv0f5lh1q2j435j1b9-morph
+#   nix result path:
+#   /nix/store/d7spc457nnzh0rnv0f5lh1q2j435j1b9-morph
+#
+# Get the last line so we can scan it.
+#
+
+OUTPUT=$1
+
+rm -v scan-target
+nix-shell --run '
+object=$(morph build morph/grid/testing/grid.nix 2>&1 | tail -n 1)
+ln -s "$object" scan-target
+'
+
+nix-shell -p vulnix --run 'vulnix ./scan-target/' | tee "$OUTPUT"