Skip to content

Commit 1af84e3

Browse files
authored
Merge branch 'main' into dependabot/github_actions/dorny/paths-filter-4
2 parents 0252964 + 262c4d8 commit 1af84e3

87 files changed

Lines changed: 4287 additions & 1025 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/coverage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
5353
- name: Upload coverage report
5454
if: always()
55-
uses: actions/upload-artifact@v6
55+
uses: actions/upload-artifact@v7
5656
with:
5757
name: snapcraftio-coverage
5858
path: coverage

.github/workflows/pr.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ jobs:
194194
run: dotrun test-python-job
195195

196196
- name: Upload coverage to Codecov
197-
uses: codecov/codecov-action@v5
197+
uses: codecov/codecov-action@v6
198198
with:
199199
flags: python
200200

@@ -219,7 +219,7 @@ jobs:
219219
yarn test-js --coverage
220220
221221
- name: Upload coverage to Codecov
222-
uses: codecov/codecov-action@v5
222+
uses: codecov/codecov-action@v6
223223
with:
224224
flags: javascript
225225

package.json

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
"@babel/preset-env": "7.29.0",
2626
"@babel/preset-react": "7.28.5",
2727
"@babel/preset-typescript": "7.28.5",
28-
"@canonical/analytics-events": "1.0.4",
29-
"@canonical/cookie-policy": "3.8.4",
28+
"@canonical/analytics-events": "1.0.5",
29+
"@canonical/cookie-policy": "3.10.0",
3030
"@canonical/global-nav": "3.8.0",
31-
"@canonical/react-components": "3.12.1",
31+
"@canonical/react-components": "4.5.1",
3232
"@canonical/store-components": "0.55.0",
3333
"@dnd-kit/core": "6.3.1",
3434
"@dnd-kit/sortable": "10.0.0",
@@ -63,7 +63,7 @@
6363
"jotai": "2.17.1",
6464
"jotai-family": "1.0.1",
6565
"nanoid": "5.1.6",
66-
"postcss": "8.5.6",
66+
"postcss": "8.5.10",
6767
"postcss-cli": "11.0.1",
6868
"prettier": "3.8.1",
6969
"prop-types": "15.8.1",
@@ -86,21 +86,21 @@
8686
"tinyglobby": "0.2.15",
8787
"topojson-client": "3.1.0",
8888
"typescript": "5.9.3",
89-
"uuid": "13.0.0",
90-
"vanilla-framework": "4.44.0",
91-
"vite": "7.3.1"
89+
"uuid": "14.0.0",
90+
"vanilla-framework": "4.50.0",
91+
"vite": "7.3.2"
9292
},
9393
"devDependencies": {
9494
"@babel/eslint-parser": "7.28.6",
95-
"@percy/cli": "1.31.8",
95+
"@percy/cli": "1.31.10",
9696
"@types/cypress": "1.1.6",
9797
"@types/d3": "7.4.3",
9898
"@types/redux-mock-store": "^1.5.0",
9999
"@types/uuid": "11.0.0",
100-
"@typescript-eslint/eslint-plugin": "^8.19.1",
101-
"@typescript-eslint/parser": "^8.19.1",
102-
"@vitest/coverage-v8": "4.0.18",
103-
"@vitest/ui": "4.0.18",
100+
"@typescript-eslint/eslint-plugin": "^8.58.0",
101+
"@typescript-eslint/parser": "^8.58.0",
102+
"@vitest/coverage-v8": "4.1.3",
103+
"@vitest/ui": "4.1.3",
104104
"concurrently": "9.2.1",
105105
"cypress": "15.10.0",
106106
"eslint": "9.39.2",
@@ -109,13 +109,17 @@
109109
"eslint-plugin-prettier": "5.5.5",
110110
"eslint-plugin-react": "7.37.5",
111111
"msw": "2.12.8",
112-
"stylelint": "17.1.1",
112+
"stylelint": "17.6.0",
113113
"stylelint-config-standard-scss": "17.0.0",
114114
"stylelint-order": "7.0.1",
115-
"vitest": "^4.0.0"
115+
"vitest": "4.1.3"
116116
},
117117
"resolutions": {
118118
"vitest/**/glob": "13.0.1",
119-
"lodash": "4.17.23"
119+
"vitest/vite": "7.3.2",
120+
"lodash": "4.18.1",
121+
"picomatch": ">=2.3.2",
122+
"brace-expansion": ">=2.0.2",
123+
"@tootallnate/once": "3.0.1"
120124
}
121125
}

redirects.yaml

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -146,74 +146,74 @@ docs/gnome-3-34-extension/?: https://forum.snapcraft.io/t/the-gnome-3-34-extensi
146146
docs/snapcraft-interfaces/?: https://forum.snapcraft.io/t/supported-interfaces/7744
147147

148148
# Old URL redirects
149-
docs/build-snaps: /docs/snap-format
150-
docs/build-snaps/build-for-another-arch: /docs/building-snaps
151-
docs/build-snaps/build-on-lxd-docker: /docs/build-snaps-on-docker
152-
docs/build-snaps/builders: /docs/t/building-the-snap/6800
153-
docs/build-snaps/debugging: /docs/debugging-building-snaps
154-
docs/build-snaps/electron: /docs/electron-apps
155-
docs/build-snaps/go: /docs/go-applications
156-
docs/build-snaps/hooks: /docs/supported-snap-hooks
157-
docs/build-snaps/java: /docs/java-applications
158-
docs/build-snaps/languages: /docs/creating-a-snap
159-
docs/build-snaps/metadata: /docs/snapcraft-app-and-service-metadata
160-
docs/build-snaps/moos: /docs/moos-applications
161-
docs/build-snaps/node: /docs/node-apps
162-
docs/build-snaps/parts: /docs/remote-reusable-parts
163-
docs/build-snaps/plugins: /docs/snapcraft-plugin-api
164-
docs/build-snaps/pre-built: /docs/t/pre-built-apps/6739
165-
docs/build-snaps/publish: /docs/releasing-to-the-snap-store
166-
docs/build-snaps/register-snap: /docs/registering-your-app-name
167-
docs/build-snaps/release: /docs/releasing-your-app
168-
docs/build-snaps/remote-parts: /docs/remote-reusable-parts
169-
docs/build-snaps/ros: /docs/ros-applications
170-
docs/build-snaps/ros2: /docs/ros2-applications
171-
docs/build-snaps/ruby: /docs/ruby-applications
172-
docs/build-snaps/rust: /docs/rust-applications
173-
docs/build-snaps/syntax: /docs/snapcraft-yaml-reference
174-
docs/build-snaps/upload: /docs/releasing-your-app
175-
docs/build-snaps/your-first-snap: /docs/build-on-lxd
176-
docs/core: /docs/quickstart-tour
177-
docs/core/install-arch-linux: /docs/installing-snap-on-arch-linux
178-
docs/core/install-debian: /docs/installing-snap-on-debian
179-
docs/core/install-elementary-os: /docs/installing-snap-on-elementary-os
180-
docs/core/install-fedora: /docs/installing-snap-on-fedora
181-
docs/core/install-gentoo: /docs/installing-snapd
182-
docs/core/install-linux-mint: /docs/installing-snap-on-linux-mint
183-
docs/core/install-manjaro: /docs/installing-snap-on-manjaro-linux
184-
docs/core/install-oe-yocto: /docs/installing-snapd
185-
docs/core/install-opensuse: /docs/installing-snapd
186-
docs/core/install-openwrt: /docs/installing-snapd
187-
docs/core/install-raspbian: /docs/installing-snap-on-raspbian
188-
docs/core/install-solus: /docs/installing-snap-on-solus
189-
docs/core/install-ubuntu: /docs/installing-snap-on-ubuntu
190-
docs/core/install: /docs/installing-snapd
191-
docs/core/interfaces: /docs/interface-management
192-
docs/core/updates: /docs/keeping-snaps-up-to-date
193-
docs/core/usage: /docs/quickstart-tour
194-
docs/deprecation-notices/dn1: /docs/t/deprecation-notice-1/8397
195-
docs/deprecation-notices/dn2: /docs/t/deprecation-notice-2/8398
196-
docs/deprecation-notices/dn3: /docs/t/deprecation-notice-3/8403
197-
docs/deprecation-notices/dn4: /docs/t/deprecation-notice-4/8404
198-
docs/deprecation-notices/dn5: /docs/t/deprecation-notice-5/8405
199-
docs/deprecation-notices/dn6: /docs/t/deprecation-notice-6/8406
200-
docs/deprecation-notices/dn7: /docs/t/deprecation-notice-7/8407
201-
docs/deprecation-notices/dn8: /docs/t/deprecation-notice-8/8408
202-
docs/deprecation-notices/dn9: /docs/t/deprecation-notice-9/8409
203-
docs/reference/channels: /docs/channels
204-
docs/reference/confinement: /docs/snap-confinement
205-
docs/reference/env: /docs/environment-variables
206-
docs/reference/interfaces: /docs/interface-management
149+
docs/build-snaps/?: /docs/snap-format
150+
docs/build-snaps/build-for-another-arch/?: /docs/building-snaps
151+
docs/build-snaps/build-on-lxd-docker/?: /docs/build-snaps-on-docker
152+
docs/build-snaps/builders/?: /docs/t/building-the-snap/6800
153+
docs/build-snaps/debugging/?: /docs/debugging-building-snaps
154+
docs/build-snaps/electron/?: /docs/electron-apps
155+
docs/build-snaps/go/?: /docs/go-applications
156+
docs/build-snaps/hooks/?: /docs/supported-snap-hooks
157+
docs/build-snaps/java/?: /docs/java-applications
158+
docs/build-snaps/languages/?: /docs/creating-a-snap
159+
docs/build-snaps/metadata/?: /docs/snapcraft-app-and-service-metadata
160+
docs/build-snaps/moos/?: /docs/moos-applications
161+
docs/build-snaps/node/?: /docs/node-apps
162+
docs/build-snaps/parts/?: /docs/remote-reusable-parts
163+
docs/build-snaps/plugins/?: /docs/snapcraft-plugin-api
164+
docs/build-snaps/pre-built/?: /docs/t/pre-built-apps/6739
165+
docs/build-snaps/publish/?: /docs/releasing-to-the-snap-store
166+
docs/build-snaps/register-snap/?: /docs/registering-your-app-name
167+
docs/build-snaps/release/?: /docs/releasing-your-app
168+
docs/build-snaps/remote-parts/?: /docs/remote-reusable-parts
169+
docs/build-snaps/ros/?: /docs/ros-applications
170+
docs/build-snaps/ros2/?: /docs/ros2-applications
171+
docs/build-snaps/ruby/?: /docs/ruby-applications
172+
docs/build-snaps/rust/?: /docs/rust-applications
173+
docs/build-snaps/syntax/?: /docs/snapcraft-yaml-reference
174+
docs/build-snaps/upload/?: /docs/releasing-your-app
175+
docs/build-snaps/your-first-snap/?: /docs/build-on-lxd
176+
docs/core/?: /docs/quickstart-tour
177+
docs/core/install-arch-linux/?: /docs/installing-snap-on-arch-linux
178+
docs/core/install-debian/?: /docs/installing-snap-on-debian
179+
docs/core/install-elementary-os/?: /docs/installing-snap-on-elementary-os
180+
docs/core/install-fedora/?: /docs/installing-snap-on-fedora
181+
docs/core/install-gentoo/?: /docs/installing-snapd
182+
docs/core/install-linux-mint/?: /docs/installing-snap-on-linux-mint
183+
docs/core/install-manjaro/?: /docs/installing-snap-on-manjaro-linux
184+
docs/core/install-oe-yocto/?: /docs/installing-snapd
185+
docs/core/install-opensuse/?: /docs/installing-snapd
186+
docs/core/install-openwrt/?: /docs/installing-snapd
187+
docs/core/install-raspbian/?: /docs/installing-snap-on-raspbian
188+
docs/core/install-solus/?: /docs/installing-snap-on-solus
189+
docs/core/install-ubuntu/?: /docs/installing-snap-on-ubuntu
190+
docs/core/install/?: /docs/installing-snapd
191+
docs/core/interfaces/?: /docs/interface-management
192+
docs/core/updates/?: /docs/keeping-snaps-up-to-date
193+
docs/core/usage/?: /docs/quickstart-tour
194+
docs/deprecation-notices/dn1/?: /docs/t/deprecation-notice-1/8397
195+
docs/deprecation-notices/dn2/?: /docs/t/deprecation-notice-2/8398
196+
docs/deprecation-notices/dn3/?: /docs/t/deprecation-notice-3/8403
197+
docs/deprecation-notices/dn4/?: /docs/t/deprecation-notice-4/8404
198+
docs/deprecation-notices/dn5/?: /docs/t/deprecation-notice-5/8405
199+
docs/deprecation-notices/dn6/?: /docs/t/deprecation-notice-6/8406
200+
docs/deprecation-notices/dn7/?: /docs/t/deprecation-notice-7/8407
201+
docs/deprecation-notices/dn8/?: /docs/t/deprecation-notice-8/8408
202+
docs/deprecation-notices/dn9/?: /docs/t/deprecation-notice-9/8409
203+
docs/reference/channels/?: /docs/channels
204+
docs/reference/confinement/?: /docs/snap-confinement
205+
docs/reference/env/?: /docs/environment-variables
206+
docs/reference/interfaces/?: /docs/interface-management
207207
docs/reference/plugins.*: /docs/writing-local-plugins
208-
docs/snaps: /docs
209-
docs/snaps/intro: /docs/quickstart-tour
210-
docs/snaps/metadata: /docs/snap-format
211-
docs/snaps/structure: /docs/system-snap-directory
208+
docs/snaps/?: /docs
209+
docs/snaps/intro/?: /docs/quickstart-tour
210+
docs/snaps/metadata/?: /docs/snap-format
211+
docs/snaps/structure/?: /docs/system-snap-directory
212212
account/details: /admin/account
213213

214214
# Redirects webhook requests using old URL format (i.e. not starting with api/)
215215
(?P<snap_name>[^/]*)/webhook/notify: /api/{snap_name}/webhook/notify
216-
(?!api/.*)(?P<github_owner>[^/]*)/(?P<github_repo>[^/]*)/webhook/notify: /api/{github_owner}/{github_repo}/webhook/notify
216+
(?!api/.*)(?P<github_owner>[^/]*)/(?P<github_repo>[^/]*)/webhook/notify: /api/{github_owner}/{github_repo}/webhook/notify
217217

218218
# First snap flow pages
219219
first-snap/python: https://documentation.ubuntu.com/snapcraft/stable/how-to/integrations/craft-a-python-app/

requirements.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# App dependencies
2-
canonicalwebteam.flask-base==3.1.1
3-
canonicalwebteam.flask-vite==0.4.0
2+
canonicalwebteam.flask-base==3.1.2
3+
canonicalwebteam.flask-vite==0.5.1
44
canonicalwebteam.candid==0.9.0
5-
canonicalwebteam.discourse==7.1.1
5+
canonicalwebteam.discourse==7.2.0
66
canonicalwebteam.blog==6.8.4
77
canonicalwebteam.search==2.1.2
88
canonicalwebteam.image-template==1.9.0
9-
canonicalwebteam.store-api==7.3.10
9+
canonicalwebteam.store-api==7.8.3
1010
canonicalwebteam.launchpad==0.9.0
1111
django-openid-auth==0.17
1212
Flask-OpenID==1.3.1
@@ -20,17 +20,17 @@ pybadges==3.0.1
2020
pycountry==24.6.1
2121
pymacaroons==0.13.0
2222
python-dateutil==2.9.0.post0
23-
requests==2.32.5
23+
requests==2.33.0
2424
responses==0.22.0
2525
ruamel.yaml==0.18.5
2626
vcrpy-unittest==0.1.7
2727
user-agents==2.2.0
2828
dnspython==2.8.0
29-
werkzeug==2.3.8
29+
werkzeug==3.1.6
3030
sentry-sdk==2.52.0
3131

3232
# Development dependencies
3333
Flask-Testing==0.8.1
3434
freezegun==1.5.5
35-
black==26.1.0
35+
black==26.3.1
3636
flake8==7.3.0
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import declareGlobal from "../declare";
2+
3+
describe("declareGlobal", () => {
4+
afterEach(() => {
5+
// Clean up any globals we set
6+
delete (globalThis as Record<string, unknown>).testApp;
7+
});
8+
9+
test("sets a nested global value", () => {
10+
declareGlobal("testApp.foo", { bar: 1 });
11+
const g = globalThis as unknown as Record<
12+
string,
13+
Record<string, Record<string, number>>
14+
>;
15+
expect(g.testApp.foo.bar).toBe(1);
16+
});
17+
18+
test("merges overlapping paths", () => {
19+
declareGlobal("testApp.a", { x: 1 });
20+
declareGlobal("testApp.b", { y: 2 });
21+
const app = (globalThis as Record<string, unknown>).testApp as Record<
22+
string,
23+
unknown
24+
>;
25+
expect(app.a).toEqual({ x: 1 });
26+
expect(app.b).toEqual({ y: 2 });
27+
});
28+
29+
test("does not pollute Object.prototype via __proto__", () => {
30+
const payload = JSON.parse('{"__proto__": {"polluted": true}}');
31+
declareGlobal("testApp.merge", payload);
32+
expect(({} as Record<string, unknown>).polluted).toBeUndefined();
33+
});
34+
35+
test("does not pollute Object.prototype via constructor.prototype", () => {
36+
const payload = JSON.parse(
37+
'{"constructor": {"prototype": {"polluted": true}}}',
38+
);
39+
declareGlobal("testApp.merge2", payload);
40+
expect(({} as Record<string, unknown>).polluted).toBeUndefined();
41+
});
42+
});

static/js/libs/declare.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ function buildObjectFromPath(path: string, value: unknown): Mergeable {
1818

1919
function deepMerge(target: Mergeable, source: Mergeable, override: boolean) {
2020
for (const key of Object.keys(source)) {
21+
// Guard against prototype pollution (CodeQL js/prototype-pollution-utility)
22+
const isBlockedKey =
23+
key === "__proto__" || key === "constructor" || key === "prototype";
24+
const isInheritedProperty =
25+
!Object.prototype.hasOwnProperty.call(target, key) &&
26+
typeof target[key] !== "undefined";
27+
if (isBlockedKey || isInheritedProperty) {
28+
continue;
29+
}
2130
if (typeof target[key] === "object" && typeof source[key] === "object") {
2231
target[key] = deepMerge(
2332
target[key] as Mergeable,

static/js/public/distro-install.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import screenshots from "./snap-details/screenshots";
22
import videos from "./snap-details/videos";
33
import initExpandableArea from "./expandable-area";
44
import triggerEventWhenVisible from "./ga-scroll-event";
5+
import initCopyCommand from "./snap-details/copyCommand";
56
import declareGlobal from "../libs/declare";
7+
import { trackPageView } from "@canonical/analytics-events";
8+
9+
if (window.ANALYTICS_ENDPOINT) {
10+
trackPageView("snap_distro_install_page");
11+
}
12+
13+
initCopyCommand();
614

715
declareGlobal("snapcraft.public.distroInstall", {
816
screenshots,

0 commit comments

Comments
 (0)