@archie/devtools 0.0.6 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,8 @@
1
1
  import {
2
- getAllowedOrigins
3
- } from "./chunk-6MYORGLK.mjs";
2
+ getAllowedOriginPatterns,
3
+ getAllowedOrigins,
4
+ isPreviewOrigin
5
+ } from "./chunk-D5EA6RR5.mjs";
4
6
 
5
7
  // src/client/inject-inspector/injectInspector.ts
6
8
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
@@ -32,12 +34,19 @@ function injectInspector(opts = {}) {
32
34
  if (allowed.length > 0) {
33
35
  win["__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__"] = allowed;
34
36
  }
37
+ const patterns = getAllowedOriginPatterns();
38
+ if (patterns.length > 0) {
39
+ win["__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__"] = patterns;
40
+ }
35
41
  let target;
36
42
  if (opts.targetOrigin !== void 0 && opts.targetOrigin !== "*") {
37
43
  target = opts.targetOrigin || void 0;
38
44
  } else if (referrer) {
39
45
  try {
40
- target = new URL(referrer).origin;
46
+ const referrerOrigin = new URL(referrer).origin;
47
+ if (!isPreviewOrigin(referrerOrigin)) {
48
+ target = referrerOrigin;
49
+ }
41
50
  } catch {
42
51
  target = void 0;
43
52
  }
@@ -48,7 +57,7 @@ function injectInspector(opts = {}) {
48
57
  win["__ARCHIE_INSPECTOR_TARGET_ORIGIN__"] = target;
49
58
  }
50
59
  }
51
- return import("./inspector-LTHUYUGW.mjs").then((m) => {
60
+ return import("./inspector-QDRZLXRP.mjs").then((m) => {
52
61
  try {
53
62
  m.default();
54
63
  } catch (err) {
@@ -0,0 +1,47 @@
1
+ // src/constants/archieOrigins.ts
2
+ var ARCHIE_HOST_ORIGINS = [
3
+ "https://app.dev.archie-platform.com",
4
+ "https://app.staging.archie-platform.com",
5
+ "https://app.archie.com"
6
+ ];
7
+ var ARCHIE_PREVIEW_ORIGIN_SUFFIXES = [
8
+ ".staging.archie-platform.com",
9
+ ".archie-platform.com",
10
+ ".archie.com"
11
+ ];
12
+ var PREVIEW_DEPLOY_HOST_SUFFIXES = [
13
+ ".previews.staging.archie-platform.com",
14
+ ".previews.archie-platform.com",
15
+ ".previews.archie.com"
16
+ ];
17
+ var LOCAL_DEV_ORIGINS = [
18
+ "http://localhost:3000",
19
+ "http://localhost:3001"
20
+ ];
21
+ function getAllowedOrigins(includeLocal = false) {
22
+ const list = [...ARCHIE_HOST_ORIGINS];
23
+ if (includeLocal) list.push(...LOCAL_DEV_ORIGINS);
24
+ return list;
25
+ }
26
+ function getAllowedOriginPatterns() {
27
+ return [...ARCHIE_PREVIEW_ORIGIN_SUFFIXES];
28
+ }
29
+ function isPreviewOrigin(origin) {
30
+ if (!origin || typeof origin !== "string") return false;
31
+ try {
32
+ const host = new URL(origin).host;
33
+ return PREVIEW_DEPLOY_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix));
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+
39
+ export {
40
+ ARCHIE_HOST_ORIGINS,
41
+ ARCHIE_PREVIEW_ORIGIN_SUFFIXES,
42
+ PREVIEW_DEPLOY_HOST_SUFFIXES,
43
+ LOCAL_DEV_ORIGINS,
44
+ getAllowedOrigins,
45
+ getAllowedOriginPatterns,
46
+ isPreviewOrigin
47
+ };
@@ -87,10 +87,33 @@ function runInspector() {
87
87
  }
88
88
  }
89
89
  var ALLOWED_ORIGINS = typeof window !== "undefined" && window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ || null;
90
+ var ALLOWED_ORIGIN_PATTERNS = typeof window !== "undefined" && window.__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__ || [];
90
91
  function isOriginAllowed(origin) {
91
92
  if (!origin) return false;
92
- if (!Array.isArray(ALLOWED_ORIGINS) || ALLOWED_ORIGINS.length === 0 || ALLOWED_ORIGINS === "*") return false;
93
- return ALLOWED_ORIGINS.indexOf(origin) !== -1;
93
+ if (Array.isArray(ALLOWED_ORIGINS) && ALLOWED_ORIGINS.length > 0 && ALLOWED_ORIGINS !== "*") {
94
+ if (ALLOWED_ORIGINS.indexOf(origin) !== -1) return true;
95
+ }
96
+ if (Array.isArray(ALLOWED_ORIGIN_PATTERNS) && ALLOWED_ORIGIN_PATTERNS.length > 0) {
97
+ try {
98
+ var host = new URL(origin).host;
99
+ for (var i = 0; i < ALLOWED_ORIGIN_PATTERNS.length; i++) {
100
+ if (host.endsWith(ALLOWED_ORIGIN_PATTERNS[i])) return true;
101
+ }
102
+ } catch (e) {
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+ function isStyleValueSafe(value) {
108
+ if (value == null || typeof value !== "string") return false;
109
+ if (value.length > 1e4) return false;
110
+ var lower = value.toLowerCase();
111
+ if (lower.indexOf("javascript:") !== -1) return false;
112
+ if (lower.indexOf("expression(") !== -1) return false;
113
+ if (lower.indexOf("-moz-binding") !== -1) return false;
114
+ if (lower.indexOf("behavior") !== -1) return false;
115
+ if (lower.indexOf("vbscript:") !== -1) return false;
116
+ return true;
94
117
  }
95
118
  var SCROLL_LISTENER_OPTS = { capture: true, passive: true };
96
119
  var RESIZE_LISTENER_OPTS = { passive: true };
@@ -691,6 +714,17 @@ function runInspector() {
691
714
  property,
692
715
  value
693
716
  });
717
+ if (!property || typeof property !== "string" || property.length > 100 || !isStyleValueSafe(value)) {
718
+ inspectorWarn("[Inspector] Rejected invalid or unsafe style property/value");
719
+ postMessageToParent({
720
+ type: "STYLE_CHANGE_FAILED",
721
+ selector,
722
+ property: typeof property === "string" ? property : "",
723
+ value: value != null ? String(value).slice(0, 100) : "",
724
+ error: !isStyleValueSafe(value) ? "Invalid or unsafe style value" : "Invalid property"
725
+ });
726
+ return;
727
+ }
694
728
  let element = null;
695
729
  element = getSelectedElement();
696
730
  if (element) {
@@ -766,6 +800,7 @@ function runInspector() {
766
800
  );
767
801
  setTimeout(() => {
768
802
  try {
803
+ if (!isStyleValueSafe(value)) return;
769
804
  const retryElement = document.querySelector(selector);
770
805
  if (retryElement) {
771
806
  inspectorLog("[Inspector] Found element on retry");
@@ -858,7 +893,8 @@ function runInspector() {
858
893
  });
859
894
  return;
860
895
  }
861
- const classes = className.split(/\s+/).filter(Boolean);
896
+ var classStr = typeof className === "string" ? className.slice(0, 2e3) : "";
897
+ const classes = classStr.split(/\s+/).filter(Boolean).slice(0, 100);
862
898
  classes.forEach((cls) => {
863
899
  if (action === "remove") {
864
900
  element.classList.remove(cls);
@@ -898,6 +934,15 @@ function runInspector() {
898
934
  });
899
935
  } else if (event.data && event.data.type === "APPLY_TEXT_CHANGE") {
900
936
  const { selector, text } = event.data;
937
+ if (text != null && typeof text === "string" && text.length > 5e5) {
938
+ postMessageToParent({
939
+ type: "TEXT_CHANGE_FAILED",
940
+ selector,
941
+ text: "",
942
+ error: "Text value too long"
943
+ });
944
+ return;
945
+ }
901
946
  const element = getSelectedElement();
902
947
  if (!element) {
903
948
  postMessageToParent({
@@ -908,7 +953,7 @@ function runInspector() {
908
953
  });
909
954
  return;
910
955
  }
911
- element.textContent = text;
956
+ element.textContent = text != null ? String(text) : "";
912
957
  postMessageToParent({
913
958
  type: "TEXT_CHANGE_APPLIED",
914
959
  selector,
@@ -1116,6 +1161,16 @@ var ARCHIE_HOST_ORIGINS = [
1116
1161
  "https://app.staging.archie-platform.com",
1117
1162
  "https://app.archie.com"
1118
1163
  ];
1164
+ var ARCHIE_PREVIEW_ORIGIN_SUFFIXES = [
1165
+ ".staging.archie-platform.com",
1166
+ ".archie-platform.com",
1167
+ ".archie.com"
1168
+ ];
1169
+ var PREVIEW_DEPLOY_HOST_SUFFIXES = [
1170
+ ".previews.staging.archie-platform.com",
1171
+ ".previews.archie-platform.com",
1172
+ ".previews.archie.com"
1173
+ ];
1119
1174
  var LOCAL_DEV_ORIGINS = [
1120
1175
  "http://localhost:3000",
1121
1176
  "http://localhost:3001"
@@ -1125,6 +1180,18 @@ function getAllowedOrigins(includeLocal = false) {
1125
1180
  if (includeLocal) list.push(...LOCAL_DEV_ORIGINS);
1126
1181
  return list;
1127
1182
  }
1183
+ function getAllowedOriginPatterns() {
1184
+ return [...ARCHIE_PREVIEW_ORIGIN_SUFFIXES];
1185
+ }
1186
+ function isPreviewOrigin(origin) {
1187
+ if (!origin || typeof origin !== "string") return false;
1188
+ try {
1189
+ const host = new URL(origin).host;
1190
+ return PREVIEW_DEPLOY_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix));
1191
+ } catch {
1192
+ return false;
1193
+ }
1194
+ }
1128
1195
 
1129
1196
  // src/client/inject-inspector/injectInspector.ts
1130
1197
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
@@ -1156,12 +1223,19 @@ function injectInspector(opts = {}) {
1156
1223
  if (allowed.length > 0) {
1157
1224
  win["__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__"] = allowed;
1158
1225
  }
1226
+ const patterns = getAllowedOriginPatterns();
1227
+ if (patterns.length > 0) {
1228
+ win["__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__"] = patterns;
1229
+ }
1159
1230
  let target;
1160
1231
  if (opts.targetOrigin !== void 0 && opts.targetOrigin !== "*") {
1161
1232
  target = opts.targetOrigin || void 0;
1162
1233
  } else if (referrer) {
1163
1234
  try {
1164
- target = new URL(referrer).origin;
1235
+ const referrerOrigin = new URL(referrer).origin;
1236
+ if (!isPreviewOrigin(referrerOrigin)) {
1237
+ target = referrerOrigin;
1238
+ }
1165
1239
  } catch {
1166
1240
  target = void 0;
1167
1241
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  injectInspector
3
- } from "../../chunk-NYVHR4QL.mjs";
4
- import "../../chunk-6MYORGLK.mjs";
3
+ } from "../../chunk-6HHNPJXA.mjs";
4
+ import "../../chunk-D5EA6RR5.mjs";
5
5
 
6
6
  // src/client/inject-inspector/auto.ts
7
7
  injectInspector();
@@ -8,8 +8,10 @@ type InjectOptions = {
8
8
  */
9
9
  allowedOrigins?: string | string[];
10
10
  /**
11
- * Origin to which the inspector sends postMessage replies.
12
- * If not set and in an iframe, uses document.referrer origin.
11
+ * Origin to which the inspector sends postMessage replies (the parent window).
12
+ * If not set and in an iframe, uses document.referrer origin — except when the page
13
+ * is a preview iframe (*.previews.staging.archie-platform.com), in which case you
14
+ * must pass the parent app origin (e.g. https://app.dev.archie-platform.com).
13
15
  * "*" is not allowed.
14
16
  */
15
17
  targetOrigin?: string;
@@ -24,7 +26,10 @@ type InjectOptions = {
24
26
  * Safe to call multiple times: avoids duplicate execution by id.
25
27
  * No-op when run outside the browser (e.g. SSR).
26
28
  *
27
- * Origin restrictions are always set (never "*"): Archie host origins, and in dev optionally localhost.
29
+ * Security: Origin restrictions are always set (never "*"). We do not inject
30
+ * script via eval/Function or write HTML (innerHTML/insertAdjacentHTML).
31
+ * The inspector only accepts postMessage from allowlisted origins and
32
+ * sanitizes style values before applying to the DOM.
28
33
  *
29
34
  * @returns Promise that resolves to the marker script element, or null if not in browser / already present
30
35
  */
@@ -8,8 +8,10 @@ type InjectOptions = {
8
8
  */
9
9
  allowedOrigins?: string | string[];
10
10
  /**
11
- * Origin to which the inspector sends postMessage replies.
12
- * If not set and in an iframe, uses document.referrer origin.
11
+ * Origin to which the inspector sends postMessage replies (the parent window).
12
+ * If not set and in an iframe, uses document.referrer origin — except when the page
13
+ * is a preview iframe (*.previews.staging.archie-platform.com), in which case you
14
+ * must pass the parent app origin (e.g. https://app.dev.archie-platform.com).
13
15
  * "*" is not allowed.
14
16
  */
15
17
  targetOrigin?: string;
@@ -24,7 +26,10 @@ type InjectOptions = {
24
26
  * Safe to call multiple times: avoids duplicate execution by id.
25
27
  * No-op when run outside the browser (e.g. SSR).
26
28
  *
27
- * Origin restrictions are always set (never "*"): Archie host origins, and in dev optionally localhost.
29
+ * Security: Origin restrictions are always set (never "*"). We do not inject
30
+ * script via eval/Function or write HTML (innerHTML/insertAdjacentHTML).
31
+ * The inspector only accepts postMessage from allowlisted origins and
32
+ * sanitizes style values before applying to the DOM.
28
33
  *
29
34
  * @returns Promise that resolves to the marker script element, or null if not in browser / already present
30
35
  */
@@ -98,10 +98,33 @@ function runInspector() {
98
98
  }
99
99
  }
100
100
  var ALLOWED_ORIGINS = typeof window !== "undefined" && window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ || null;
101
+ var ALLOWED_ORIGIN_PATTERNS = typeof window !== "undefined" && window.__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__ || [];
101
102
  function isOriginAllowed(origin) {
102
103
  if (!origin) return false;
103
- if (!Array.isArray(ALLOWED_ORIGINS) || ALLOWED_ORIGINS.length === 0 || ALLOWED_ORIGINS === "*") return false;
104
- return ALLOWED_ORIGINS.indexOf(origin) !== -1;
104
+ if (Array.isArray(ALLOWED_ORIGINS) && ALLOWED_ORIGINS.length > 0 && ALLOWED_ORIGINS !== "*") {
105
+ if (ALLOWED_ORIGINS.indexOf(origin) !== -1) return true;
106
+ }
107
+ if (Array.isArray(ALLOWED_ORIGIN_PATTERNS) && ALLOWED_ORIGIN_PATTERNS.length > 0) {
108
+ try {
109
+ var host = new URL(origin).host;
110
+ for (var i = 0; i < ALLOWED_ORIGIN_PATTERNS.length; i++) {
111
+ if (host.endsWith(ALLOWED_ORIGIN_PATTERNS[i])) return true;
112
+ }
113
+ } catch (e) {
114
+ }
115
+ }
116
+ return false;
117
+ }
118
+ function isStyleValueSafe(value) {
119
+ if (value == null || typeof value !== "string") return false;
120
+ if (value.length > 1e4) return false;
121
+ var lower = value.toLowerCase();
122
+ if (lower.indexOf("javascript:") !== -1) return false;
123
+ if (lower.indexOf("expression(") !== -1) return false;
124
+ if (lower.indexOf("-moz-binding") !== -1) return false;
125
+ if (lower.indexOf("behavior") !== -1) return false;
126
+ if (lower.indexOf("vbscript:") !== -1) return false;
127
+ return true;
105
128
  }
106
129
  var SCROLL_LISTENER_OPTS = { capture: true, passive: true };
107
130
  var RESIZE_LISTENER_OPTS = { passive: true };
@@ -702,6 +725,17 @@ function runInspector() {
702
725
  property,
703
726
  value
704
727
  });
728
+ if (!property || typeof property !== "string" || property.length > 100 || !isStyleValueSafe(value)) {
729
+ inspectorWarn("[Inspector] Rejected invalid or unsafe style property/value");
730
+ postMessageToParent({
731
+ type: "STYLE_CHANGE_FAILED",
732
+ selector,
733
+ property: typeof property === "string" ? property : "",
734
+ value: value != null ? String(value).slice(0, 100) : "",
735
+ error: !isStyleValueSafe(value) ? "Invalid or unsafe style value" : "Invalid property"
736
+ });
737
+ return;
738
+ }
705
739
  let element = null;
706
740
  element = getSelectedElement();
707
741
  if (element) {
@@ -777,6 +811,7 @@ function runInspector() {
777
811
  );
778
812
  setTimeout(() => {
779
813
  try {
814
+ if (!isStyleValueSafe(value)) return;
780
815
  const retryElement = document.querySelector(selector);
781
816
  if (retryElement) {
782
817
  inspectorLog("[Inspector] Found element on retry");
@@ -869,7 +904,8 @@ function runInspector() {
869
904
  });
870
905
  return;
871
906
  }
872
- const classes = className.split(/\s+/).filter(Boolean);
907
+ var classStr = typeof className === "string" ? className.slice(0, 2e3) : "";
908
+ const classes = classStr.split(/\s+/).filter(Boolean).slice(0, 100);
873
909
  classes.forEach((cls) => {
874
910
  if (action === "remove") {
875
911
  element.classList.remove(cls);
@@ -909,6 +945,15 @@ function runInspector() {
909
945
  });
910
946
  } else if (event.data && event.data.type === "APPLY_TEXT_CHANGE") {
911
947
  const { selector, text } = event.data;
948
+ if (text != null && typeof text === "string" && text.length > 5e5) {
949
+ postMessageToParent({
950
+ type: "TEXT_CHANGE_FAILED",
951
+ selector,
952
+ text: "",
953
+ error: "Text value too long"
954
+ });
955
+ return;
956
+ }
912
957
  const element = getSelectedElement();
913
958
  if (!element) {
914
959
  postMessageToParent({
@@ -919,7 +964,7 @@ function runInspector() {
919
964
  });
920
965
  return;
921
966
  }
922
- element.textContent = text;
967
+ element.textContent = text != null ? String(text) : "";
923
968
  postMessageToParent({
924
969
  type: "TEXT_CHANGE_APPLIED",
925
970
  selector,
@@ -1134,6 +1179,16 @@ var ARCHIE_HOST_ORIGINS = [
1134
1179
  "https://app.staging.archie-platform.com",
1135
1180
  "https://app.archie.com"
1136
1181
  ];
1182
+ var ARCHIE_PREVIEW_ORIGIN_SUFFIXES = [
1183
+ ".staging.archie-platform.com",
1184
+ ".archie-platform.com",
1185
+ ".archie.com"
1186
+ ];
1187
+ var PREVIEW_DEPLOY_HOST_SUFFIXES = [
1188
+ ".previews.staging.archie-platform.com",
1189
+ ".previews.archie-platform.com",
1190
+ ".previews.archie.com"
1191
+ ];
1137
1192
  var LOCAL_DEV_ORIGINS = [
1138
1193
  "http://localhost:3000",
1139
1194
  "http://localhost:3001"
@@ -1143,6 +1198,18 @@ function getAllowedOrigins(includeLocal = false) {
1143
1198
  if (includeLocal) list.push(...LOCAL_DEV_ORIGINS);
1144
1199
  return list;
1145
1200
  }
1201
+ function getAllowedOriginPatterns() {
1202
+ return [...ARCHIE_PREVIEW_ORIGIN_SUFFIXES];
1203
+ }
1204
+ function isPreviewOrigin(origin) {
1205
+ if (!origin || typeof origin !== "string") return false;
1206
+ try {
1207
+ const host = new URL(origin).host;
1208
+ return PREVIEW_DEPLOY_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix));
1209
+ } catch {
1210
+ return false;
1211
+ }
1212
+ }
1146
1213
 
1147
1214
  // src/client/inject-inspector/injectInspector.ts
1148
1215
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
@@ -1174,12 +1241,19 @@ function injectInspector(opts = {}) {
1174
1241
  if (allowed.length > 0) {
1175
1242
  win["__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__"] = allowed;
1176
1243
  }
1244
+ const patterns = getAllowedOriginPatterns();
1245
+ if (patterns.length > 0) {
1246
+ win["__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__"] = patterns;
1247
+ }
1177
1248
  let target;
1178
1249
  if (opts.targetOrigin !== void 0 && opts.targetOrigin !== "*") {
1179
1250
  target = opts.targetOrigin || void 0;
1180
1251
  } else if (referrer) {
1181
1252
  try {
1182
- target = new URL(referrer).origin;
1253
+ const referrerOrigin = new URL(referrer).origin;
1254
+ if (!isPreviewOrigin(referrerOrigin)) {
1255
+ target = referrerOrigin;
1256
+ }
1183
1257
  } catch {
1184
1258
  target = void 0;
1185
1259
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  injectInspector
3
- } from "../../chunk-NYVHR4QL.mjs";
4
- import "../../chunk-6MYORGLK.mjs";
3
+ } from "../../chunk-6HHNPJXA.mjs";
4
+ import "../../chunk-D5EA6RR5.mjs";
5
5
  export {
6
6
  injectInspector
7
7
  };
@@ -6,6 +6,19 @@
6
6
  * @see https://app.archie.com
7
7
  */
8
8
  declare const ARCHIE_HOST_ORIGINS: readonly ["https://app.dev.archie-platform.com", "https://app.staging.archie-platform.com", "https://app.archie.com"];
9
+ /**
10
+ * Origin suffix patterns for Archie preview iframes (e.g. deploy previews).
11
+ * The inspector allows any origin whose host ends with one of these suffixes
12
+ * to send messages to the inspector (allowlist).
13
+ */
14
+ declare const ARCHIE_PREVIEW_ORIGIN_SUFFIXES: readonly [".staging.archie-platform.com", ".archie-platform.com", ".archie.com"];
15
+ /**
16
+ * Host suffixes that identify a **preview deploy** iframe (e.g. 0cf7f787-....previews.staging.archie-platform.com).
17
+ * Used only by isPreviewOrigin() to decide "don't use referrer as targetOrigin" when the *current page*
18
+ * is a preview iframe. Must be narrower than ARCHIE_PREVIEW_ORIGIN_SUFFIXES so that the parent
19
+ * (e.g. app.dev.archie-platform.com) is not treated as a preview and its origin is used as targetOrigin.
20
+ */
21
+ declare const PREVIEW_DEPLOY_HOST_SUFFIXES: readonly [".previews.staging.archie-platform.com", ".previews.archie-platform.com", ".previews.archie.com"];
9
22
  /**
10
23
  * Common local origins for development. Add these when testing the inspector
11
24
  * with the host running on localhost (e.g. host on :3000, preview iframe on :5173).
@@ -20,5 +33,16 @@ type ArchieHostOrigin = (typeof ARCHIE_HOST_ORIGINS)[number];
20
33
  * @param includeLocal - If true, appends LOCAL_DEV_ORIGINS for local testing
21
34
  */
22
35
  declare function getAllowedOrigins(includeLocal?: boolean): string[];
36
+ /**
37
+ * Returns origin suffix patterns for preview iframes. The inspector allows any origin
38
+ * one of these suffixes (after the protocol).
39
+ */
40
+ declare function getAllowedOriginPatterns(): string[];
41
+ /**
42
+ * Returns true if the given origin is a preview **deploy** iframe (e.g. xxx.previews.staging.archie-platform.com).
43
+ * Used so we don't use that origin as postMessage targetOrigin; the target must be the parent app.
44
+ * Does not match app.dev.archie-platform.com / app.staging.archie-platform.com (so they can be used as target).
45
+ */
46
+ declare function isPreviewOrigin(origin: string): boolean;
23
47
 
24
- export { ARCHIE_HOST_ORIGINS, type ArchieHostOrigin, LOCAL_DEV_ORIGINS, getAllowedOrigins };
48
+ export { ARCHIE_HOST_ORIGINS, ARCHIE_PREVIEW_ORIGIN_SUFFIXES, type ArchieHostOrigin, LOCAL_DEV_ORIGINS, PREVIEW_DEPLOY_HOST_SUFFIXES, getAllowedOriginPatterns, getAllowedOrigins, isPreviewOrigin };
@@ -6,6 +6,19 @@
6
6
  * @see https://app.archie.com
7
7
  */
8
8
  declare const ARCHIE_HOST_ORIGINS: readonly ["https://app.dev.archie-platform.com", "https://app.staging.archie-platform.com", "https://app.archie.com"];
9
+ /**
10
+ * Origin suffix patterns for Archie preview iframes (e.g. deploy previews).
11
+ * The inspector allows any origin whose host ends with one of these suffixes
12
+ * to send messages to the inspector (allowlist).
13
+ */
14
+ declare const ARCHIE_PREVIEW_ORIGIN_SUFFIXES: readonly [".staging.archie-platform.com", ".archie-platform.com", ".archie.com"];
15
+ /**
16
+ * Host suffixes that identify a **preview deploy** iframe (e.g. 0cf7f787-....previews.staging.archie-platform.com).
17
+ * Used only by isPreviewOrigin() to decide "don't use referrer as targetOrigin" when the *current page*
18
+ * is a preview iframe. Must be narrower than ARCHIE_PREVIEW_ORIGIN_SUFFIXES so that the parent
19
+ * (e.g. app.dev.archie-platform.com) is not treated as a preview and its origin is used as targetOrigin.
20
+ */
21
+ declare const PREVIEW_DEPLOY_HOST_SUFFIXES: readonly [".previews.staging.archie-platform.com", ".previews.archie-platform.com", ".previews.archie.com"];
9
22
  /**
10
23
  * Common local origins for development. Add these when testing the inspector
11
24
  * with the host running on localhost (e.g. host on :3000, preview iframe on :5173).
@@ -20,5 +33,16 @@ type ArchieHostOrigin = (typeof ARCHIE_HOST_ORIGINS)[number];
20
33
  * @param includeLocal - If true, appends LOCAL_DEV_ORIGINS for local testing
21
34
  */
22
35
  declare function getAllowedOrigins(includeLocal?: boolean): string[];
36
+ /**
37
+ * Returns origin suffix patterns for preview iframes. The inspector allows any origin
38
+ * one of these suffixes (after the protocol).
39
+ */
40
+ declare function getAllowedOriginPatterns(): string[];
41
+ /**
42
+ * Returns true if the given origin is a preview **deploy** iframe (e.g. xxx.previews.staging.archie-platform.com).
43
+ * Used so we don't use that origin as postMessage targetOrigin; the target must be the parent app.
44
+ * Does not match app.dev.archie-platform.com / app.staging.archie-platform.com (so they can be used as target).
45
+ */
46
+ declare function isPreviewOrigin(origin: string): boolean;
23
47
 
24
- export { ARCHIE_HOST_ORIGINS, type ArchieHostOrigin, LOCAL_DEV_ORIGINS, getAllowedOrigins };
48
+ export { ARCHIE_HOST_ORIGINS, ARCHIE_PREVIEW_ORIGIN_SUFFIXES, type ArchieHostOrigin, LOCAL_DEV_ORIGINS, PREVIEW_DEPLOY_HOST_SUFFIXES, getAllowedOriginPatterns, getAllowedOrigins, isPreviewOrigin };
@@ -21,8 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var archieOrigins_exports = {};
22
22
  __export(archieOrigins_exports, {
23
23
  ARCHIE_HOST_ORIGINS: () => ARCHIE_HOST_ORIGINS,
24
+ ARCHIE_PREVIEW_ORIGIN_SUFFIXES: () => ARCHIE_PREVIEW_ORIGIN_SUFFIXES,
24
25
  LOCAL_DEV_ORIGINS: () => LOCAL_DEV_ORIGINS,
25
- getAllowedOrigins: () => getAllowedOrigins
26
+ PREVIEW_DEPLOY_HOST_SUFFIXES: () => PREVIEW_DEPLOY_HOST_SUFFIXES,
27
+ getAllowedOriginPatterns: () => getAllowedOriginPatterns,
28
+ getAllowedOrigins: () => getAllowedOrigins,
29
+ isPreviewOrigin: () => isPreviewOrigin
26
30
  });
27
31
  module.exports = __toCommonJS(archieOrigins_exports);
28
32
  var ARCHIE_HOST_ORIGINS = [
@@ -30,6 +34,16 @@ var ARCHIE_HOST_ORIGINS = [
30
34
  "https://app.staging.archie-platform.com",
31
35
  "https://app.archie.com"
32
36
  ];
37
+ var ARCHIE_PREVIEW_ORIGIN_SUFFIXES = [
38
+ ".staging.archie-platform.com",
39
+ ".archie-platform.com",
40
+ ".archie.com"
41
+ ];
42
+ var PREVIEW_DEPLOY_HOST_SUFFIXES = [
43
+ ".previews.staging.archie-platform.com",
44
+ ".previews.archie-platform.com",
45
+ ".previews.archie.com"
46
+ ];
33
47
  var LOCAL_DEV_ORIGINS = [
34
48
  "http://localhost:3000",
35
49
  "http://localhost:3001"
@@ -39,9 +53,25 @@ function getAllowedOrigins(includeLocal = false) {
39
53
  if (includeLocal) list.push(...LOCAL_DEV_ORIGINS);
40
54
  return list;
41
55
  }
56
+ function getAllowedOriginPatterns() {
57
+ return [...ARCHIE_PREVIEW_ORIGIN_SUFFIXES];
58
+ }
59
+ function isPreviewOrigin(origin) {
60
+ if (!origin || typeof origin !== "string") return false;
61
+ try {
62
+ const host = new URL(origin).host;
63
+ return PREVIEW_DEPLOY_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix));
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
42
68
  // Annotate the CommonJS export names for ESM import in node:
43
69
  0 && (module.exports = {
44
70
  ARCHIE_HOST_ORIGINS,
71
+ ARCHIE_PREVIEW_ORIGIN_SUFFIXES,
45
72
  LOCAL_DEV_ORIGINS,
46
- getAllowedOrigins
73
+ PREVIEW_DEPLOY_HOST_SUFFIXES,
74
+ getAllowedOriginPatterns,
75
+ getAllowedOrigins,
76
+ isPreviewOrigin
47
77
  });
@@ -1,10 +1,18 @@
1
1
  import {
2
2
  ARCHIE_HOST_ORIGINS,
3
+ ARCHIE_PREVIEW_ORIGIN_SUFFIXES,
3
4
  LOCAL_DEV_ORIGINS,
4
- getAllowedOrigins
5
- } from "../chunk-6MYORGLK.mjs";
5
+ PREVIEW_DEPLOY_HOST_SUFFIXES,
6
+ getAllowedOriginPatterns,
7
+ getAllowedOrigins,
8
+ isPreviewOrigin
9
+ } from "../chunk-D5EA6RR5.mjs";
6
10
  export {
7
11
  ARCHIE_HOST_ORIGINS,
12
+ ARCHIE_PREVIEW_ORIGIN_SUFFIXES,
8
13
  LOCAL_DEV_ORIGINS,
9
- getAllowedOrigins
14
+ PREVIEW_DEPLOY_HOST_SUFFIXES,
15
+ getAllowedOriginPatterns,
16
+ getAllowedOrigins,
17
+ isPreviewOrigin
10
18
  };
@@ -72,10 +72,33 @@ function runInspector() {
72
72
  }
73
73
  }
74
74
  var ALLOWED_ORIGINS = typeof window !== "undefined" && window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ || null;
75
+ var ALLOWED_ORIGIN_PATTERNS = typeof window !== "undefined" && window.__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__ || [];
75
76
  function isOriginAllowed(origin) {
76
77
  if (!origin) return false;
77
- if (!Array.isArray(ALLOWED_ORIGINS) || ALLOWED_ORIGINS.length === 0 || ALLOWED_ORIGINS === "*") return false;
78
- return ALLOWED_ORIGINS.indexOf(origin) !== -1;
78
+ if (Array.isArray(ALLOWED_ORIGINS) && ALLOWED_ORIGINS.length > 0 && ALLOWED_ORIGINS !== "*") {
79
+ if (ALLOWED_ORIGINS.indexOf(origin) !== -1) return true;
80
+ }
81
+ if (Array.isArray(ALLOWED_ORIGIN_PATTERNS) && ALLOWED_ORIGIN_PATTERNS.length > 0) {
82
+ try {
83
+ var host = new URL(origin).host;
84
+ for (var i = 0; i < ALLOWED_ORIGIN_PATTERNS.length; i++) {
85
+ if (host.endsWith(ALLOWED_ORIGIN_PATTERNS[i])) return true;
86
+ }
87
+ } catch (e) {
88
+ }
89
+ }
90
+ return false;
91
+ }
92
+ function isStyleValueSafe(value) {
93
+ if (value == null || typeof value !== "string") return false;
94
+ if (value.length > 1e4) return false;
95
+ var lower = value.toLowerCase();
96
+ if (lower.indexOf("javascript:") !== -1) return false;
97
+ if (lower.indexOf("expression(") !== -1) return false;
98
+ if (lower.indexOf("-moz-binding") !== -1) return false;
99
+ if (lower.indexOf("behavior") !== -1) return false;
100
+ if (lower.indexOf("vbscript:") !== -1) return false;
101
+ return true;
79
102
  }
80
103
  var SCROLL_LISTENER_OPTS = { capture: true, passive: true };
81
104
  var RESIZE_LISTENER_OPTS = { passive: true };
@@ -676,6 +699,17 @@ function runInspector() {
676
699
  property,
677
700
  value
678
701
  });
702
+ if (!property || typeof property !== "string" || property.length > 100 || !isStyleValueSafe(value)) {
703
+ inspectorWarn("[Inspector] Rejected invalid or unsafe style property/value");
704
+ postMessageToParent({
705
+ type: "STYLE_CHANGE_FAILED",
706
+ selector,
707
+ property: typeof property === "string" ? property : "",
708
+ value: value != null ? String(value).slice(0, 100) : "",
709
+ error: !isStyleValueSafe(value) ? "Invalid or unsafe style value" : "Invalid property"
710
+ });
711
+ return;
712
+ }
679
713
  let element = null;
680
714
  element = getSelectedElement();
681
715
  if (element) {
@@ -751,6 +785,7 @@ function runInspector() {
751
785
  );
752
786
  setTimeout(() => {
753
787
  try {
788
+ if (!isStyleValueSafe(value)) return;
754
789
  const retryElement = document.querySelector(selector);
755
790
  if (retryElement) {
756
791
  inspectorLog("[Inspector] Found element on retry");
@@ -843,7 +878,8 @@ function runInspector() {
843
878
  });
844
879
  return;
845
880
  }
846
- const classes = className.split(/\s+/).filter(Boolean);
881
+ var classStr = typeof className === "string" ? className.slice(0, 2e3) : "";
882
+ const classes = classStr.split(/\s+/).filter(Boolean).slice(0, 100);
847
883
  classes.forEach((cls) => {
848
884
  if (action === "remove") {
849
885
  element.classList.remove(cls);
@@ -883,6 +919,15 @@ function runInspector() {
883
919
  });
884
920
  } else if (event.data && event.data.type === "APPLY_TEXT_CHANGE") {
885
921
  const { selector, text } = event.data;
922
+ if (text != null && typeof text === "string" && text.length > 5e5) {
923
+ postMessageToParent({
924
+ type: "TEXT_CHANGE_FAILED",
925
+ selector,
926
+ text: "",
927
+ error: "Text value too long"
928
+ });
929
+ return;
930
+ }
886
931
  const element = getSelectedElement();
887
932
  if (!element) {
888
933
  postMessageToParent({
@@ -893,7 +938,7 @@ function runInspector() {
893
938
  });
894
939
  return;
895
940
  }
896
- element.textContent = text;
941
+ element.textContent = text != null ? String(text) : "";
897
942
  postMessageToParent({
898
943
  type: "TEXT_CHANGE_APPLIED",
899
944
  selector,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.0.6",
3
+ "version": "0.0.9",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -1,21 +0,0 @@
1
- // src/constants/archieOrigins.ts
2
- var ARCHIE_HOST_ORIGINS = [
3
- "https://app.dev.archie-platform.com",
4
- "https://app.staging.archie-platform.com",
5
- "https://app.archie.com"
6
- ];
7
- var LOCAL_DEV_ORIGINS = [
8
- "http://localhost:3000",
9
- "http://localhost:3001"
10
- ];
11
- function getAllowedOrigins(includeLocal = false) {
12
- const list = [...ARCHIE_HOST_ORIGINS];
13
- if (includeLocal) list.push(...LOCAL_DEV_ORIGINS);
14
- return list;
15
- }
16
-
17
- export {
18
- ARCHIE_HOST_ORIGINS,
19
- LOCAL_DEV_ORIGINS,
20
- getAllowedOrigins
21
- };