diff --git a/build-pipeline-yocto.yml.jinja2 b/build-pipeline-yocto.yml.jinja2
index b23e689a295d636c1ebcf9b47faa7b8a55cf6f7e..90fc3d4dd10a85d2c88d79b37df56ecde07c82b5 100644
--- a/build-pipeline-yocto.yml.jinja2
+++ b/build-pipeline-yocto.yml.jinja2
@@ -17,6 +17,7 @@ include:
 stages:
   - build
   - deploy
+  - security
   - changelog
   - notify
   - sync-mirror
@@ -342,6 +343,16 @@ deploy-{{ machine }}-{{ distro }}:
      artifacts: true
   variables: &deploy-{{ machine }}-{{ distro }}
       <<: *build-{{ machine }}-{{ distro }}
+#----------------------------------------------------
+# {{ SECO_REMOTE }}-security-{{ machine }}-{{ distro }}
+#----------------------------------------------------
+security-{{ machine }}-{{ distro }}:
+  extends: .security
+  needs:
+   - job: deploy-{{ machine }}-{{ distro }}
+     artifacts: true
+  variables:
+      <<: *deploy-{{ machine }}-{{ distro }}
           {% if testing is defined %}
 #----------------------------------------------------
 # {{ SECO_REMOTE }}-test-{{ machine }}-{{ distro }}
@@ -399,6 +410,16 @@ deploy-{{ machine }}-{{ distro }}:
      artifacts: true
   variables: &deploy-{{ machine }}-{{ distro }}
       <<: *build-{{ machine }}-{{ distro }}
+#----------------------------------------------------
+# {{ SECO_REMOTE }}-security-{{ machine }}-{{ distro }}
+#----------------------------------------------------
+security-{{ machine }}-{{ distro }}:
+  extends: .security
+  needs:
+   - job: deploy-{{ machine }}-{{ distro }}
+     artifacts: true
+  variables:
+      <<: *deploy-{{ machine }}-{{ distro }}
       {% if machine == 'a62-1G-4x256M' or machine == 'c20' or machine == 'd23' or machine == 'intel' %}
 #----------------------------------------------------
 # {{ SECO_REMOTE }}-test-{{ machine }}-{{ distro }}
diff --git a/build-pipeline.yml b/build-pipeline.yml
index e2b9d72bb776651719a7c70f6b479339bc724857..f9b6ef6a3a771491bfbf9729be667e091c8e3154 100644
--- a/build-pipeline.yml
+++ b/build-pipeline.yml
@@ -203,6 +203,47 @@ workflow:
       - Link_report_${BOARD}_${IMAGE_NAME}.txt
       - MD5_sums_${BOARD}_${IMAGE_NAME}.txt
 
+# -----------------------------------
+# Stage: security
+# -----------------------------------
+.security:
+  extends:
+    - .infrastructure
+  image: git.seco.com:5050/clea-os/infrastructure/ci-images/infrastructure/exein-analyzer:latest
+  stage: security
+  cache: {}
+  retry: 2
+  timeout: 2h
+  tags:
+    - security
+  rules:
+    - if: $SECURITY_STAGE == "manual"
+      when: manual
+      allow_failure: true
+    - if: $SECURITY_STAGE == "auto"
+      allow_failure: true
+  script:
+    - |
+       if [ -n "$CUSTOM" ]; then
+          AZURE_STORAGE_SAS_TOKEN="${AZURE_STORAGE_PRIVATE_SAS_TOKEN}"
+          AZURE_CONTAINER_NAME="${AZURE_PRIVATE_CONTAINER_NAME}"
+       fi
+       if [ -n "$CI_COMMIT_TAG" ]; then
+         if [ ! -n "$CUSTOM" ]; then
+           AZURE_STORAGE_SAS_TOKEN="${AZURE_STORAGE_PUBLIC_SAS_TOKEN}"
+           AZURE_CONTAINER_NAME="${AZURE_PUBLIC_CONTAINER_NAME}"
+         fi
+       fi
+    - .gitlab-ci/scripts/exein-analyzer.sh $EXEIN_API_PLATFORM_URL $EXEIN_API_KEY $AZURE_CONTAINER_NAME $AZURE_STORAGE_SAS_TOKEN
+  allow_failure: true
+  artifacts:
+    when: always
+    paths:
+      - "*exein*.tar.gz"
+      - Job_report_${BOARD}_${IMAGE_NAME}.txt
+      - Job_message_${BOARD}_${IMAGE_NAME}.txt
+      - Link_report_${BOARD}_${IMAGE_NAME}.txt
+
 # -----------------------------------
 # Stage: changelog
 # -----------------------------------
diff --git a/docs/add-new-project-to-pipeline.md b/docs/add-new-project-to-pipeline.md
index 53f0f0e65c644ed8bdbfa8ae5e44306b79e4af4a..adb9c6225fe447fedee5a1506b73ae51ac97c751 100644
--- a/docs/add-new-project-to-pipeline.md
+++ b/docs/add-new-project-to-pipeline.md
@@ -181,3 +181,5 @@ level) in the custom manifest repository:<>
   Set whether to include the Test stage (supported values: `manual`, `auto`)
 * `CREATE_CHANGELOG`  
   Set whether "changelog" job should be started.
+* `SECURITY_STAGE`
+  Set whether to include the Test stage (supported values: `manual`, `auto`)
diff --git a/manifest-pipeline-yocto.yml b/manifest-pipeline-yocto.yml
index 9bd48597328b687e920852aa23fa72eba5c3084e..54b77451718673a36c0a03893c02d6b605210ab1 100644
--- a/manifest-pipeline-yocto.yml
+++ b/manifest-pipeline-yocto.yml
@@ -66,6 +66,7 @@ yocto-pipeline:
     ARTIFACTS_PATH: build_*/${IMAGES_PATH}/**/*
     TEST_STAGE: "auto"
     CREATE_CHANGELOG: "true"
+    SECURITY_STAGE: "auto"
 
 sdk-pipeline:
   extends:
diff --git a/scripts/exein-analyzer.sh b/scripts/exein-analyzer.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0bcef4cd33e624e9fd27272302836239d6672d6c
--- /dev/null
+++ b/scripts/exein-analyzer.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+
+EXEIN_API_PLATFORM_URL=$1
+EXEIN_API_KEY=$2
+AZURE_CONTAINER_NAME=$3
+AZURE_STORAGE_SAS_TOKEN=$4
+
+ListObject(){
+	COSMO_HTTP_PLATFORM_URL="$EXEIN_API_PLATFORM_URL" \
+	cosmo \
+	--api-key=$EXEIN_API_KEY \
+	object list
+}
+
+CreateObject(){
+	COSMO_HTTP_PLATFORM_URL="$EXEIN_API_PLATFORM_URL" \
+	cosmo \
+	--api-key=$EXEIN_API_KEY \
+	object new $1
+}
+
+TestReport(){
+	COSMO_HTTP_PLATFORM_URL="$EXEIN_API_PLATFORM_URL" \
+	cosmo \
+	--api-key=$EXEIN_API_KEY \
+	scan new \
+	--report $1 \
+	--output $2 \
+	$3 \
+	$4 \
+	linux \
+	info kernel cve-check password-hash hardening security-scan static-code crypto software-bom capability
+}
+
+# Get Link report
+link_report=$(ls | grep '^Link_report')
+echo "This is the Link report the job is analyzing: $link_report"
+
+# Get Azure  from Link report
+azure_path=$(grep '^http' "$link_report" | head -n 1 | cut -d '/' -f 5- | sed -n 's/\(.*\)\/seco_.*/\1/p')
+echo "This is the Azure Path for report deploy: ${azure_path}"
+
+# Create Analyzer Object name by first checking if it exists, otherwise reuse the existing one
+object_name="${CI_JOB_NAME#*-}"
+echo "This is the Exein Scan Object Name: ${object_name}"
+output=$(ListObject)
+
+# Check if the output contains the exact match
+if echo "$output" | grep -qw "$object_name"; then
+  echo "An Exein Object with the same name exists, reusing it: $object_name"
+  object_id=$(ListObject | grep ${object_name} | head -n 1 | cut -d '|' -f 2 | tr -d ' ')
+else
+  echo "No match found for the selected Exein Object Name, creating it: $object_name"
+  object_id=$(CreateObject ${object_name} | grep ${object_name} | head -n 1 | cut -d '|' -f 2 | tr -d ' ')
+fi
+
+echo "This is the Exein Object ID: ${object_id}"
+
+scan_report_cnt=0
+
+# Use grep to filter lines starting with 'http' of URL report and process each line
+while read -r url; do
+  echo "Processing software artifact at this link: $url"
+  file_name=$(basename "${url%%\?*}")
+  
+  # For the moment performing scan only on WIC image
+  if [[ "$file_name" == *wic.bz2* ]]; then
+    echo "Found WIC image on which to perform Exein Analyzer scan: $file_name"
+
+    # Download the file using curl
+    curl -o "${file_name}" "${url}"
+    bzip2 -d "$file_name"
+    file_name=$(echo "${file_name}" | sed 's/\.bz2//')
+
+    # Set report and output names
+    report_name=$(echo "$file_name" | sed 's/seco_/&exein-analyzer-report_/' | sed 's/\..*/.pdf/')
+    output_name=$(echo "$file_name" | sed 's/seco_/&exein-analyzer-output_/' | sed 's/\..*/.json/')
+
+    # Calculate the path to the file for the scan
+    file_path="${PWD}/${file_name}"
+
+    echo "This is the Exein Analyzer Report Name : $report_name"
+    echo "This is the Exein Analyzer Output Name : $output_name"
+    echo "This is the Exein Analyzer Object ID : $object_id"
+    echo "This is the Exein Analyzer File Path : $file_path"
+
+    # Call TestReport and check for errors
+    TestReport "$report_name" "$output_name" "$object_id" "$file_path"
+    if [ $? -eq 1 ]; then
+        echo "Error detected, exiting..."
+        exit 1
+    else
+      scan_report_cnt=$((scan_report_cnt + 1))
+    fi
+  else
+    echo "Not performing Exein Analyzer scan on SW artifacts files different from WIC images, skipping them: $file_name"
+    continue
+  fi
+done < <(grep '^http' "$link_report")
+
+echo "Final scan report count: $scan_report_cnt"
+
+# Exit if the shall_exit variable is set to 1
+if [ "$shall_exit" -eq 1 ]; then
+  exit 0
+fi
+
+# Search for at least one file creted by the scan
+report_file=$(find . -name "*output*image*json" -print -quit)
+
+# Check if any files were found and eventually create an archive
+if [ -z "$report_file" ]; then
+    echo "Error: No files matching '*output*json' found, no scans performed"
+    exit 1
+else
+    archive_id=$(echo "$report_file" | sed -E 's/.*_([0-9A-Za-z-]+_[0-9A-Za-z]+_[0-9]{8})\.json/\1/')
+    report_archive_name="seco_exein-analyzer_${object_name}_${archive_id}.tar.gz"
+    echo "This is the name of the archive report that will be created: ${report_archive_name}"
+    tar -czvf $report_archive_name *output*.json *report*.pdf
+fi
+
+# Upload the report to Azure at the same path of other artifacts in the link report
+echo "Uploading Exein report to Azure"
+az storage blob upload --account-name $AZURE_STORAGE_ACCOUNT \
+    --sas-token $AZURE_STORAGE_SAS_TOKEN \
+    --container-name $AZURE_CONTAINER_NAME \
+    --file $report_archive_name \
+    --name $azure_path/$report_archive_name \
+    --overwrite
+
+# Update link report with the Exein security analysis report
+current_year=$(date -u '+%Y')
+current_month=$(date -u '+%m')
+current_month=${current_month#0}
+future_month=$(( (current_month + 6) % 12 ))
+future_year=$(( current_year + (current_month + 6) / 12 ))
+
+# Adjust the year if the future month is 0
+if [ $future_month -eq 0 ]; then
+    future_month=12
+    future_year=$(( future_year - 1 ))
+fi
+
+# Format the expiration date
+expire_date="${future_year}-${future_month}-01T00:00Z"
+
+# Generate read-only SAS token and URL
+sas_report=$(az storage blob generate-sas --account-key $AZURE_STORAGE_KEY --container-name $AZURE_CONTAINER_NAME --name $azure_path/$report_archive_name --permissions r --expiry "$expire_date" --output tsv)
+url_report=$(az storage blob url --container-name $AZURE_CONTAINER_NAME --name $azure_path/$report_archive_name --output tsv | sed -E 's/\?s.*//')
+
+# Add link to the report
+case "$AZURE_CONTAINER_NAME" in
+    *"private"*)
+        eval echo -e "\$url_report?\$sas_report" >> ${link_report}
+      ;;
+    *)
+        eval echo -e "\$url_report" >> ${link_report}
+      ;;
+esac
+
+# Count the total number of Exein scan reports and eventually throw 
+# an error if the total number is lower than the expected one
+# We perform this check at the end because we want to upload 
+# something anyway since the failure is very probable with this
+# version of the Exein Analyzer
+file_count=$(find . -type f -name "*output*image*json" | wc -l)
+if [ "$file_count" -lt "$scan_report_cnt" ]; then
+  echo "Error: Found $file_count files, which is less than the expected $scan_report_cnt."
+  exit 1
+else
+  echo "Success: Found expected output $scan_report_cnt files."
+fi
\ No newline at end of file