diff --git a/.chainguard/source.yaml b/.chainguard/source.yaml
index 06eb69aa9..4f5bd2a8e 100644
--- a/.chainguard/source.yaml
+++ b/.chainguard/source.yaml
@@ -8,8 +8,11 @@ spec:
identities:
- subjectRegExp: .+@gmail.com$ # Allow commits signed with Gmail accounts, not ideal, for testing purposes only.
issuer: https://github.com/login/oauth
+ - issuer: https://token.actions.githubusercontent.com
+ # again, not ideal, for testing purposes only.
+ subjectRegExp: https://github.com/Pinguteca/eShop/.github/workflows/.+$
ctlog:
url: https://rekor.sigstore.dev
- key:
- # Allow commits signed by GitHub.
+ # Allow commits signed by GitHub, e.g. the UI
kms: https://github.com/web-flow.gpg
\ No newline at end of file
diff --git a/.github/actions.lock.json b/.github/actions.lock.json
index 9b566d94c..b35d08c8a 100644
--- a/.github/actions.lock.json
+++ b/.github/actions.lock.json
@@ -1,6 +1,6 @@
{
"version": 1,
- "generated": "2026-01-11T21:02:15.616Z",
+ "generated": "2026-02-01T23:20:49.722Z",
"actions": {
"step-security/harden-runner": [
{
@@ -24,6 +24,12 @@
"sha": "3e8a2a226fad9e1ecbf2d359b8a7697554a4ac6d",
"integrity": "sha256-696e56578f4dd56549ae896605cf415b78f3432bb0cbd42890a48975f84d3503",
"dependencies": []
+ },
+ {
+ "version": "18e5e3427cf9d6bcfbefe60dca48e40292f000c5",
+ "sha": "18e5e3427cf9d6bcfbefe60dca48e40292f000c5",
+ "integrity": "sha256-61939c587c8882b626cc98e46cfb424dfdc4f26cd9b9dd51721016cab919fba9",
+ "dependencies": []
}
],
"calibreapp/image-actions": [
@@ -94,11 +100,19 @@
"dependencies": []
}
],
- "gjtorikian/gh-actions-lockfile": [
+ "dorny/paths-filter": [
{
- "version": "24be31e941fc36df78f82762cd45c28b4cbcaedd",
- "sha": "24be31e941fc36df78f82762cd45c28b4cbcaedd",
- "integrity": "sha256-6a3612083812bfbf22ef48e284a93dbf82766dea50126f85d2160ae092018087",
+ "version": "de90cc6fb38fc0963ad72b210f1f284cd68cea36",
+ "sha": "de90cc6fb38fc0963ad72b210f1f284cd68cea36",
+ "integrity": "sha256-07c63522e75cd14669d8202d83e0823a170f8d05a89a488729eb60446f9f7af4",
+ "dependencies": []
+ }
+ ],
+ "pnpm/action-setup": [
+ {
+ "version": "41ff72655975bd51cab0327fa583b6e92b6d3061",
+ "sha": "41ff72655975bd51cab0327fa583b6e92b6d3061",
+ "integrity": "sha256-ff9db052ccf236e3d7520da06ef8fdd687abdb9b4c1b470031a96912fa937a68",
"dependencies": []
}
],
@@ -110,6 +124,14 @@
"dependencies": []
}
],
+ "gjtorikian/gh-actions-lockfile": [
+ {
+ "version": "24be31e941fc36df78f82762cd45c28b4cbcaedd",
+ "sha": "24be31e941fc36df78f82762cd45c28b4cbcaedd",
+ "integrity": "sha256-6a3612083812bfbf22ef48e284a93dbf82766dea50126f85d2160ae092018087",
+ "dependencies": []
+ }
+ ],
"actions/configure-pages": [
{
"version": "983d7736d9b0ae728b81ab479565c72886d7745b",
diff --git a/.github/workflows/img-optimization.yml b/.github/workflows/img-optimization.yml
index 214be3a22..60b4ddf35 100644
--- a/.github/workflows/img-optimization.yml
+++ b/.github/workflows/img-optimization.yml
@@ -5,7 +5,7 @@
# - at 11 PM every Sunday in anything gets missed with any of the above scenarios
# For Pull Requests, the images are added to the PR.
# For other scenarios, a new PR will be opened if any images are compressed.
-name: Compress images
+name: Strip metadata and optimize images with Calibre
on:
pull_request:
types: [reopened, ready_for_review, review_requested]
@@ -78,7 +78,7 @@ jobs:
with:
# For non-Pull Requests, run in compressOnly mode and we'll PR after.
compressOnly: ${{ github.event_name != 'pull_request' }}
- minPctChange: "10"
+ minPctChange: "0"
jpegQuality: "85"
jpegProgressive: true
pngQuality: "80"
diff --git a/.github/workflows/svg-optimization.yml b/.github/workflows/svg-optimization.yml
new file mode 100644
index 000000000..a4d512090
--- /dev/null
+++ b/.github/workflows/svg-optimization.yml
@@ -0,0 +1,102 @@
+name: Sanitize, strip metadata and optimize SVGs
+on:
+ pull_request:
+ types: [reopened, ready_for_review, review_requested]
+ branches: [master, main, develop]
+ paths:
+ - "**.svg"
+ - "svgo.config.mjs"
+ # TODO: adjust workflow for on-demand runs and scheduling
+ workflow_dispatch:
+ schedule:
+ - cron: "00 23 * * 0"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions: {}
+
+jobs:
+ svgOptimization:
+ name: Sanitize and optimize SVGs
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+
+ permissions:
+ contents: write # to write commits
+ id-token: write # Enable OIDC for Gitsign
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
+ with:
+ egress-policy: audit
+ disable-telemetry: "true"
+ disable-sudo: "true" # runs on container so same issue as zizmor
+ - name: Optimize Git config for CI
+ run: |
+ # Disable compression for faster network transfer.
+ git config --global core.compression 0
+ # Turn off fsync
+ git config --global core.fsync -all
+ - name: Checkout Branch
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ persist-credentials: true
+ sparse-checkout: |
+ **.svg
+ svgo.config.mjs
+ sparse-checkout-cone-mode: false
+ filter: "blob:none"
+ # TODO: skip for on-demand and scheduled runs
+ - name: Detect changed SVGs
+ uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
+ id: svg_changes
+ with:
+ # order for choosing the correct base ref:
+ # PR base branch (when running on pull_request)
+ # fallback to current branch name
+ base: ${{ github.event.pull_request.base.ref || github.ref_name }}
+ list-files: shell
+ # Filter only added or modified svg files
+ filters: |
+ svg:
+ - added|modified: '**/*.svg'
+ - name: Setup Gitsign for commit signing
+ if: steps.svg_changes.outputs.svg == 'true'
+ uses: chainguard-dev/actions/setup-gitsign@18e5e3427cf9d6bcfbefe60dca48e40292f000c5 # v1.5.13
+ - name: Install pnpm
+ if: steps.svg_changes.outputs.svg == 'true'
+ uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
+ with:
+ version: 10
+ standalone: false
+ - name: Use Node.js
+ if: steps.svg_changes.outputs.svg == 'true'
+ uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
+ with:
+ node-version: 24.12.0
+ - name: Install SVGO
+ if: steps.svg_changes.outputs.svg == 'true'
+ run: |
+ # 21 days in minutes to avoid installing potentially compromised versions
+ pnpm config set -g minimumReleaseAge 30240
+ pnpm install -g svgo@4
+ - name: Sanitize, strip metadata and optimize SVGs
+ if: steps.svg_changes.outputs.svg == 'true'
+ env:
+ STEPS_SVG_CHANGES_OUTPUTS_SVG_FILES: ${{ steps.svg_changes.outputs.svg_files }}
+ run: |
+ echo "### :white_check_mark: **SVGO optimization results**" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ svgo --no-color --multipass --final-newline --eol lf --config svgo.config.mjs ${STEPS_SVG_CHANGES_OUTPUTS_SVG_FILES} >> $GITHUB_STEP_SUMMARY
+ - uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
+ if: steps.svg_changes.outputs.svg == 'true'
+ with:
+ commit_message: |
+ chore(svg): sanitize and optimize SVGO
+
+ changed files: ${{ steps.svg_changes.outputs.svg_files }}
+ file_pattern: "**.svg"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5939c4d31..106e36013 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -62,3 +62,13 @@ repos:
pass_filenames: true
types: [png]
stages: ["pre-push"]
+ - repo: local
+ hooks:
+ - id: svgo
+ name: SVGO optimization
+ description: Optimize SVG files using SVGO.
+ language: node
+ entry: svgo
+ args: ["--multipass", "--quiet", "--config", "--final-newline", "--eol", "lf", "svgo.config.mjs"]
+ types: [svg]
+ stages: ["pre-push"]
diff --git a/ghalint.yaml b/ghalint.yaml
index 7200427e9..f944d7391 100644
--- a/ghalint.yaml
+++ b/ghalint.yaml
@@ -8,3 +8,6 @@ excludes:
- policy_name: checkout_persist_credentials_should_be_false
workflow_file_path: .github/workflows/verify-gha-integrity.yml
job_name: update-lockfile
+ - policy_name: checkout_persist_credentials_should_be_false
+ workflow_file_path: .github/workflows/svg-optimization.yml
+ job_name: svgOptimization
\ No newline at end of file
diff --git a/hush.svg b/hush.svg
new file mode 100644
index 000000000..a4343a0c7
--- /dev/null
+++ b/hush.svg
@@ -0,0 +1 @@
+
diff --git a/src/ClientApp/Resources/Images/test3.svg b/src/ClientApp/Resources/Images/test3.svg
new file mode 100644
index 000000000..28f974346
--- /dev/null
+++ b/src/ClientApp/Resources/Images/test3.svg
@@ -0,0 +1 @@
+
diff --git a/svgo.config.mjs b/svgo.config.mjs
new file mode 100644
index 000000000..c4caf7809
--- /dev/null
+++ b/svgo.config.mjs
@@ -0,0 +1,56 @@
+export default {
+ multipass: true,
+ js2svg: { pretty: false, indent: 0 },
+ plugins: [
+ {
+ name: 'preset-default',
+ params: {
+ overrides: {
+ },
+ },
+ },
+ // sanitze SVGs by removing potentially dangerous elements/attributes
+ // remove