diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json index 6c1c237ecd..c33e5537b8 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json @@ -22,7 +22,7 @@ "@types/react-dom": "^18.2.6", "@vitejs/plugin-react": "^4.0.1", "dotenv": "^16.3.1", - "sass": "^1.63.6", + "sass": "^1.81.0", "typescript": "^5.1.3", "vite": "^4.3.9", "vite-plugin-environment": "^1.1.3" diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/public/.ic-assets.json5 b/src/dfx/assets/project_templates/react/src/__frontend_name__/public/.ic-assets.json5 index 9ec78c35d2..a9e3a76251 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/public/.ic-assets.json5 +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/public/.ic-assets.json5 @@ -1,6 +1,8 @@ [ { "match": "**/*", + "security_policy": "standard", + "disable_security_policy_warning": true, "headers": { // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. // We recommend tightening the CSP for your specific application. Some recommendations are as follows: @@ -21,7 +23,7 @@ // Notes about the CSP below: // - We added img-src data: because data: images are used often. // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. - "Content-Security-Policy": "default-src 'self';script-src 'self';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-inline';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';", // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. // To configure permissions go here https://www.permissionspolicy.com/ diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/public/logo2.svg b/src/dfx/assets/project_templates/react/src/__frontend_name__/public/logo.svg similarity index 100% rename from src/dfx/assets/project_templates/react/src/__frontend_name__/public/logo2.svg rename to src/dfx/assets/project_templates/react/src/__frontend_name__/public/logo.svg diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/src/App.jsx b/src/dfx/assets/project_templates/react/src/__frontend_name__/src/App.jsx index 844e6b7e1a..6b59e947d9 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/src/App.jsx +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/src/App.jsx @@ -4,26 +4,25 @@ import { __backend_name_ident__ } from 'declarations/__backend_name__'; function App() { const [greeting, setGreeting] = useState(''); - function handleSubmit(event) { + async function handleSubmit(event) { event.preventDefault(); const name = event.target.elements.name.value; - __backend_name_ident__.greet(name).then((greeting) => { - setGreeting(greeting); - }); + const greeting = await __backend_name_ident__.greet(name); + setGreeting(greeting); return false; } return ( -
- DFINITY logo -
-
-
- - - -
-
{greeting}
+
+
+

{greeting}

+
+ + + +
+ DFINITY logo +
); } diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/src/index.scss b/src/dfx/assets/project_templates/react/src/__frontend_name__/src/index.scss index 283585fb27..a7fe4d643e 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/src/index.scss +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/src/index.scss @@ -1,39 +1,100 @@ -body { - font-family: sans-serif; - font-size: 1.5rem; +:root { + --vc-brand-gradient: radial-gradient(at 100% 100%, hsla(265, 80%, 75%, 1) 0px, transparent 50%), radial-gradient(at 100% 0%, hsla(272, 86%, 55%, 1) 0px, transparent 50%), radial-gradient(at 0% 18%, hsla(223, 79%, 66%, 1) 0px, transparent 50%); + --vc-brand-purple: #4b19d6; + + --rc-background-image--highlight: var(--vc-brand-gradient); + --rc-background--highlight: var(--vc-brand-purple); + + --rc-interaction-color: #4b19d6; + --rc-interaction-color-hover: #3c14aa; } -img { - max-width: 50vw; - max-height: 25vw; - display: block; - margin: auto; +body, html { + margin: 0; + padding: 0; + font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + height: 100%; } -form { +#root { + height: 100%; +} + +.container { + min-height: 100%; display: flex; justify-content: center; - gap: 0.5em; - flex-flow: row wrap; - max-width: 40vw; - margin: auto; - align-items: baseline; + align-items: center; + background-image: var(--rc-background-image--highlight); + background-color: var(--rc-background--highlight); } -button[type="submit"] { - padding: 5px 20px; - margin: 10px auto; - float: right; +.card { + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 320px; + display: flex; + flex-direction: column; + gap: 2rem; + align-items: center; } -#greeting { - margin: 10px auto; - padding: 10px 60px; - border: 1px solid #222; +.logo { + text-align: center; + margin-bottom: 1rem; } -#greeting:empty { - display: none; +.logo svg { + width: 100px; + height: auto; +} + +h1 { + color: #333; + text-align: center; + margin: 0 0 1rem; + word-wrap: break-word; + white-space: pre-wrap; + width: 100%; + + &:empty { + display: none; + } +} + +form { + display: flex; + flex-direction: column; + width: 100%; +} + +label { + margin-bottom: 0.5rem; + color: #333; +} + +input { + padding: 0.5rem; + margin-bottom: 1rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + padding: 0.5rem; + background-color: var(--rc-interaction-color); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s; +} + +button:hover { + background-color: var(--rc-interaction-color-hover); } .browser-warning { diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js index 9755aa9919..dff5523a0d 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js @@ -13,29 +13,37 @@ export default defineConfig({ optimizeDeps: { esbuildOptions: { define: { - global: "globalThis", + global: 'globalThis', }, }, }, server: { proxy: { - "/api": { - target: "http://127.0.0.1:4943", + '/api': { + target: 'http://127.0.0.1:4943', changeOrigin: true, }, }, }, plugins: [ react(), - environment("all", { prefix: "CANISTER_" }), - environment("all", { prefix: "DFX_" }), + environment('all', { prefix: 'CANISTER_' }), + environment('all', { prefix: 'DFX_' }), ], + css: { + preprocessorOptions: { + scss: { + api: 'modern-compiler', + silenceDeprecations: ['legacy-js-api'], + }, + }, + }, resolve: { alias: [ { - find: "declarations", + find: 'declarations', replacement: fileURLToPath( - new URL("../declarations", import.meta.url) + new URL('../declarations', import.meta.url), ), }, ], diff --git a/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/src/tests/App.test.jsx b/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/src/tests/App.test.jsx index f41d9a9e56..a2950b0fac 100644 --- a/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/src/tests/App.test.jsx +++ b/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/src/tests/App.test.jsx @@ -10,7 +10,7 @@ describe('App', () => { , ); - expect(document.body.innerHTML).toMatchInlineSnapshot('"
DFINITY logo

"'); + expect(document.body.innerHTML).toMatchInlineSnapshot('"

DFINITY logo
"'); expect(1).toEqual(1); }); }); diff --git a/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/vite.config.js.patch b/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/vite.config.js.patch index d8d55951d1..5766485c5c 100644 --- a/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/vite.config.js.patch +++ b/src/dfx/assets/project_templates/react_tests/src/__frontend_name__/vite.config.js.patch @@ -3,7 +3,7 @@ @@ -1,1 +1,2 @@ +/// import { fileURLToPath, URL } from 'url'; -@@ -33,1 +33,5 @@ +@@ -41,1 +41,5 @@ + test: { + environment: 'jsdom', + setupFiles: 'src/setupTests.js', diff --git a/src/dfx/assets/project_templates/simple_assets/src/__frontend_name__/assets/.ic-assets.json5 b/src/dfx/assets/project_templates/simple_assets/src/__frontend_name__/assets/.ic-assets.json5 index c74c1e1181..a9e3a76251 100644 --- a/src/dfx/assets/project_templates/simple_assets/src/__frontend_name__/assets/.ic-assets.json5 +++ b/src/dfx/assets/project_templates/simple_assets/src/__frontend_name__/assets/.ic-assets.json5 @@ -1,18 +1,54 @@ [ { "match": "**/*", - - // Provides a base set of security headers that will work for most dapps. - // Any headers you manually specify will override the headers provided by the policy. - // See 'dfx info security-policy' to see the policy and for advice on how to harden the headers. - // Once you improved the headers for your dapp, set the security policy to "hardened" to disable the warning. - // Options are: "hardened" | "standard" | "disabled". "security_policy": "standard", + "disable_security_policy_warning": true, + "headers": { + // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. + // We recommend tightening the CSP for your specific application. Some recommendations are as follows: + // - Use the CSP Evaluator (https://csp-evaluator.withgoogle.com/) to validate the CSP you define. + // - Follow the “Strict CSP” recommendations (https://csp.withgoogle.com/docs/strict-csp.html). However, note that in the context of the IC, + // nonces cannot be used because the response bodies must be static to work well with HTTP asset certification. + // Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described + // in https://csp.withgoogle.com/docs/faq.html in section “What if my site is static and I can't add nonces to scripts?”. + // See for example the II CSP (https://github.com/dfinity/internet-identity/blob/main/src/internet_identity/src/http.rs). + // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can + // make requests to https://*.icp0.io, hence being able to call any canister via https://icp0.io/api/v2/canister/{canister-ID}. + // This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data. + // The developer can configure this policy to only allow requests to their specific canisters, + // e.g: connect-src 'self' https://icp-api.io/api/v2/canister/{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa + // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use + // instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: https://fonts.googleapis.com or other CDNs) + + // Notes about the CSP below: + // - We added img-src data: because data: images are used often. + // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-inline';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';", + + // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. + // To configure permissions go here https://www.permissionspolicy.com/ + "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", + + // Security: Mitigates clickjacking attacks. + // See: https://owasp.org/www-community/attacks/Clickjacking. + "X-Frame-Options": "DENY", + + // Security: Avoids forwarding referrer information to other origins. + // See: https://owasp.org/www-project-secure-headers/#referrer-policy. + "Referrer-Policy": "same-origin", + + // Security: Tells the user’s browser that it must always use HTTPS with your site. + // See: https://owasp.org/www-project-secure-headers/#http-strict-transport-security + "Strict-Transport-Security": "max-age=31536000; includeSubDomains", - // Uncomment to disable the warning about using the - // standard security policy, if you understand the risk - // "disable_security_policy_warning": true, + // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header. + // See: https://owasp.org/www-project-secure-headers/#x-content-type-options + "X-Content-Type-Options": "nosniff", + // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block. + // See: https://owasp.org/www-community/attacks/xss/ + "X-XSS-Protection": "1; mode=block" + }, // Uncomment to redirect all requests from .raw.icp0.io to .icp0.io // "allow_raw_access": false }, diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json index 43a79c3630..52d15c8dd3 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json @@ -20,7 +20,7 @@ "@sveltejs/kit": "^2.5.26", "@sveltejs/vite-plugin-svelte": "^3.1.2", "dotenv": "^16.3.1", - "sass": "^1.63.6", + "sass": "^1.81.0", "svelte": "^4.2.19", "svelte-check": "^4.0.1", "typescript": "^5.6.2", diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/index.scss b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/index.scss index 283585fb27..a7fe4d643e 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/index.scss +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/index.scss @@ -1,39 +1,100 @@ -body { - font-family: sans-serif; - font-size: 1.5rem; +:root { + --vc-brand-gradient: radial-gradient(at 100% 100%, hsla(265, 80%, 75%, 1) 0px, transparent 50%), radial-gradient(at 100% 0%, hsla(272, 86%, 55%, 1) 0px, transparent 50%), radial-gradient(at 0% 18%, hsla(223, 79%, 66%, 1) 0px, transparent 50%); + --vc-brand-purple: #4b19d6; + + --rc-background-image--highlight: var(--vc-brand-gradient); + --rc-background--highlight: var(--vc-brand-purple); + + --rc-interaction-color: #4b19d6; + --rc-interaction-color-hover: #3c14aa; } -img { - max-width: 50vw; - max-height: 25vw; - display: block; - margin: auto; +body, html { + margin: 0; + padding: 0; + font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + height: 100%; } -form { +#root { + height: 100%; +} + +.container { + min-height: 100%; display: flex; justify-content: center; - gap: 0.5em; - flex-flow: row wrap; - max-width: 40vw; - margin: auto; - align-items: baseline; + align-items: center; + background-image: var(--rc-background-image--highlight); + background-color: var(--rc-background--highlight); } -button[type="submit"] { - padding: 5px 20px; - margin: 10px auto; - float: right; +.card { + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 320px; + display: flex; + flex-direction: column; + gap: 2rem; + align-items: center; } -#greeting { - margin: 10px auto; - padding: 10px 60px; - border: 1px solid #222; +.logo { + text-align: center; + margin-bottom: 1rem; } -#greeting:empty { - display: none; +.logo svg { + width: 100px; + height: auto; +} + +h1 { + color: #333; + text-align: center; + margin: 0 0 1rem; + word-wrap: break-word; + white-space: pre-wrap; + width: 100%; + + &:empty { + display: none; + } +} + +form { + display: flex; + flex-direction: column; + width: 100%; +} + +label { + margin-bottom: 0.5rem; + color: #333; +} + +input { + padding: 0.5rem; + margin-bottom: 1rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + padding: 0.5rem; + background-color: var(--rc-interaction-color); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s; +} + +button:hover { + background-color: var(--rc-interaction-color-hover); } .browser-warning { diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/routes/+page.svelte b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/routes/+page.svelte index 472a02ea70..0094d978a6 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/routes/+page.svelte +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/src/routes/+page.svelte @@ -4,23 +4,21 @@ let greeting = ""; - function onSubmit(event) { + async function onSubmit(event) { const name = event.target.name.value; - backend.greet(name).then((response) => { - greeting = response; - }); + greeting = await backend.greet(name); return false; } -
- DFINITY logo -
-
-
- - - -
-
{greeting}
+
+
+

{greeting}

+
+ + + +
+ DFINITY logo +
diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/.ic-assets.json5 b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/.ic-assets.json5 index e809af2e48..a9e3a76251 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/.ic-assets.json5 +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/.ic-assets.json5 @@ -1,22 +1,54 @@ [ { "match": "**/*", - - // Provides a base set of security headers that will work for most dapps. - // Any headers you manually specify will override the headers provided by the policy. - // See 'dfx info security-policy' to see the policy and for advice on how to harden the headers. - // Once you improved the headers for your dapp, set the security policy to "hardened" to disable the warning. - // Options are: "hardened" | "standard" | "disabled". "security_policy": "standard", - + "disable_security_policy_warning": true, "headers": { - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval' 'unsafe-inline';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", - }, + // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. + // We recommend tightening the CSP for your specific application. Some recommendations are as follows: + // - Use the CSP Evaluator (https://csp-evaluator.withgoogle.com/) to validate the CSP you define. + // - Follow the “Strict CSP” recommendations (https://csp.withgoogle.com/docs/strict-csp.html). However, note that in the context of the IC, + // nonces cannot be used because the response bodies must be static to work well with HTTP asset certification. + // Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described + // in https://csp.withgoogle.com/docs/faq.html in section “What if my site is static and I can't add nonces to scripts?”. + // See for example the II CSP (https://github.com/dfinity/internet-identity/blob/main/src/internet_identity/src/http.rs). + // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can + // make requests to https://*.icp0.io, hence being able to call any canister via https://icp0.io/api/v2/canister/{canister-ID}. + // This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data. + // The developer can configure this policy to only allow requests to their specific canisters, + // e.g: connect-src 'self' https://icp-api.io/api/v2/canister/{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa + // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use + // instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: https://fonts.googleapis.com or other CDNs) + + // Notes about the CSP below: + // - We added img-src data: because data: images are used often. + // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-inline';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';", + + // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. + // To configure permissions go here https://www.permissionspolicy.com/ + "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", - // Uncomment to disable the warning about using the - // standard security policy, if you understand the risk - // "disable_security_policy_warning": true, + // Security: Mitigates clickjacking attacks. + // See: https://owasp.org/www-community/attacks/Clickjacking. + "X-Frame-Options": "DENY", + // Security: Avoids forwarding referrer information to other origins. + // See: https://owasp.org/www-project-secure-headers/#referrer-policy. + "Referrer-Policy": "same-origin", + + // Security: Tells the user’s browser that it must always use HTTPS with your site. + // See: https://owasp.org/www-project-secure-headers/#http-strict-transport-security + "Strict-Transport-Security": "max-age=31536000; includeSubDomains", + + // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header. + // See: https://owasp.org/www-project-secure-headers/#x-content-type-options + "X-Content-Type-Options": "nosniff", + + // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block. + // See: https://owasp.org/www-community/attacks/xss/ + "X-XSS-Protection": "1; mode=block" + }, // Uncomment to redirect all requests from .raw.icp0.io to .icp0.io // "allow_raw_access": false }, diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/logo2.svg b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/logo.svg similarity index 100% rename from src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/logo2.svg rename to src/dfx/assets/project_templates/svelte/src/__frontend_name__/static/logo.svg diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js index 70d491d6cd..d1c348951a 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js @@ -30,6 +30,14 @@ export default defineConfig({ environment("all", { prefix: "CANISTER_" }), environment("all", { prefix: "DFX_" }), ], + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler", + silenceDeprecations: ["legacy-js-api"], + }, + }, + }, resolve: { alias: [ { diff --git a/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/src/tests/App.test.js b/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/src/tests/App.test.js index 07d4b79a8c..1d5050d7c4 100644 --- a/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/src/tests/App.test.js +++ b/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/src/tests/App.test.js @@ -14,6 +14,6 @@ test('mount component', async () => { const instance = new App({ target: host, props: {} }); expect(instance).toBeTruthy(); expect(host.innerHTML).toMatchInlineSnapshot( - '"
DFINITY logo

"' + '"

DFINITY logo
"' ); }); diff --git a/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/vite.config.js.patch b/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/vite.config.js.patch index d8d55951d1..5766485c5c 100644 --- a/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/vite.config.js.patch +++ b/src/dfx/assets/project_templates/svelte_tests/src/__frontend_name__/vite.config.js.patch @@ -3,7 +3,7 @@ @@ -1,1 +1,2 @@ +/// import { fileURLToPath, URL } from 'url'; -@@ -33,1 +33,5 @@ +@@ -41,1 +41,5 @@ + test: { + environment: 'jsdom', + setupFiles: 'src/setupTests.js', diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/assets/.ic-assets.json5 b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/assets/.ic-assets.json5 index c74c1e1181..a9e3a76251 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/assets/.ic-assets.json5 +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/assets/.ic-assets.json5 @@ -1,18 +1,54 @@ [ { "match": "**/*", - - // Provides a base set of security headers that will work for most dapps. - // Any headers you manually specify will override the headers provided by the policy. - // See 'dfx info security-policy' to see the policy and for advice on how to harden the headers. - // Once you improved the headers for your dapp, set the security policy to "hardened" to disable the warning. - // Options are: "hardened" | "standard" | "disabled". "security_policy": "standard", + "disable_security_policy_warning": true, + "headers": { + // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. + // We recommend tightening the CSP for your specific application. Some recommendations are as follows: + // - Use the CSP Evaluator (https://csp-evaluator.withgoogle.com/) to validate the CSP you define. + // - Follow the “Strict CSP” recommendations (https://csp.withgoogle.com/docs/strict-csp.html). However, note that in the context of the IC, + // nonces cannot be used because the response bodies must be static to work well with HTTP asset certification. + // Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described + // in https://csp.withgoogle.com/docs/faq.html in section “What if my site is static and I can't add nonces to scripts?”. + // See for example the II CSP (https://github.com/dfinity/internet-identity/blob/main/src/internet_identity/src/http.rs). + // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can + // make requests to https://*.icp0.io, hence being able to call any canister via https://icp0.io/api/v2/canister/{canister-ID}. + // This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data. + // The developer can configure this policy to only allow requests to their specific canisters, + // e.g: connect-src 'self' https://icp-api.io/api/v2/canister/{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa + // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use + // instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: https://fonts.googleapis.com or other CDNs) + + // Notes about the CSP below: + // - We added img-src data: because data: images are used often. + // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-inline';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';", + + // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. + // To configure permissions go here https://www.permissionspolicy.com/ + "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", + + // Security: Mitigates clickjacking attacks. + // See: https://owasp.org/www-community/attacks/Clickjacking. + "X-Frame-Options": "DENY", + + // Security: Avoids forwarding referrer information to other origins. + // See: https://owasp.org/www-project-secure-headers/#referrer-policy. + "Referrer-Policy": "same-origin", + + // Security: Tells the user’s browser that it must always use HTTPS with your site. + // See: https://owasp.org/www-project-secure-headers/#http-strict-transport-security + "Strict-Transport-Security": "max-age=31536000; includeSubDomains", - // Uncomment to disable the warning about using the - // standard security policy, if you understand the risk - // "disable_security_policy_warning": true, + // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header. + // See: https://owasp.org/www-project-secure-headers/#x-content-type-options + "X-Content-Type-Options": "nosniff", + // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block. + // See: https://owasp.org/www-community/attacks/xss/ + "X-XSS-Protection": "1; mode=block" + }, // Uncomment to redirect all requests from .raw.icp0.io to .icp0.io // "allow_raw_access": false }, diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json index 36d9b2fef0..a4681231f5 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json @@ -14,7 +14,7 @@ "@testing-library/jest-dom": "^5.16.5", "cross-fetch": "^3.1.6", "dotenv": "^16.3.1", - "sass": "^1.63.6", + "sass": "^1.81.0", "typescript": "^5.1.3", "vite": "^4.3.9", "vite-plugin-environment": "^1.1.3", diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/App.js b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/App.js index 4839411ce4..36d30e5308 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/App.js +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/App.js @@ -1,9 +1,9 @@ import { html, render } from 'lit-html'; import { __backend_name_ident__ } from 'declarations/__backend_name__'; -import logo from './logo2.svg'; +import logo from './logo.svg'; class App { - greeting = ''; + greeting = ""; constructor() { this.#render(); @@ -11,29 +11,29 @@ class App { #handleSubmit = async (e) => { e.preventDefault(); - const name = document.getElementById('name').value; + const name = document.getElementById("name").value; this.greeting = await __backend_name_ident__.greet(name); this.#render(); }; #render() { let body = html` -
- DFINITY logo -
-
-
- - - -
-
${this.greeting}
-
+
+
+

${this.greeting}

+
+ + + +
+ DFINITY logo +
+
`; - render(body, document.getElementById('root')); + render(body, document.getElementById("root")); document - .querySelector('form') - .addEventListener('submit', this.#handleSubmit); + .querySelector("form") + .addEventListener("submit", this.#handleSubmit); } } diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/index.scss b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/index.scss index 283585fb27..a7fe4d643e 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/index.scss +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/index.scss @@ -1,39 +1,100 @@ -body { - font-family: sans-serif; - font-size: 1.5rem; +:root { + --vc-brand-gradient: radial-gradient(at 100% 100%, hsla(265, 80%, 75%, 1) 0px, transparent 50%), radial-gradient(at 100% 0%, hsla(272, 86%, 55%, 1) 0px, transparent 50%), radial-gradient(at 0% 18%, hsla(223, 79%, 66%, 1) 0px, transparent 50%); + --vc-brand-purple: #4b19d6; + + --rc-background-image--highlight: var(--vc-brand-gradient); + --rc-background--highlight: var(--vc-brand-purple); + + --rc-interaction-color: #4b19d6; + --rc-interaction-color-hover: #3c14aa; } -img { - max-width: 50vw; - max-height: 25vw; - display: block; - margin: auto; +body, html { + margin: 0; + padding: 0; + font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + height: 100%; } -form { +#root { + height: 100%; +} + +.container { + min-height: 100%; display: flex; justify-content: center; - gap: 0.5em; - flex-flow: row wrap; - max-width: 40vw; - margin: auto; - align-items: baseline; + align-items: center; + background-image: var(--rc-background-image--highlight); + background-color: var(--rc-background--highlight); } -button[type="submit"] { - padding: 5px 20px; - margin: 10px auto; - float: right; +.card { + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 320px; + display: flex; + flex-direction: column; + gap: 2rem; + align-items: center; } -#greeting { - margin: 10px auto; - padding: 10px 60px; - border: 1px solid #222; +.logo { + text-align: center; + margin-bottom: 1rem; } -#greeting:empty { - display: none; +.logo svg { + width: 100px; + height: auto; +} + +h1 { + color: #333; + text-align: center; + margin: 0 0 1rem; + word-wrap: break-word; + white-space: pre-wrap; + width: 100%; + + &:empty { + display: none; + } +} + +form { + display: flex; + flex-direction: column; + width: 100%; +} + +label { + margin-bottom: 0.5rem; + color: #333; +} + +input { + padding: 0.5rem; + margin-bottom: 1rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + padding: 0.5rem; + background-color: var(--rc-interaction-color); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s; +} + +button:hover { + background-color: var(--rc-interaction-color-hover); } .browser-warning { diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/logo2.svg b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/logo.svg similarity index 100% rename from src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/logo2.svg rename to src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/src/logo.svg diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js index 9d6f44396e..3a49377799 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js @@ -12,32 +12,37 @@ export default defineConfig({ optimizeDeps: { esbuildOptions: { define: { - global: "globalThis", + global: 'globalThis', }, }, }, server: { proxy: { - "/api": { - target: "http://127.0.0.1:4943", + '/api': { + target: 'http://127.0.0.1:4943', changeOrigin: true, }, }, }, - publicDir: "assets", + publicDir: 'assets', plugins: [ - environment("all", { prefix: "CANISTER_" }), - environment("all", { prefix: "DFX_" }), + environment('all', { prefix: 'CANISTER_' }), + environment('all', { prefix: 'DFX_' }), ], + css: { + preprocessorOptions: { + scss: { + api: 'modern-compiler', + silenceDeprecations: ['legacy-js-api'], + }, + }, + }, resolve: { alias: [ { - find: "declarations", - replacement: fileURLToPath( - new URL("../declarations", import.meta.url) - ), + find: 'declarations', + replacement: fileURLToPath(new URL('../declarations', import.meta.url)), }, ], - dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/vanilla_js_tests/src/__frontend_name__/vite.config.js.patch b/src/dfx/assets/project_templates/vanilla_js_tests/src/__frontend_name__/vite.config.js.patch index 60a9e7fd86..4bdb037ee1 100644 --- a/src/dfx/assets/project_templates/vanilla_js_tests/src/__frontend_name__/vite.config.js.patch +++ b/src/dfx/assets/project_templates/vanilla_js_tests/src/__frontend_name__/vite.config.js.patch @@ -3,7 +3,7 @@ @@ -1,1 +1,2 @@ +/// import { defineConfig } from 'vite'; -@@ -32,1 +32,5 @@ +@@ -40,1 +40,5 @@ + test: { + environment: 'jsdom', + setupFiles: 'src/setupTests.js', diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json index 8367abd0d5..cd27815935 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json @@ -15,7 +15,7 @@ "@vue/tsconfig": "^0.4.0", "dotenv": "^16.3.1", "prettier": "^2.8.8", - "sass": "^1.63.6", + "sass": "^1.81.0", "typescript": "^5.1.3", "vite": "^4.3.9", "vite-plugin-environment": "^1.1.3" diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/public/.ic-assets.json5 b/src/dfx/assets/project_templates/vue/src/__frontend_name__/public/.ic-assets.json5 index c74c1e1181..a9e3a76251 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/public/.ic-assets.json5 +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/public/.ic-assets.json5 @@ -1,18 +1,54 @@ [ { "match": "**/*", - - // Provides a base set of security headers that will work for most dapps. - // Any headers you manually specify will override the headers provided by the policy. - // See 'dfx info security-policy' to see the policy and for advice on how to harden the headers. - // Once you improved the headers for your dapp, set the security policy to "hardened" to disable the warning. - // Options are: "hardened" | "standard" | "disabled". "security_policy": "standard", + "disable_security_policy_warning": true, + "headers": { + // Security: The Content Security Policy (CSP) given below aims at working with many apps rather than providing maximal security. + // We recommend tightening the CSP for your specific application. Some recommendations are as follows: + // - Use the CSP Evaluator (https://csp-evaluator.withgoogle.com/) to validate the CSP you define. + // - Follow the “Strict CSP” recommendations (https://csp.withgoogle.com/docs/strict-csp.html). However, note that in the context of the IC, + // nonces cannot be used because the response bodies must be static to work well with HTTP asset certification. + // Thus, we recommend to include script hashes (in combination with strict-dynamic) in the CSP as described + // in https://csp.withgoogle.com/docs/faq.html in section “What if my site is static and I can't add nonces to scripts?”. + // See for example the II CSP (https://github.com/dfinity/internet-identity/blob/main/src/internet_identity/src/http.rs). + // - It is recommended to tighten the connect-src directive. With the current CSP configuration the browser can + // make requests to https://*.icp0.io, hence being able to call any canister via https://icp0.io/api/v2/canister/{canister-ID}. + // This could potentially be used in combination with another vulnerability (e.g. XSS) to exfiltrate private data. + // The developer can configure this policy to only allow requests to their specific canisters, + // e.g: connect-src 'self' https://icp-api.io/api/v2/canister/{my-canister-ID}, where {my-canister-ID} has the following format: aaaaa-aaaaa-aaaaa-aaaaa-aaa + // - It is recommended to configure style-src, style-src-elem and font-src directives with the resources your canister is going to use + // instead of using the wild card (*) option. Normally this will include 'self' but also other third party styles or fonts resources (e.g: https://fonts.googleapis.com or other CDNs) + + // Notes about the CSP below: + // - We added img-src data: because data: images are used often. + // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-inline';connect-src 'self' http://localhost:* https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';", + + // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. + // To configure permissions go here https://www.permissionspolicy.com/ + "Permissions-Policy": "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-placement=(), vertical-scroll=()", + + // Security: Mitigates clickjacking attacks. + // See: https://owasp.org/www-community/attacks/Clickjacking. + "X-Frame-Options": "DENY", + + // Security: Avoids forwarding referrer information to other origins. + // See: https://owasp.org/www-project-secure-headers/#referrer-policy. + "Referrer-Policy": "same-origin", + + // Security: Tells the user’s browser that it must always use HTTPS with your site. + // See: https://owasp.org/www-project-secure-headers/#http-strict-transport-security + "Strict-Transport-Security": "max-age=31536000; includeSubDomains", - // Uncomment to disable the warning about using the - // standard security policy, if you understand the risk - // "disable_security_policy_warning": true, + // Security: Prevents the browser from interpreting files as a different MIME type to what is specified in the Content-Type header. + // See: https://owasp.org/www-project-secure-headers/#x-content-type-options + "X-Content-Type-Options": "nosniff", + // Security: Enables browser features to mitigate some of the XSS attacks. Note that it has to be in mode=block. + // See: https://owasp.org/www-community/attacks/xss/ + "X-XSS-Protection": "1; mode=block" + }, // Uncomment to redirect all requests from .raw.icp0.io to .icp0.io // "allow_raw_access": false }, diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/public/logo2.svg b/src/dfx/assets/project_templates/vue/src/__frontend_name__/public/logo.svg similarity index 100% rename from src/dfx/assets/project_templates/vue/src/__frontend_name__/public/logo2.svg rename to src/dfx/assets/project_templates/vue/src/__frontend_name__/public/logo.svg diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/App.vue b/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/App.vue index 8f9f08a883..ae382c7b34 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/App.vue +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/App.vue @@ -1,28 +1,29 @@ diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/index.scss b/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/index.scss index 283585fb27..904848f5fc 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/index.scss +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/src/index.scss @@ -1,39 +1,100 @@ -body { - font-family: sans-serif; - font-size: 1.5rem; +:root { + --vc-brand-gradient: radial-gradient(at 100% 100%, hsla(265, 80%, 75%, 1) 0px, transparent 50%), radial-gradient(at 100% 0%, hsla(272, 86%, 55%, 1) 0px, transparent 50%), radial-gradient(at 0% 18%, hsla(223, 79%, 66%, 1) 0px, transparent 50%); + --vc-brand-purple: #4b19d6; + + --rc-background-image--highlight: var(--vc-brand-gradient); + --rc-background--highlight: var(--vc-brand-purple); + + --rc-interaction-color: #4b19d6; + --rc-interaction-color-hover: #3c14aa; } -img { - max-width: 50vw; - max-height: 25vw; - display: block; - margin: auto; +body, html { + margin: 0; + padding: 0; + font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + height: 100%; } -form { +#app { + height: 100%; +} + +.container { + min-height: 100%; display: flex; justify-content: center; - gap: 0.5em; - flex-flow: row wrap; - max-width: 40vw; - margin: auto; - align-items: baseline; + align-items: center; + background-image: var(--rc-background-image--highlight); + background-color: var(--rc-background--highlight); } -button[type="submit"] { - padding: 5px 20px; - margin: 10px auto; - float: right; +.card { + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 320px; + display: flex; + flex-direction: column; + gap: 2rem; + align-items: center; } -#greeting { - margin: 10px auto; - padding: 10px 60px; - border: 1px solid #222; +.logo { + text-align: center; + margin-bottom: 1rem; } -#greeting:empty { - display: none; +.logo svg { + width: 100px; + height: auto; +} + +h1 { + color: #333; + text-align: center; + margin: 0 0 1rem; + word-wrap: break-word; + white-space: pre-wrap; + width: 100%; + + &:empty { + display: none; + } +} + +form { + display: flex; + flex-direction: column; + width: 100%; +} + +label { + margin-bottom: 0.5rem; + color: #333; +} + +input { + padding: 0.5rem; + margin-bottom: 1rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + padding: 0.5rem; + background-color: var(--rc-interaction-color); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s; +} + +button:hover { + background-color: var(--rc-interaction-color-hover); } .browser-warning { diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js index 92afec3511..a1c3196c3e 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js @@ -30,11 +30,19 @@ export default defineConfig({ environment('all', { prefix: 'CANISTER_' }), environment('all', { prefix: 'DFX_' }), ], + css: { + preprocessorOptions: { + scss: { + api: 'modern-compiler', + silenceDeprecations: ['legacy-js-api'], + }, + }, + }, resolve: { alias: [ { find: 'declarations', replacement: fileURLToPath(new URL('../declarations', import.meta.url)) }, { find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }, ], dedupe: ['@dfinity/agent'], - } + }, }); diff --git a/src/dfx/assets/project_templates/vue_tests/src/__frontend_name__/vite.config.js.patch b/src/dfx/assets/project_templates/vue_tests/src/__frontend_name__/vite.config.js.patch index d8d55951d1..5766485c5c 100644 --- a/src/dfx/assets/project_templates/vue_tests/src/__frontend_name__/vite.config.js.patch +++ b/src/dfx/assets/project_templates/vue_tests/src/__frontend_name__/vite.config.js.patch @@ -3,7 +3,7 @@ @@ -1,1 +1,2 @@ +/// import { fileURLToPath, URL } from 'url'; -@@ -33,1 +33,5 @@ +@@ -41,1 +41,5 @@ + test: { + environment: 'jsdom', + setupFiles: 'src/setupTests.js', diff --git a/src/dfx/src/lib/operations/canister/install_canister.rs b/src/dfx/src/lib/operations/canister/install_canister.rs index c75cf8cacc..68ef6130de 100644 --- a/src/dfx/src/lib/operations/canister/install_canister.rs +++ b/src/dfx/src/lib/operations/canister/install_canister.rs @@ -334,7 +334,8 @@ async fn wait_for_module_hash( // Playground may modify wasm before installing, therefore we cannot predict what the hash is supposed to be. debug!( env.get_logger(), - "Module hash verification is skipped for playground deployments." + "Canister {} has been successfully leased. Installing new code.", + canister_id ); break; }