|
1 | 1 | 'use strict'; |
2 | | -const { isSafeHttpUrl } = require('./app-spec.js'); |
| 2 | +const { isSafeHttpUrl, webResourceNameFromRef } = require('./app-spec.js'); |
3 | 3 | // Reconstruct a COMPLETE app-spec from a DEPLOYED app (the edit flow's "pull everything" step). Pure |
4 | 4 | // + testable: `read` supplies the deployed state — the app (sitemap JSON, via the SDK's app read |
5 | 5 | // path which surfaces entity/genPage/icon subareas), its generative pages (via pac list+download), |
@@ -31,23 +31,20 @@ function subAreaToSpec(sa, pageRefById, dashboardNameById) { |
31 | 31 | return name ? { ...base, dashboard: name } : null; // drop only if we couldn't reconstruct the dashboard |
32 | 32 | } |
33 | 33 | if (sa.type === 'URL' && sa.url) { |
34 | | - // A sitemap URL subarea does not always carry a real link. The Site Map Designer's "custom page |
35 | | - // backed by an HTML web resource" writes a TOKEN instead — `$webresource:<name>` (Dataverse also |
36 | | - // serves the same resource at `/WebResources/<name>`). The App Spec has no `webresource` subarea |
37 | | - // kind, and the validator requires http(s) (`isSafeHttpUrl`, a deliberate guard: a javascript: |
38 | | - // or file: nav entry in a shipped app is a script-injection / local-file-exfil vector). |
| 34 | + // A URL subarea is EITHER a real link OR a web-resource TOKEN (`$webresource:<name>`, which |
| 35 | + // Dataverse also serves at `/WebResources/<name>`) — the Site Map Designer's "custom page backed |
| 36 | + // by an HTML web resource". BOTH round-trip: the validator accepts a token whose web resource is |
| 37 | + // declared in `webResources[]`, and `collectSitemap` adds that name to the download's |
| 38 | + // `customRefs`, so its CONTENT is fetched and re-declared — the same path a custom nav icon |
| 39 | + // referenced by token already takes. |
39 | 40 | // |
40 | | - // Passing the token through therefore made the WHOLE download fail validation and write no spec |
41 | | - // at all — blocking download → edit → rebuild for the entire app over one unrelated nav entry, |
42 | | - // with an error naming a validator rather than the offending subarea, and `--allow-lossy-download` |
43 | | - // did not cover it. Drop it like CustomPage instead, so it is counted in `droppedSubareas`: the |
44 | | - // maker is told exactly which nav entry will be missing, and the existing lossy override writes |
45 | | - // the rest of the app. |
46 | | - // |
47 | | - // Tested against the shared `isSafeHttpUrl` rather than a `$webresource:` string match, so ANY |
48 | | - // scheme the validator would reject is dropped here rather than failing the download, and the two |
49 | | - // cannot drift apart. |
50 | | - return isSafeHttpUrl(sa.url) ? { ...base, url: sa.url } : null; |
| 41 | + // Anything else (a `javascript:`/`file:` scheme, a malformed string) cannot be expressed in the |
| 42 | + // App Spec, so it is dropped here rather than emitted. Passing it through made the WHOLE download |
| 43 | + // fail validation and write no spec at all, blocking download → edit → rebuild for the entire app |
| 44 | + // over one nav entry (issue #430). A drop is counted in `droppedSubareas`, so the maker is told |
| 45 | + // which entry will be missing and `--allow-lossy-download` writes the rest. |
| 46 | + if (webResourceNameFromRef(sa.url) || isSafeHttpUrl(sa.url)) return { ...base, url: sa.url }; |
| 47 | + return null; |
51 | 48 | } |
52 | 49 | return null; // CustomPage / unmapped — not hydrated |
53 | 50 | } |
|
0 commit comments