Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e3b0429
feat(precommit): add prototype svgo hook
Jan 24, 2026
eb19d04
feat(precommit): add SVGO config file
Jan 25, 2026
bcd1892
feat(ci): add SVG optimization workflow
Jan 31, 2026
c6a6867
chore(ci): add SVG for workflow testing
Jan 31, 2026
a6712da
chore(ci): add SVGO config file to workflow checkout
Jan 31, 2026
5dc99b0
chore(ci): update SVG for workflow testing
Jan 31, 2026
32194ad
chore(svg): sanitize and optimize SVGO
Pinguladora Jan 31, 2026
58716d1
chore(ci): update Chainguard Enforce setup
Jan 31, 2026
5769fa7
chore(precommit): update SVGO setup
Jan 31, 2026
c816cba
chore(ci): add dorny path filter GHA
Jan 31, 2026
cc662b1
chore(ci): update SVGO for workflow testing
Jan 31, 2026
b40b845
chore(svg): sanitize and optimize SVGO
Pinguladora Jan 31, 2026
9f60edf
chore(ci): update SVGO workflow with individual processing
Jan 31, 2026
77ec8a3
chore(ci): add dummy SVGs for workflow testing
Jan 31, 2026
7177935
chore(svg): sanitize and optimize SVGO
Pinguladora Jan 31, 2026
56ed34c
chore(ci): update workflow title and commit msg
Feb 1, 2026
70dfb90
chore(ci): remove for loop in SVG processing
Feb 1, 2026
287005d
chore(ci): add SVGO results to job summary
Feb 1, 2026
1c60227
chore(ci): add dummy SVG for workflow testing
Feb 1, 2026
ae1e7ea
chore(svg): sanitize and optimize SVGO
Pinguladora Feb 1, 2026
2e24642
chore(ci): remove color from SVGO output
Feb 1, 2026
e181c82
chore(ci): add dummy SVG for workflow testing
Feb 1, 2026
59f0198
chore(svg): sanitize and optimize SVGO
Pinguladora Feb 1, 2026
c4f313a
chore(ci): remove ls step
Feb 1, 2026
5ea15e2
chore(ci): name gitsign step
Feb 1, 2026
c8e8d69
chore(ci): change to pnpm
Feb 1, 2026
bad26dc
chore(ci): add dummy SVG for workflow testing
Feb 1, 2026
a186b99
chore(ci): remove pnpm cache for Node
Feb 1, 2026
c989385
chore(ci): add dummy SVG for workflow testing
Feb 1, 2026
7e6e737
chore(svg): sanitize and optimize SVGO
Pinguladora Feb 1, 2026
6b5fe52
chore(ci): add ghalint exception
Feb 1, 2026
34139ea
chore(ci): follow Zizmor recommendation
Feb 1, 2026
40b04c8
chore(ci): rename SVG step
Feb 1, 2026
1e1c988
chore(ci): add dummy SVG for workflow testing
Feb 1, 2026
e5431be
chore(svg): sanitize and optimize SVGO
Pinguladora Feb 1, 2026
f75ac5f
chore(ci): adjust workflow for PRs
Feb 1, 2026
ef94de9
chore(calibre): change picture change percent
Feb 1, 2026
e049838
chore(ci): update actions lockfile
Pinguladora Feb 1, 2026
2384df7
chore(ci): add minimun release age
Feb 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .chainguard/source.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
32 changes: 27 additions & 5 deletions .github/actions.lock.json
Original file line number Diff line number Diff line change
@@ -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": [
{
Expand All @@ -24,6 +24,12 @@
"sha": "3e8a2a226fad9e1ecbf2d359b8a7697554a4ac6d",
"integrity": "sha256-696e56578f4dd56549ae896605cf415b78f3432bb0cbd42890a48975f84d3503",
"dependencies": []
},
{
"version": "18e5e3427cf9d6bcfbefe60dca48e40292f000c5",
"sha": "18e5e3427cf9d6bcfbefe60dca48e40292f000c5",
"integrity": "sha256-61939c587c8882b626cc98e46cfb424dfdc4f26cd9b9dd51721016cab919fba9",
"dependencies": []
}
],
"calibreapp/image-actions": [
Expand Down Expand Up @@ -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": []
}
],
Expand All @@ -110,6 +124,14 @@
"dependencies": []
}
],
"gjtorikian/gh-actions-lockfile": [
{
"version": "24be31e941fc36df78f82762cd45c28b4cbcaedd",
"sha": "24be31e941fc36df78f82762cd45c28b4cbcaedd",
"integrity": "sha256-6a3612083812bfbf22ef48e284a93dbf82766dea50126f85d2160ae092018087",
"dependencies": []
}
],
"actions/configure-pages": [
{
"version": "983d7736d9b0ae728b81ab479565c72886d7745b",
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/img-optimization.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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"
Expand Down
102 changes: 102 additions & 0 deletions .github/workflows/svg-optimization.yml
Original file line number Diff line number Diff line change
@@ -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"
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
3 changes: 3 additions & 0 deletions ghalint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions hush.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/ClientApp/Resources/Images/test3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions svgo.config.mjs
Original file line number Diff line number Diff line change
@@ -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 <style> blocks
'removeStyleElement',
// remove <script>, SVG event attributes, inline event handlers, and script-URIs from links
'removeScripts',
'removeRasterImages',
// remove attributes whose value starts with "javascript:" (links, hrefs, xlink:href)
// The value regex checks for javascript: URI schemes (case-insensitive)
{
name: 'removeAttrs',
params: {
attrs: ['*:*:^javascript:\\s*:', '*:*:^\\s*data:text/html'], // drop JS URIs and inline HTML data URIs
},
},
{
name: 'removeAttributesBySelector',
params: {
selectors: [
// remove href/xlink:href from <a> or <image> elements which may point to tracking resources
{ selector: 'a', attributes: ['href', 'xlink:href'] },
{ selector: 'image', attributes: ['href', 'xlink:href'] },
],
},
},
// SVGO does not have a built-in remove element by tag
// plugin: remove any <foreignObject> elements entirely
{
name: 'removeForeignObject',
fn: () => {
return {
element: {
enter: (node, parentNode) => {
if (!parentNode) return;
const nodeName = (node.name || '').toLowerCase();
if (nodeName === 'foreignobject') {
// remove node from parent's children array
parentNode.children = parentNode.children.filter((c) => c !== node);
}
},
},
};
},
},
],
};
1 change: 1 addition & 0 deletions test1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.