@shopify/create-app 4.9.2 → 4.9.3
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.
- package/dist/{analytics-6EJD33WP.js → analytics-TTLHU7RJ.js} +14 -14
- package/dist/{analytics-TUZG7DA4.js → analytics-XMJCXJPW.js} +14 -14
- package/dist/{chunk-TCPQPFTE.js → chunk-3FNBF3J4.js} +3 -3
- package/dist/{chunk-R2AIQR2X.js → chunk-5H43E2UF.js} +14 -14
- package/dist/{chunk-IY53IJS3.js → chunk-7U2D7OQY.js} +16 -16
- package/dist/{chunk-UHTO6LKY.js → chunk-CJUEOAXA.js} +4 -4
- package/dist/{chunk-KCVRVXZY.js → chunk-GI3EXDGY.js} +4 -4
- package/dist/{chunk-YMDHYX2O.js → chunk-GXR6N3MP.js} +2 -2
- package/dist/{chunk-U5KA5YG3.js → chunk-HAZKL7NQ.js} +6 -6
- package/dist/{chunk-4L6UUTTJ.js → chunk-HT6VPO56.js} +3 -3
- package/dist/{chunk-GR2RHEDU.js → chunk-KFK6UTCG.js} +2 -2
- package/dist/{chunk-PYUSHB4W.js → chunk-MA5EMKMH.js} +2 -2
- package/dist/{chunk-AKE2R5YA.js → chunk-N46TBZG5.js} +2 -2
- package/dist/{chunk-UU2BFFYI.js → chunk-N5WBV6WX.js} +5 -5
- package/dist/{chunk-MB6QQB5L.js → chunk-QIGNQLSJ.js} +2 -2
- package/dist/{chunk-PI6OTTFS.js → chunk-R6VAFEZ5.js} +3 -3
- package/dist/{chunk-BJRUKXTH.js → chunk-T2OHLRUY.js} +4 -4
- package/dist/{chunk-DBII5OBE.js → chunk-U53ZNKER.js} +7 -7
- package/dist/{chunk-CVBQ5S5E.js → chunk-YGIFL56H.js} +9 -9
- package/dist/{chunk-JLQYDEOX.js → chunk-ZWNY6NQH.js} +6 -6
- package/dist/{command-json-schema-JURPJVZ3.js → command-json-schema-RCC6ZVRF.js} +7 -7
- package/dist/{conf-store-WTXS6AY6.js → conf-store-STBXZZZR.js} +5 -5
- package/dist/{custom-oclif-loader-M5AZS4TE.js → custom-oclif-loader-Q7GRLCIZ.js} +5 -5
- package/dist/{deprecations-HATTH3TU.js → deprecations-T466QS35.js} +10 -10
- package/dist/{environments-P6W4YQBT.js → environments-L53AHQLZ.js} +11 -11
- package/dist/{error-handler-T5DK23DW.js → error-handler-DRCELNRR.js} +14 -14
- package/dist/{fs-YBH7ZBPM.js → fs-VUYIAOSY.js} +3 -3
- package/dist/hooks/postrun.js +4 -4
- package/dist/hooks/prerun.js +6 -6
- package/dist/index.js +173 -109
- package/dist/{is-global-RSJKDZBD.js → is-global-C4ZYYOO2.js} +3 -3
- package/dist/{json-error-EHXN4G6A.js → json-error-YAPVM6KF.js} +6 -6
- package/dist/{local-XHZE5NPW.js → local-YQI64EPC.js} +2 -2
- package/dist/{metadata-3AKOV7W4.js → metadata-NABA27NK.js} +3 -3
- package/dist/{node-package-manager-IRHXUBQ2.js → node-package-manager-GY7LMT5D.js} +12 -12
- package/dist/{notifications-system-LJSYFBE6.js → notifications-system-244NM3IW.js} +15 -15
- package/dist/{os-TMXMOEZT.js → os-34M2RBVM.js} +4 -4
- package/dist/{output-62KIUOOP.js → output-GT6WTA4L.js} +3 -3
- package/dist/{system-JRCUPDF5.js → system-Q623ZJ6Z.js} +10 -10
- package/dist/{ui-XXDBKKJS.js → ui-DVZCQR62.js} +10 -10
- package/dist/{upgrade-J6F3T32S.js → upgrade-3FR3Z3QX.js} +16 -16
- package/dist/{version-VZYNTIET.js → version-GRDUIVHB.js} +11 -11
- package/dist/{version-NC64OPAP.js → version-GZMTMHG3.js} +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
decodeToml,
|
|
5
5
|
loadEnvironment,
|
|
6
6
|
require_toml_patch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-N5WBV6WX.js";
|
|
8
8
|
import {
|
|
9
9
|
require_normalize_path
|
|
10
10
|
} from "./chunk-RIQNGBMO.js";
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
setCurrentCommandId,
|
|
17
17
|
showNotificationsIfNeeded
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YGIFL56H.js";
|
|
19
19
|
import {
|
|
20
20
|
ClientError,
|
|
21
21
|
DevServerCore,
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
setPathValue,
|
|
82
82
|
shopifyFetch,
|
|
83
83
|
storeAdminUrl
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-5H43E2UF.js";
|
|
85
85
|
import {
|
|
86
86
|
setNextDeprecationDate
|
|
87
87
|
} from "./chunk-MSED5ZFM.js";
|
|
@@ -103,26 +103,26 @@ import {
|
|
|
103
103
|
usesWorkspaces,
|
|
104
104
|
versionSatisfies,
|
|
105
105
|
writePackageJSON
|
|
106
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-HAZKL7NQ.js";
|
|
107
107
|
import {
|
|
108
108
|
import_websocket_server
|
|
109
109
|
} from "./chunk-LDYT4FTV.js";
|
|
110
110
|
import "./chunk-FKZ3NFHZ.js";
|
|
111
111
|
import {
|
|
112
112
|
CLI_KIT_VERSION
|
|
113
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-MA5EMKMH.js";
|
|
114
114
|
import "./chunk-G53O4PDO.js";
|
|
115
115
|
import {
|
|
116
116
|
LocalStorage,
|
|
117
117
|
require_ajv,
|
|
118
118
|
runAtMinimumInterval,
|
|
119
119
|
serviceEnvironment
|
|
120
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-CJUEOAXA.js";
|
|
121
121
|
import {
|
|
122
122
|
globalCLIVersion,
|
|
123
123
|
isPreReleaseVersion,
|
|
124
124
|
localCLIVersion
|
|
125
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-QIGNQLSJ.js";
|
|
126
126
|
import {
|
|
127
127
|
AbortController as AbortController2,
|
|
128
128
|
Alert,
|
|
@@ -170,10 +170,10 @@ import {
|
|
|
170
170
|
use_input_default,
|
|
171
171
|
use_stdin_default,
|
|
172
172
|
use_stdout_default
|
|
173
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-U53ZNKER.js";
|
|
174
174
|
import {
|
|
175
175
|
runWithCommandEventsForCommand
|
|
176
|
-
} from "./chunk-
|
|
176
|
+
} from "./chunk-T2OHLRUY.js";
|
|
177
177
|
import {
|
|
178
178
|
execa,
|
|
179
179
|
isStream,
|
|
@@ -186,14 +186,14 @@ import {
|
|
|
186
186
|
require_signal_exit,
|
|
187
187
|
stripFinalNewline,
|
|
188
188
|
username
|
|
189
|
-
} from "./chunk-
|
|
189
|
+
} from "./chunk-GXR6N3MP.js";
|
|
190
190
|
import "./chunk-JGGOQOPO.js";
|
|
191
191
|
import {
|
|
192
192
|
addPublicMetadata,
|
|
193
193
|
addSensitiveMetadata,
|
|
194
194
|
createRuntimeMetadataContainer,
|
|
195
195
|
runWithTimer
|
|
196
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-HT6VPO56.js";
|
|
197
197
|
import {
|
|
198
198
|
fileHash,
|
|
199
199
|
getAppAutomationToken,
|
|
@@ -206,20 +206,20 @@ import {
|
|
|
206
206
|
} from "./chunk-SHCNJ47P.js";
|
|
207
207
|
import {
|
|
208
208
|
defineJsonOutputSchema
|
|
209
|
-
} from "./chunk-
|
|
209
|
+
} from "./chunk-N46TBZG5.js";
|
|
210
210
|
import "./chunk-CM6ALOD7.js";
|
|
211
211
|
import {
|
|
212
212
|
AbortError,
|
|
213
213
|
AbortSilentError,
|
|
214
214
|
BugError,
|
|
215
215
|
CancelExecution
|
|
216
|
-
} from "./chunk-
|
|
216
|
+
} from "./chunk-GI3EXDGY.js";
|
|
217
217
|
import {
|
|
218
218
|
waitForPostRunHookAndExit
|
|
219
|
-
} from "./chunk-
|
|
219
|
+
} from "./chunk-7U2D7OQY.js";
|
|
220
220
|
import {
|
|
221
221
|
treeKill
|
|
222
|
-
} from "./chunk-
|
|
222
|
+
} from "./chunk-KFK6UTCG.js";
|
|
223
223
|
import {
|
|
224
224
|
appendFile,
|
|
225
225
|
asHumanFriendlyArray,
|
|
@@ -316,7 +316,7 @@ import {
|
|
|
316
316
|
unstyled,
|
|
317
317
|
writeFile,
|
|
318
318
|
writeFileSync
|
|
319
|
-
} from "./chunk-
|
|
319
|
+
} from "./chunk-ZWNY6NQH.js";
|
|
320
320
|
import {
|
|
321
321
|
codespacePortForwardingDomain,
|
|
322
322
|
codespaceURL,
|
|
@@ -330,7 +330,7 @@ import {
|
|
|
330
330
|
isUnitTest,
|
|
331
331
|
isVerbose,
|
|
332
332
|
noInputFlag
|
|
333
|
-
} from "./chunk-
|
|
333
|
+
} from "./chunk-R6VAFEZ5.js";
|
|
334
334
|
import {
|
|
335
335
|
environmentVariables,
|
|
336
336
|
themeKitAccessDomain
|
|
@@ -203943,7 +203943,7 @@ var import_core2 = __toESM(require_lib()), BaseCommand = class extends import_co
|
|
|
203943
203943
|
}
|
|
203944
203944
|
async catch(error) {
|
|
203945
203945
|
error.skipOclifErrorHandling = !0;
|
|
203946
|
-
let { errorHandler } = await import("./error-handler-
|
|
203946
|
+
let { errorHandler } = await import("./error-handler-DRCELNRR.js");
|
|
203947
203947
|
return await errorHandler(error, this.config), import_core2.Errors.handle(error);
|
|
203948
203948
|
}
|
|
203949
203949
|
async _run() {
|
|
@@ -203951,11 +203951,11 @@ var import_core2 = __toESM(require_lib()), BaseCommand = class extends import_co
|
|
|
203951
203951
|
}
|
|
203952
203952
|
async init() {
|
|
203953
203953
|
if (this.exitWithTimestampWhenEnvVariablePresent(), setCurrentCommandId(this.id ?? ""), !isDevelopment()) {
|
|
203954
|
-
let { registerCleanBugsnagErrorsFromWithinPlugins } = await import("./error-handler-
|
|
203954
|
+
let { registerCleanBugsnagErrorsFromWithinPlugins } = await import("./error-handler-DRCELNRR.js");
|
|
203955
203955
|
await registerCleanBugsnagErrorsFromWithinPlugins(this.config);
|
|
203956
203956
|
}
|
|
203957
203957
|
await removeDuplicatedPlugins(this.config), this.showNpmFlagWarning();
|
|
203958
|
-
let { showNotificationsIfNeeded: showNotificationsIfNeeded2 } = await import("./notifications-system-
|
|
203958
|
+
let { showNotificationsIfNeeded: showNotificationsIfNeeded2 } = await import("./notifications-system-244NM3IW.js");
|
|
203959
203959
|
return await showNotificationsIfNeeded2(), super.init();
|
|
203960
203960
|
}
|
|
203961
203961
|
// NPM creates an environment variable for every flag passed to a script.
|
|
@@ -203963,7 +203963,7 @@ var import_core2 = __toESM(require_lib()), BaseCommand = class extends import_co
|
|
|
203963
203963
|
// and warns the user to use the `--` separator.
|
|
203964
203964
|
showNpmFlagWarning() {
|
|
203965
203965
|
let commandVariables = this.constructor;
|
|
203966
|
-
Object.keys(commandVariables.flags || {}).map((key) => `npm_config_${underscore(key).replace(/^no_/, "")}`).some((flag) => process.env[flag] !== void 0) && import("./ui-
|
|
203966
|
+
Object.keys(commandVariables.flags || {}).map((key) => `npm_config_${underscore(key).replace(/^no_/, "")}`).some((flag) => process.env[flag] !== void 0) && import("./ui-DVZCQR62.js").then(({ renderWarning: renderWarning2 }) => {
|
|
203967
203967
|
renderWarning2({
|
|
203968
203968
|
body: [
|
|
203969
203969
|
"NPM scripts require an extra",
|
|
@@ -204012,7 +204012,7 @@ ${explanation} in non-interactive terminal environments, such as a CI environmen
|
|
|
204012
204012
|
let flags = originalResult.flags, environmentsFileName = this.environmentsFilename();
|
|
204013
204013
|
if (!environmentsFileName)
|
|
204014
204014
|
return originalResult;
|
|
204015
|
-
let { environmentFilePath } = await import("./environments-
|
|
204015
|
+
let { environmentFilePath } = await import("./environments-L53AHQLZ.js"), environmentFileExists = await environmentFilePath(environmentsFileName, { from: flags.path }), environments = [];
|
|
204016
204016
|
flags.environment && (environments = Array.isArray(flags.environment) ? flags.environment : [flags.environment]);
|
|
204017
204017
|
let environmentSpecified = environments.length > 0;
|
|
204018
204018
|
if (!environmentFileExists && !environmentSpecified || environmentSpecified && environments.length > 1)
|
|
@@ -204033,7 +204033,7 @@ ${explanation} in non-interactive terminal environments, such as a CI environmen
|
|
|
204033
204033
|
* is specified it will try to load a default environment.
|
|
204034
204034
|
*/
|
|
204035
204035
|
async loadEnvironmentForCommand(path4, environmentsFileName, specifiedEnvironment) {
|
|
204036
|
-
let { loadEnvironment: loadEnvironment2 } = await import("./environments-
|
|
204036
|
+
let { loadEnvironment: loadEnvironment2 } = await import("./environments-L53AHQLZ.js");
|
|
204037
204037
|
return specifiedEnvironment ? { environment: await loadEnvironment2(specifiedEnvironment, environmentsFileName, { from: path4 }), isDefaultEnvironment: !1 } : { environment: await loadEnvironment2("default", environmentsFileName, { from: path4, silent: !0 }), isDefaultEnvironment: !0 };
|
|
204038
204038
|
}
|
|
204039
204039
|
};
|
|
@@ -204056,7 +204056,7 @@ function reportEnvironmentApplication(noDefaultsFlags, flagsWithEnvironments, en
|
|
|
204056
204056
|
if (Object.keys(changes).length === 0)
|
|
204057
204057
|
return;
|
|
204058
204058
|
let items = Object.entries(changes).map(([name, value]) => `${name}: ${value}`);
|
|
204059
|
-
import("./ui-
|
|
204059
|
+
import("./ui-DVZCQR62.js").then(({ renderInfo: renderInfo2 }) => {
|
|
204060
204060
|
renderInfo2({
|
|
204061
204061
|
headline: ["Using applicable flags from", { userInput: environmentName }, "environment:"],
|
|
204062
204062
|
body: [{ list: { items } }]
|
|
@@ -204107,7 +204107,7 @@ async function removeDuplicatedPlugins(config2) {
|
|
|
204107
204107
|
let plugins = Array.from(config2.plugins.values()), bundlePlugins = ["@shopify/app", "@shopify/plugin-cloudflare"], pluginsToRemove = plugins.filter((plugin) => bundlePlugins.includes(plugin.name));
|
|
204108
204108
|
if (pluginsToRemove.length > 0) {
|
|
204109
204109
|
let commandsToRun = pluginsToRemove.map((plugin) => ` - shopify plugins remove ${plugin.name}`).join(`
|
|
204110
|
-
`), { renderWarning: renderWarning2 } = await import("./ui-
|
|
204110
|
+
`), { renderWarning: renderWarning2 } = await import("./ui-DVZCQR62.js");
|
|
204111
204111
|
renderWarning2({
|
|
204112
204112
|
headline: `Unsupported plugins detected: ${pluginsToRemove.map((plugin) => plugin.name).join(", ")}`,
|
|
204113
204113
|
body: [
|
|
@@ -204129,20 +204129,20 @@ init_cjs_shims();
|
|
|
204129
204129
|
init_cjs_shims();
|
|
204130
204130
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
204131
204131
|
async function launchCLI(options) {
|
|
204132
|
-
let { isDevelopment: isDevelopment2 } = await import("./local-
|
|
204132
|
+
let { isDevelopment: isDevelopment2 } = await import("./local-YQI64EPC.js"), { ShopifyConfig } = await import("./custom-oclif-loader-Q7GRLCIZ.js"), oclifModule = await import("./lib-NYDMHWZM.js"), { run, flush, Errors: Errors2, settings } = oclifModule.default ?? oclifModule;
|
|
204133
204133
|
isDevelopment2() && (settings.debug = !0);
|
|
204134
204134
|
try {
|
|
204135
204135
|
let config2 = new ShopifyConfig({ root: fileURLToPath2(options.moduleURL) });
|
|
204136
204136
|
await config2.load(), options.lazyCommandLoader && config2.setLazyCommandLoader(options.lazyCommandLoader);
|
|
204137
204137
|
let argv = options.argv ?? process.argv.slice(2), passthroughIndex = argv.indexOf("--");
|
|
204138
204138
|
if ((passthroughIndex === -1 ? argv : argv.slice(0, passthroughIndex)).includes("--json-schema") || isTruthy(process.env.SHOPIFY_FLAG_JSON_SCHEMA)) {
|
|
204139
|
-
let { printCommandJsonSchema } = await import("./command-json-schema-
|
|
204139
|
+
let { printCommandJsonSchema } = await import("./command-json-schema-RCC6ZVRF.js");
|
|
204140
204140
|
await printCommandJsonSchema(config2, argv, options.lazyCommandLoader);
|
|
204141
204141
|
return;
|
|
204142
204142
|
}
|
|
204143
204143
|
await run(argv, config2), await flush();
|
|
204144
204144
|
} catch (error) {
|
|
204145
|
-
let { errorHandler } = await import("./error-handler-
|
|
204145
|
+
let { errorHandler } = await import("./error-handler-DRCELNRR.js");
|
|
204146
204146
|
await errorHandler(error);
|
|
204147
204147
|
let handledError = error;
|
|
204148
204148
|
return Errors2.handle({
|
|
@@ -204160,7 +204160,7 @@ var import_core3 = __toESM(require_lib());
|
|
|
204160
204160
|
async function exitIfOldNodeVersion(versions2 = process.versions) {
|
|
204161
204161
|
let nodeVersion = versions2.node, nodeMajorVersion = Number(nodeVersion.split(".")[0]);
|
|
204162
204162
|
if (nodeMajorVersion < 22) {
|
|
204163
|
-
let { renderError: renderError2 } = await import("./ui-
|
|
204163
|
+
let { renderError: renderError2 } = await import("./ui-DVZCQR62.js");
|
|
204164
204164
|
renderError2({
|
|
204165
204165
|
headline: "Upgrade to a supported Node version now.",
|
|
204166
204166
|
body: [
|
|
@@ -204187,7 +204187,7 @@ async function runCLI(options, launchCLI2 = launchCLI, argv = process.argv, env2
|
|
|
204187
204187
|
return setupEnvironmentVariables(options, argv, env2), options.runInCreateMode && await addInitToArgvWhenRunningCreateCLI(options, argv), forceNoColor(argv, env2), await exitIfOldNodeVersion(versions2), launchCLI2({ moduleURL: options.moduleURL, lazyCommandLoader: options.lazyCommandLoader });
|
|
204188
204188
|
}
|
|
204189
204189
|
async function addInitToArgvWhenRunningCreateCLI(options, argv = process.argv) {
|
|
204190
|
-
let { findUpAndReadPackageJson: findUpAndReadPackageJson2 } = await import("./node-package-manager-
|
|
204190
|
+
let { findUpAndReadPackageJson: findUpAndReadPackageJson2 } = await import("./node-package-manager-GY7LMT5D.js"), { moduleDirectory: moduleDirectory2 } = await import("./path-2KZ7FSGN.js"), name = (await findUpAndReadPackageJson2(moduleDirectory2(options.moduleURL))).content.name.replace("@shopify/create-", "");
|
|
204191
204191
|
if (argv.findIndex((arg) => arg.includes("init")) === -1) {
|
|
204192
204192
|
let initIndex2 = argv.findIndex((arg) => arg.match(new RegExp(`bin(\\/|\\\\)+(create-${name}|dev|run)`))) + 1;
|
|
204193
204193
|
argv.splice(initIndex2, 0, "init");
|
|
@@ -217373,7 +217373,7 @@ init_cjs_shims();
|
|
|
217373
217373
|
|
|
217374
217374
|
// ../cli-kit/dist/public/node/liquid.js
|
|
217375
217375
|
init_cjs_shims();
|
|
217376
|
-
var import_liquidjs = __toESM(require_liquid_node()
|
|
217376
|
+
var import_liquidjs = __toESM(require_liquid_node());
|
|
217377
217377
|
function renderLiquidTemplate(templateContent, data) {
|
|
217378
217378
|
let engine = new import_liquidjs.Liquid();
|
|
217379
217379
|
return engine.render(engine.parse(templateContent), data);
|
|
@@ -220472,7 +220472,7 @@ init_cjs_shims();
|
|
|
220472
220472
|
init_cjs_shims();
|
|
220473
220473
|
var import_core29 = __toESM(require_lib(), 1), blockingLevels = ["high", "medium", "low", "none"], appSecurityBlockingFlag = {
|
|
220474
220474
|
blocking: import_core29.Flags.custom({
|
|
220475
|
-
description: "The minimum finding severity that causes a non-zero exit code.",
|
|
220475
|
+
description: "The minimum finding severity that causes a non-zero exit code: high, medium, or low. Defaults to none, which never fails on findings.",
|
|
220476
220476
|
options: blockingLevels,
|
|
220477
220477
|
default: "none",
|
|
220478
220478
|
env: "SHOPIFY_FLAG_APP_SECURITY_BLOCKING"
|
|
@@ -220595,7 +220595,7 @@ Do not report:
|
|
|
220595
220595
|
`,
|
|
220596
220596
|
`---
|
|
220597
220597
|
id: APP_PROXY_LIQUID_INJECTION
|
|
220598
|
-
version:
|
|
220598
|
+
version: 3
|
|
220599
220599
|
severity: high
|
|
220600
220600
|
precedence: prefer-agent
|
|
220601
220601
|
---
|
|
@@ -220700,10 +220700,10 @@ Do not report:
|
|
|
220700
220700
|
- Protocol-only canonicalization concerns with no demonstrated app-local exploit path
|
|
220701
220701
|
- Test handlers
|
|
220702
220702
|
`,
|
|
220703
|
-
"---\nid: COMMITTED_SECRET\nversion:
|
|
220703
|
+
"---\nid: COMMITTED_SECRET\nversion: 4\nseverity: high\nprecedence: union\n---\n\n# Committed Secret\n\nInspect files skipped by deterministic secret scanning for committed credentials. Never quote or reproduce a secret; cite only the file and redacted credential kind, and recommend rotation.\n\nDo not report placeholders, public client identifiers (`SHOPIFY_API_KEY`, Stripe `pk_`), or files git confirms are untracked and ignored. Template env files (`.env.example`, `.sample`, `.template`, `.dist`) are findings only when they contain a known credential format.\n",
|
|
220704
220704
|
`---
|
|
220705
220705
|
id: CREDENTIAL_BROWSER_LEAKAGE
|
|
220706
|
-
version:
|
|
220706
|
+
version: 2
|
|
220707
220707
|
severity: high
|
|
220708
220708
|
precedence: prefer-agent
|
|
220709
220709
|
---
|
|
@@ -220714,7 +220714,7 @@ Trace credentials, access tokens, session tokens, and client secrets into loader
|
|
|
220714
220714
|
`,
|
|
220715
220715
|
`---
|
|
220716
220716
|
id: CREDENTIAL_LOG_LEAKAGE
|
|
220717
|
-
version:
|
|
220717
|
+
version: 2
|
|
220718
220718
|
severity: high
|
|
220719
220719
|
precedence: prefer-agent
|
|
220720
220720
|
---
|
|
@@ -221025,7 +221025,7 @@ Do not report:
|
|
|
221025
221025
|
`,
|
|
221026
221026
|
`---
|
|
221027
221027
|
id: DEPRECATED_SCRIPT_TAG_SCOPE
|
|
221028
|
-
version:
|
|
221028
|
+
version: 2
|
|
221029
221029
|
severity: medium
|
|
221030
221030
|
precedence: prefer-agent
|
|
221031
221031
|
---
|
|
@@ -221036,7 +221036,7 @@ Inspect parsed app scopes and JavaScript/TypeScript Admin API operations for dep
|
|
|
221036
221036
|
`,
|
|
221037
221037
|
`---
|
|
221038
221038
|
id: EOL_API_VERSION
|
|
221039
|
-
version:
|
|
221039
|
+
version: 2
|
|
221040
221040
|
severity: low
|
|
221041
221041
|
precedence: prefer-agent
|
|
221042
221042
|
---
|
|
@@ -221045,7 +221045,7 @@ precedence: prefer-agent
|
|
|
221045
221045
|
|
|
221046
221046
|
Inspect the selected \`shopify.app*.toml\` plus React Router \`app/shopify.server.*\` declarations. Shopify publishes quarterly versions in January, April, July, and October and supports each stable version for 12 months; the app security check allows a documented 30-day extension grace period before reporting it as end-of-life. Cite the exact declaration. For malformed config, computed \`ApiVersion\` values, or a Shopify-announced exceptional extension, inspect the source and current lifecycle policy rather than inferring from unrelated constants.
|
|
221047
221047
|
`,
|
|
221048
|
-
"---\nid: EXPIRING_OFFLINE_TOKEN\nversion:
|
|
221048
|
+
"---\nid: EXPIRING_OFFLINE_TOKEN\nversion: 2\nseverity: medium\nprecedence: prefer-agent\n---\n\n# Expiring Offline Token\n\nFor supported React Router apps, verify `expiringOfflineAccessTokens` is enabled and the selected session storage persists `expires`, `refreshToken`, and `refreshTokenExpires` metadata needed for refresh and rotation. `isOnline: false` selects an offline session; it does not disable token expiry and is not a finding. Report an explicit `expiringOfflineAccessTokens: false`. Treat absent or computed flags, custom storage, and ambiguous Prisma schemas as unresolved investigation: inspect storage adapters, migrations, and serialization before returning a clean result. Config-only and unsupported frameworks are handled by the runtime applicability boundary.\n",
|
|
221049
221049
|
`---
|
|
221050
221050
|
id: INSECURE_WEBHOOK_URL
|
|
221051
221051
|
version: 2
|
|
@@ -221060,17 +221060,24 @@ Inspect webhook destinations and OAuth redirect URLs in the selected Shopify app
|
|
|
221060
221060
|
"---\nid: LIQUID_UNSAFE_RENDER\nversion: 1\nseverity: medium\nprecedence: union\n---\n\n# Liquid Unsafe Render\n\nInspect only theme-extension Liquid/HTML files the parser could not analyze. Liquid output is not automatically HTML-escaped. Check the destination: use `escape`/`escape_once` for HTML text and ordinary attributes, `json` when embedding a value as JavaScript data, and `metafield_tag` only for supported rich metafield rendering in HTML content. HTML escaping is not sufficient for event handlers, `srcdoc`, or a `<script src>` URL; avoid merchant-controlled output there and validate any dynamic URL. An ordinary non-script `src` attribute is not itself an executable sink. `{% raw %}` suppresses Liquid parsing, so apparent output inside it is literal text. Follow rendered snippets and inspect ambiguous filters or parser failures before reporting.\n",
|
|
221061
221061
|
`---
|
|
221062
221062
|
id: METAFIELD_OFFLINE_TOKEN
|
|
221063
|
-
version:
|
|
221063
|
+
version: 2
|
|
221064
221064
|
severity: high
|
|
221065
221065
|
---
|
|
221066
221066
|
|
|
221067
221067
|
# Metafield Offline Token
|
|
221068
221068
|
|
|
221069
|
-
Trace metafield writes and determine
|
|
221069
|
+
Trace metafield and metaobject writes and determine their token provenance. Report a write that runs in an offline Admin API context that no verified request for the same shop authorizes: for example \`unauthenticated.admin(shop)\` or a stored offline session reached from an unauthenticated route, an unverified webhook or app proxy request, or a job whose shop or values come from request-controlled input.
|
|
221070
|
+
|
|
221071
|
+
An offline session returned by \`authenticate.admin(request)\` (the React Router SDK default when \`useOnlineTokens\` isn't set) is a verified, shop-bound context: Shopify verified the session token and the staff member's access to the app. Don't report a write only because its token is offline or because an online token would add user attribution or expiry. Report it when:
|
|
221072
|
+
|
|
221073
|
+
- the app defines its own per-user permission, for example an app-level role or allowlist check, and this write bypasses it. Name that permission.
|
|
221074
|
+
- request-controlled values reach an app-reserved (\`$app\`) metafield or metaobject that the app treats as trusted state and merchants can't edit themselves, for example a definition with \`access.admin = "merchant_read"\` or no merchant access, or billing, entitlement or feature-flag data. Writing to the \`$app\` namespace isn't safe by itself.
|
|
221075
|
+
|
|
221076
|
+
Confirm token provenance and compensating authorization before reporting.
|
|
221070
221077
|
`,
|
|
221071
221078
|
`---
|
|
221072
221079
|
id: MISSING_AUTHORIZATION_CHECK
|
|
221073
|
-
version:
|
|
221080
|
+
version: 3
|
|
221074
221081
|
severity: high
|
|
221075
221082
|
---
|
|
221076
221083
|
|
|
@@ -221127,6 +221134,16 @@ actions.
|
|
|
221127
221134
|
changes, direct URLs that survive permission downgrades, and backend actions
|
|
221128
221135
|
that remain callable after a feature is hidden or disabled in the UI.
|
|
221129
221136
|
|
|
221137
|
+
## Shopify embedded apps (React Router)
|
|
221138
|
+
|
|
221139
|
+
\`authenticate.admin(request)\` verifies the Shopify session token, so the caller is a staff member of that shop whom Shopify has granted access to this app. With offline tokens (the SDK default), Admin API calls use the app's granted scopes rather than the staff member's own permissions. That is Shopify's standard embedded app model, not a finding by itself. Report a handler only when it skips a concrete authorization boundary inside the app:
|
|
221140
|
+
|
|
221141
|
+
- The app defines its own roles, owners, or allowlists (for example a check on \`sessionToken.sub\`, \`onlineAccessInfo.associated_user\`, \`account_owner\`, or an app database role), and a handler that performs the same or a more privileged operation doesn't apply it. Compare each route's loader and action, and sibling routes.
|
|
221142
|
+
- A request-supplied identifier selects an app-owned record that belongs to another user of the app.
|
|
221143
|
+
- The app uses online tokens to respect staff permissions on some paths, but a privileged path falls back to an offline session.
|
|
221144
|
+
|
|
221145
|
+
If none of these applies, record the check as executed with no finding.
|
|
221146
|
+
|
|
221130
221147
|
## What to report
|
|
221131
221148
|
|
|
221132
221149
|
For each action that exposes a concrete unauthorized operation beyond authentication:
|
|
@@ -221530,7 +221547,7 @@ Do not report:
|
|
|
221530
221547
|
`,
|
|
221531
221548
|
`---
|
|
221532
221549
|
id: REQUEST_CONTROLLED_ADMIN_CONTEXT
|
|
221533
|
-
version:
|
|
221550
|
+
version: 4
|
|
221534
221551
|
severity: high
|
|
221535
221552
|
precedence: prefer-agent
|
|
221536
221553
|
---
|
|
@@ -222129,17 +222146,7 @@ Do not report:
|
|
|
222129
222146
|
- \`exec()\` with literal strings (no user input)
|
|
222130
222147
|
- Shell calls with properly escaped arguments
|
|
222131
222148
|
`,
|
|
222132
|
-
|
|
222133
|
-
id: STATIC_FRAME_ANCESTORS
|
|
222134
|
-
version: 1
|
|
222135
|
-
severity: high
|
|
222136
|
-
precedence: prefer-agent
|
|
222137
|
-
---
|
|
222138
|
-
|
|
222139
|
-
# Static Frame Ancestors
|
|
222140
|
-
|
|
222141
|
-
Inspect embedded-app Content-Security-Policy construction. Report wildcard or static cross-shop frame-ancestors policies; accept a policy derived safely for the authenticated shop plus Shopify Admin.
|
|
222142
|
-
`,
|
|
222149
|
+
"---\nid: STATIC_FRAME_ANCESTORS\nversion: 2\nseverity: high\nprecedence: prefer-agent\n---\n\n# Static Frame Ancestors\n\nInspect embedded-app Content-Security-Policy construction, including headers that app code sets after or instead of the SDK's. Report wildcard or static cross-shop frame-ancestors policies, such as `*`, `https:`, `*.myshopify.com`, or a fixed list of shop domains, whether the value is written literally or built from variables. Accept a policy derived safely for the shop plus Shopify Admin.\n\nThe React Router SDK's `addDocumentResponseHeaders` (from `shopifyApp()`, usually called in `entry.server`) is the accepted pattern: it sets `frame-ancestors` to the sanitized request shop plus Shopify Admin origins, including Shopify-operated development admin origins. Don't report it unless app code overrides or extends its header.\n",
|
|
222143
222150
|
`---
|
|
222144
222151
|
id: TEXT_SETTING_HTML_SMUGGLING
|
|
222145
222152
|
version: 2
|
|
@@ -222334,7 +222341,7 @@ Do not report:
|
|
|
222334
222341
|
`,
|
|
222335
222342
|
`---
|
|
222336
222343
|
id: UNAUTHENTICATED_ENDPOINT
|
|
222337
|
-
version:
|
|
222344
|
+
version: 3
|
|
222338
222345
|
severity: high
|
|
222339
222346
|
precedence: union
|
|
222340
222347
|
---
|
|
@@ -222471,7 +222478,7 @@ pack instructions.
|
|
|
222471
222478
|
`,
|
|
222472
222479
|
`---
|
|
222473
222480
|
id: UNSAFE_INNERHTML
|
|
222474
|
-
version:
|
|
222481
|
+
version: 3
|
|
222475
222482
|
severity: high
|
|
222476
222483
|
precedence: union
|
|
222477
222484
|
---
|
|
@@ -222745,7 +222752,10 @@ For each check:
|
|
|
222745
222752
|
5. Keep the check \`id\` and \`version\` exactly as they appear in {{AGENT_CHECKS_PATH}}.
|
|
222746
222753
|
6. Include concise evidence citations. Never include a detected secret value or unnecessary personal data.
|
|
222747
222754
|
|
|
222748
|
-
A check with no verified issue must not produce a fabricated finding.
|
|
222755
|
+
A check with no verified issue must not produce a fabricated finding. Choose the status from what you were able to investigate, not from whether you can prove a negative:
|
|
222756
|
+
|
|
222757
|
+
- \`executed\`: you traced the paths the check directs you to and found no concrete issue. This includes code that follows the safe pattern the check or its \`docs_url\` describes. A candidate is ruled out when the code establishes the boundary the check asks about, even if you can't rule out hypothetical policies or requirements that the repository doesn't define.
|
|
222758
|
+
- \`unresolved\`: you couldn't complete the investigation, for example because required code was unreadable, out of scope, or outside the repository and you couldn't inspect it, or you found a specific candidate whose boundary you could neither establish nor show to be missing. Name the candidate's file and line, or the missing input, in the reason.
|
|
222749
222759
|
|
|
222750
222760
|
### 4. Write one findings document
|
|
222751
222761
|
|
|
@@ -222789,7 +222799,7 @@ Write a single JSON document that covers every check you ran:
|
|
|
222789
222799
|
- \`status\` is one of:
|
|
222790
222800
|
- \`executed\`: you investigated the check, whether or not it produced findings.
|
|
222791
222801
|
- \`not_applicable\`: the capability the check covers is absent. It can't have findings.
|
|
222792
|
-
- \`unresolved\`: you couldn't
|
|
222802
|
+
- \`unresolved\`: you couldn't complete the investigation, or you named a specific candidate whose boundary is still unclear. An unresolved check didn't pass; never describe it as passing.
|
|
222793
222803
|
- \`not_applicable\` and \`unresolved\` require a \`reason\` with a short \`code\` and a \`message\`.
|
|
222794
222804
|
- Each finding needs \`file\`, \`line\` (1 or greater), \`message\`, and at least one \`evidence\` item with \`file\`, \`line\`, and \`quote\`.
|
|
222795
222805
|
- Optional finding fields: \`snippet\`, \`confidence\` (\`high\`, \`medium\`, or \`low\`), \`reasoning\`, and \`suppression\` (\`{"justification": "..."}\`).
|
|
@@ -223182,11 +223192,11 @@ var CHECK_DOCS_BASE_URL = "https://shopify.dev/docs/apps/build/security/app-secu
|
|
|
223182
223192
|
},
|
|
223183
223193
|
{
|
|
223184
223194
|
id: "METAFIELD_OFFLINE_TOKEN",
|
|
223185
|
-
title: "Metafield write
|
|
223195
|
+
title: "Metafield write in an unverified offline token context",
|
|
223186
223196
|
severity: "high",
|
|
223187
223197
|
points: -15,
|
|
223188
|
-
description: "
|
|
223189
|
-
fix: "
|
|
223198
|
+
description: "Investigates metafield and metaobject writes made with an offline Admin API context that no verified request for the same shop authorizes.",
|
|
223199
|
+
fix: "Make the write from a request verified with authenticate.admin(request), or derive the shop and values from a verified source.",
|
|
223190
223200
|
guide: "https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens",
|
|
223191
223201
|
docsUrl: `${CHECK_DOCS_BASE_URL}/metafield-offline-token`
|
|
223192
223202
|
},
|
|
@@ -223326,7 +223336,7 @@ var CHECK_DOCS_BASE_URL = "https://shopify.dev/docs/apps/build/security/app-secu
|
|
|
223326
223336
|
title: "Embedded app frame-ancestors uses a wildcard",
|
|
223327
223337
|
severity: "high",
|
|
223328
223338
|
points: -12,
|
|
223329
|
-
description: "Detects
|
|
223339
|
+
description: "Detects wildcard or static cross-shop frame-ancestors policies in embedded app code, whether written literally or built from variables.",
|
|
223330
223340
|
fix: "Restrict frame-ancestors to Shopify Admin and the authenticated shop origin.",
|
|
223331
223341
|
guide: "https://shopify.dev/docs/apps/build/security/set-up-iframe-protection",
|
|
223332
223342
|
requires: "embedded_app",
|
|
@@ -223525,7 +223535,8 @@ Rules:
|
|
|
223525
223535
|
- Record unresolved and not_applicable checks with a reason: { "code": "...", "message": "..." }.
|
|
223526
223536
|
- A not_applicable check can't have findings.
|
|
223527
223537
|
- An unsupported or unresolved check didn't pass. Never describe it as passing or complete.
|
|
223528
|
-
- If you can't prove exploitability or affected authority, record the check as unresolved
|
|
223538
|
+
- If you can't prove exploitability or affected authority for a specific candidate, don't report a finding: record the check as unresolved and name the candidate in the reason.
|
|
223539
|
+
- If you investigated and the code establishes the boundary the check asks about, record the check as executed. An unprovable hypothetical is not a reason to leave a check unresolved.
|
|
223529
223540
|
- Don't report things you couldn't confirm \u2014 uncertainty is not a finding.`, catalogEntry = (checkId) => {
|
|
223530
223541
|
let entry = RULE_CATALOG.find((candidate) => candidate.id === checkId);
|
|
223531
223542
|
if (!entry)
|
|
@@ -224270,6 +224281,13 @@ function readRepositoryFile(absolutePath) {
|
|
|
224270
224281
|
let located = configuredReader().explicitInputs.has(path4) ? explicitInputPath(path4) : containedRepositoryPath(path4), result = located.path ? readBoundedFile(located.path) : located.failure;
|
|
224271
224282
|
return repositoryFileCache.set(path4, result), result.ok || recordSkippedFile(path4, result), result;
|
|
224272
224283
|
}
|
|
224284
|
+
function readFileContainedBy(directory, path4) {
|
|
224285
|
+
let inspected = inspectRepositoryPath(directory, path4);
|
|
224286
|
+
if (inspected.status === "missing")
|
|
224287
|
+
return;
|
|
224288
|
+
let result = inspected.status === "file" ? readBoundedFile(inspected.path) : repositoryPathFailure(inspected.reason);
|
|
224289
|
+
return result.ok || recordSkippedFile(joinPath(directory, path4), result), result;
|
|
224290
|
+
}
|
|
224273
224291
|
function readRepositoryText(absolutePath) {
|
|
224274
224292
|
let result = readRepositoryFile(absolutePath);
|
|
224275
224293
|
return result.ok ? result.content.toString() : void 0;
|
|
@@ -224372,15 +224390,14 @@ function isProbablyBinary(content) {
|
|
|
224372
224390
|
return sample.length > 0 && suspiciousControlBytes / sample.length > 0.1;
|
|
224373
224391
|
}
|
|
224374
224392
|
function findSensitiveFiles(appRoot, repositoryFiles, selectedAppConfigPath) {
|
|
224375
|
-
return repositoryFiles.filter(isSensitiveFile).filter((path4) => !LOCKFILE_MANAGERS.has(path4)).filter((path4) => !isValidFormatAppConfigurationFileName(basename(path4)) || path4 === selectedAppConfigPath).flatMap((path4) => {
|
|
224393
|
+
return repositoryFiles.filter(isSensitiveFile).filter((path4) => !LOCKFILE_MANAGERS.has(basename(path4))).filter((path4) => !isValidFormatAppConfigurationFileName(basename(path4)) || path4 === selectedAppConfigPath).flatMap((path4) => {
|
|
224376
224394
|
let absolutePath = joinPath(appRoot, path4), result = readRepositoryFile(absolutePath);
|
|
224377
224395
|
return result.ok ? isProbablyBinary(result.content) ? [] : [{ path: path4, absolutePath, ext: extname(path4), content: result.content.toString() }] : [{ path: path4, absolutePath, ext: extname(path4), content: void 0 }];
|
|
224378
224396
|
});
|
|
224379
224397
|
}
|
|
224380
|
-
function
|
|
224398
|
+
function dependencyAutomationRoot(appRoot) {
|
|
224381
224399
|
let marker = findRepositoryMarker(appRoot);
|
|
224382
|
-
|
|
224383
|
-
return marker.status === "ambiguous" ? marker.reason : marker.directory === appRoot ? void 0 : "App root is nested below a parent Git repository";
|
|
224400
|
+
return marker.status === "ambiguous" ? { unresolvedReason: marker.reason } : { directory: marker.status === "found" ? marker.directory : appRoot };
|
|
224384
224401
|
}
|
|
224385
224402
|
function findDependencyAutomationInputs(appRoot, gatheredPaths) {
|
|
224386
224403
|
let canonicalRoot;
|
|
@@ -224390,25 +224407,28 @@ function findDependencyAutomationInputs(appRoot, gatheredPaths) {
|
|
|
224390
224407
|
} catch (error) {
|
|
224391
224408
|
return { files: [], unresolvedReason: inspectErrorReason("app root", error) };
|
|
224392
224409
|
}
|
|
224393
|
-
let
|
|
224394
|
-
if (
|
|
224395
|
-
return { files: [], unresolvedReason:
|
|
224396
|
-
let gathered = new Set(gatheredPaths), files = [], unresolvedReason;
|
|
224397
|
-
for (let
|
|
224398
|
-
|
|
224399
|
-
|
|
224400
|
-
let absolutePath = joinPath(canonicalRoot, relative), result = readRepositoryFile(absolutePath);
|
|
224401
|
-
if (!result.ok) {
|
|
224402
|
-
unresolvedReason ??= result.reason === "too_large" ? `${relative} is too large to inspect` : result.detail ?? `Could not read ${relative}`;
|
|
224410
|
+
let configurationRoot = dependencyAutomationRoot(canonicalRoot);
|
|
224411
|
+
if ("unresolvedReason" in configurationRoot)
|
|
224412
|
+
return { files: [], unresolvedReason: configurationRoot.unresolvedReason };
|
|
224413
|
+
let repositoryRoot = configurationRoot.directory, { scanDirectories } = configuredReader(), isScanned = (absolutePath) => repositoryRoot === canonicalRoot || scanDirectories.some((directory) => isSubpath(directory, absolutePath)), gathered = new Set(gatheredPaths), files = [], unresolvedReason;
|
|
224414
|
+
for (let configurationPath of DEPENDENCY_AUTOMATION_CONFIG_PATHS) {
|
|
224415
|
+
let absolutePath = joinPath(repositoryRoot, configurationPath), relative = normalizePath(relativePath(canonicalRoot, absolutePath)), scanned = isScanned(absolutePath);
|
|
224416
|
+
if (scanned && !gathered.has(relative))
|
|
224403
224417
|
continue;
|
|
224418
|
+
let result = scanned ? readRepositoryFile(absolutePath) : readFileContainedBy(repositoryRoot, configurationPath);
|
|
224419
|
+
if (result !== void 0) {
|
|
224420
|
+
if (!result.ok) {
|
|
224421
|
+
unresolvedReason ??= result.reason === "too_large" ? `${relative} is too large to inspect` : result.detail ?? `Could not read ${relative}`;
|
|
224422
|
+
continue;
|
|
224423
|
+
}
|
|
224424
|
+
files.push({
|
|
224425
|
+
path: relative,
|
|
224426
|
+
absolutePath,
|
|
224427
|
+
ext: extname(relative),
|
|
224428
|
+
content: result.content.toString()
|
|
224429
|
+
});
|
|
224430
|
+
break;
|
|
224404
224431
|
}
|
|
224405
|
-
files.push({
|
|
224406
|
-
path: relative,
|
|
224407
|
-
absolutePath,
|
|
224408
|
-
ext: extname(relative),
|
|
224409
|
-
content: result.content.toString()
|
|
224410
|
-
});
|
|
224411
|
-
break;
|
|
224412
224432
|
}
|
|
224413
224433
|
return files.length > 0 ? { files } : { files, ...unresolvedReason ? { unresolvedReason } : {} };
|
|
224414
224434
|
}
|
|
@@ -224454,6 +224474,7 @@ function findManifests(appRoot, discoveredPaths) {
|
|
|
224454
224474
|
|
|
224455
224475
|
// ../app/dist/cli/services/app-security-engine/capabilities/detect.js
|
|
224456
224476
|
init_cjs_shims();
|
|
224477
|
+
var REACT_ROUTER_PACKAGE = "@shopify/shopify-app-react-router";
|
|
224457
224478
|
function detectCapabilities(appToml, extensions, sourceFiles, appTomls = appToml ? [appToml] : []) {
|
|
224458
224479
|
let themeExtension = extensions.some((extension) => extension.type === "theme"), appEmbed = extensions.some((extension) => extension.type === "theme" && hasAppEmbedBlock(extension)), embeddedApp = appTomls.some((configuration) => configuration.raw.embedded === !0), scriptTags = sourceFiles.some((file) => file.content ? /script[_-]?tags?|ScriptTag/i.test(file.content) : !1), storefrontMetafieldWrites = sourceFiles.some((file) => file.content ? /metafields?Set|metafields?\/.*(?:POST|PUT|create|update)|write.*metafield|metafield.*write/i.test(file.content) : !1), hasBackend = sourceFiles.some(detectRouteDefinitions);
|
|
224459
224480
|
return {
|
|
@@ -224469,11 +224490,49 @@ function detectCapabilities(appToml, extensions, sourceFiles, appTomls = appToml
|
|
|
224469
224490
|
checkout_extension: extensions.some((extension) => extension.type === "checkout_ui" || extension.type === "checkout_ui_extension")
|
|
224470
224491
|
};
|
|
224471
224492
|
}
|
|
224472
|
-
function
|
|
224473
|
-
let
|
|
224474
|
-
|
|
224475
|
-
|
|
224476
|
-
|
|
224493
|
+
function detectReactRouterRoots(manifests, candidates, otherAppDirectories = []) {
|
|
224494
|
+
let declaringManifests = manifests.filter((manifest) => manifest.dependencies[REACT_ROUTER_PACKAGE] !== void 0 || manifest.devDependencies?.[REACT_ROUTER_PACKAGE] !== void 0);
|
|
224495
|
+
if (declaringManifests.length === 0)
|
|
224496
|
+
return [];
|
|
224497
|
+
let candidatePaths = candidates.map((candidate) => candidate.path);
|
|
224498
|
+
return [.../* @__PURE__ */ new Set([".", ...declaringManifests.map((manifest) => dirname(manifest.path))])].filter((root) => hasReactRouterStructure(root, candidatePaths)).filter((root) => !belongsToOtherApp(root, otherAppDirectories)).sort();
|
|
224499
|
+
}
|
|
224500
|
+
function isReactRouterServerPath(root, path4) {
|
|
224501
|
+
let pathInRoot = pathWithinRoot(root, path4);
|
|
224502
|
+
return pathInRoot !== void 0 && /^app\/shopify\.server\.[cm]?[jt]sx?$/.test(pathInRoot);
|
|
224503
|
+
}
|
|
224504
|
+
function isReactRouterSourcePath(path4, reactRouterRoots, otherAppDirectories) {
|
|
224505
|
+
let otherAppCode = otherAppCodeDirectories(otherAppDirectories).filter((directory) => !isInsideReactRouterRoot(directory, reactRouterRoots));
|
|
224506
|
+
return (!climbsOutOfDirectory(path4) || reactRouterRoots.some((root) => pathWithinRoot(root, path4) !== void 0)) && !otherAppCode.some((directory) => pathWithinRoot(directory, path4) !== void 0);
|
|
224507
|
+
}
|
|
224508
|
+
function isInsideReactRouterRoot(directory, reactRouterRoots) {
|
|
224509
|
+
return reactRouterRoots.some((root) => root === "." ? !climbsOutOfDirectory(directory) : directory === root || pathWithinRoot(root, directory) !== void 0);
|
|
224510
|
+
}
|
|
224511
|
+
function climbsOutOfDirectory(path4) {
|
|
224512
|
+
return path4 === ".." || path4.startsWith("../");
|
|
224513
|
+
}
|
|
224514
|
+
function pathWithinRoot(root, path4) {
|
|
224515
|
+
if (root === ".")
|
|
224516
|
+
return path4;
|
|
224517
|
+
if (!path4.startsWith(`${root}/`))
|
|
224518
|
+
return;
|
|
224519
|
+
let pathInRoot = path4.slice(root.length + 1);
|
|
224520
|
+
return climbsOutOfDirectory(pathInRoot) ? void 0 : pathInRoot;
|
|
224521
|
+
}
|
|
224522
|
+
function hasReactRouterStructure(root, paths) {
|
|
224523
|
+
return paths.some((path4) => pathWithinRoot(root, path4)?.startsWith("app/routes/")) && paths.some((path4) => isReactRouterServerPath(root, path4));
|
|
224524
|
+
}
|
|
224525
|
+
function belongsToOtherApp(root, otherAppDirectories) {
|
|
224526
|
+
return root === "." ? !1 : otherAppDirectories.includes(root) || otherAppCodeDirectories(otherAppDirectories).some((directory) => root === directory || pathWithinRoot(directory, root) !== void 0);
|
|
224527
|
+
}
|
|
224528
|
+
function otherAppCodeDirectories(otherAppDirectories) {
|
|
224529
|
+
return otherAppDirectories.flatMap((directory) => isAncestorOfAppDirectory(directory) ? [`${directory}/app`, `${directory}/web`] : [directory]);
|
|
224530
|
+
}
|
|
224531
|
+
function isAncestorOfAppDirectory(directory) {
|
|
224532
|
+
return directory.split("/").every((segment) => segment === "..");
|
|
224533
|
+
}
|
|
224534
|
+
function detectProject(extensions, candidates, reactRouterRoots) {
|
|
224535
|
+
let reactRouter = reactRouterRoots.length > 0, themeExtensions = extensions.filter((extension) => extension.type === "theme"), themeExtension = themeExtensions.length > 0, themePaths = new Set(themeExtensions.flatMap((extension) => extension.files.map((file) => file.path))), hasSources = candidates.length > 0, surface;
|
|
224477
224536
|
reactRouter && themeExtension ? surface = "mixed" : reactRouter ? surface = "react_router" : themeExtension && candidates.some((candidate) => !themePaths.has(candidate.path)) ? surface = "mixed" : themeExtension ? surface = "theme_app_extension" : hasSources ? surface = "unknown" : surface = "config_only";
|
|
224478
224537
|
let framework;
|
|
224479
224538
|
reactRouter ? framework = "react_router" : surface === "config_only" || surface === "theme_app_extension" ? framework = "none" : surface === "mixed" ? framework = "mixed" : framework = "unknown";
|
|
@@ -225133,11 +225192,11 @@ function scanEolApiVersions(context, referenceDate = /* @__PURE__ */ new Date())
|
|
|
225133
225192
|
]);
|
|
225134
225193
|
if (context.detection.framework !== "react_router")
|
|
225135
225194
|
return configIssues;
|
|
225136
|
-
let sourceIssues = context.sourceFiles.flatMap((file) => scanReactRouterApiVersion(file, referenceDate));
|
|
225195
|
+
let sourceIssues = context.sourceFiles.flatMap((file) => scanReactRouterApiVersion(file, context.reactRouterRoots, referenceDate));
|
|
225137
225196
|
return [...configIssues, ...sourceIssues];
|
|
225138
225197
|
}
|
|
225139
|
-
function scanReactRouterApiVersion(file, referenceDate) {
|
|
225140
|
-
if (!file.content ||
|
|
225198
|
+
function scanReactRouterApiVersion(file, reactRouterRoots, referenceDate) {
|
|
225199
|
+
if (!file.content || !reactRouterRoots.some((root) => isReactRouterServerPath(root, file.path)))
|
|
225141
225200
|
return [];
|
|
225142
225201
|
let content = maskStringsExceptVersions(stripComments4(file.content));
|
|
225143
225202
|
return [
|
|
@@ -226082,7 +226141,7 @@ var JAVASCRIPT_EXTENSIONS6 = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mt
|
|
|
226082
226141
|
configRule(missingComplianceWebhooks),
|
|
226083
226142
|
{
|
|
226084
226143
|
id: "MISSING_DEPENDENCY_SECURITY_AUTOMATION",
|
|
226085
|
-
version:
|
|
226144
|
+
version: 2,
|
|
226086
226145
|
lifecycle: "active",
|
|
226087
226146
|
analysisMode: "structured_config",
|
|
226088
226147
|
target: "dependency_automation",
|
|
@@ -226090,7 +226149,7 @@ var JAVASCRIPT_EXTENSIONS6 = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mt
|
|
|
226090
226149
|
},
|
|
226091
226150
|
{
|
|
226092
226151
|
id: "EOL_API_VERSION",
|
|
226093
|
-
version:
|
|
226152
|
+
version: 2,
|
|
226094
226153
|
lifecycle: "active",
|
|
226095
226154
|
analysisMode: "regex",
|
|
226096
226155
|
target: "config_and_source",
|
|
@@ -226098,16 +226157,16 @@ var JAVASCRIPT_EXTENSIONS6 = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mt
|
|
|
226098
226157
|
runner: (context) => scanEolApiVersions(context)
|
|
226099
226158
|
},
|
|
226100
226159
|
{
|
|
226101
|
-
...jsCheck("EXPIRING_OFFLINE_TOKEN", (context) => scanExpiringOfflineTokens(context)),
|
|
226160
|
+
...jsCheck("EXPIRING_OFFLINE_TOKEN", (context) => scanExpiringOfflineTokens(context), "source", 2),
|
|
226102
226161
|
extensions: [...JAVASCRIPT_EXTENSIONS6, ".prisma"]
|
|
226103
226162
|
},
|
|
226104
226163
|
{
|
|
226105
|
-
...jsCheck("UNAUTHENTICATED_ENDPOINT", (context) => scanUnauthenticatedEndpoints(context.sourceFiles), "source",
|
|
226164
|
+
...jsCheck("UNAUTHENTICATED_ENDPOINT", (context) => scanUnauthenticatedEndpoints(context.sourceFiles), "source", 3),
|
|
226106
226165
|
requires: "has_backend"
|
|
226107
226166
|
},
|
|
226108
|
-
jsCheck("REQUEST_CONTROLLED_ADMIN_CONTEXT", (context) => scanRequestControlledAdminContext(context.sourceFiles), "source",
|
|
226167
|
+
jsCheck("REQUEST_CONTROLLED_ADMIN_CONTEXT", (context) => scanRequestControlledAdminContext(context.sourceFiles), "source", 4),
|
|
226109
226168
|
{
|
|
226110
|
-
...configRule(deprecatedScriptTagScope),
|
|
226169
|
+
...configRule(deprecatedScriptTagScope, 2),
|
|
226111
226170
|
target: "config_and_source",
|
|
226112
226171
|
analysisMode: "regex",
|
|
226113
226172
|
extensions: JAVASCRIPT_EXTENSIONS6,
|
|
@@ -226119,14 +226178,14 @@ var JAVASCRIPT_EXTENSIONS6 = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mt
|
|
|
226119
226178
|
configRule(insecureWebhookUrl, 2),
|
|
226120
226179
|
{
|
|
226121
226180
|
id: "COMMITTED_SECRET",
|
|
226122
|
-
version:
|
|
226181
|
+
version: 4,
|
|
226123
226182
|
lifecycle: "active",
|
|
226124
226183
|
analysisMode: "regex",
|
|
226125
226184
|
target: "secrets",
|
|
226126
226185
|
runner: (context) => scanCommittedSecrets(context.sensitiveFiles, context.appRoot, context.gitIgnoreListing)
|
|
226127
226186
|
},
|
|
226128
|
-
jsCheck("CREDENTIAL_LOG_LEAKAGE", (context) => scanCredentialLogLeakage(context.sourceFiles)),
|
|
226129
|
-
jsCheck("CREDENTIAL_BROWSER_LEAKAGE", (context) => scanCredentialBrowserLeakage(context.sourceFiles)),
|
|
226187
|
+
jsCheck("CREDENTIAL_LOG_LEAKAGE", (context) => scanCredentialLogLeakage(context.sourceFiles), "source", 2),
|
|
226188
|
+
jsCheck("CREDENTIAL_BROWSER_LEAKAGE", (context) => scanCredentialBrowserLeakage(context.sourceFiles), "source", 2),
|
|
226130
226189
|
{
|
|
226131
226190
|
id: "LIQUID_UNSAFE_RENDER",
|
|
226132
226191
|
version: 1,
|
|
@@ -226138,16 +226197,16 @@ var JAVASCRIPT_EXTENSIONS6 = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mt
|
|
|
226138
226197
|
runner: (context) => liquidRunner(context, "LIQUID_UNSAFE_RENDER")
|
|
226139
226198
|
},
|
|
226140
226199
|
{
|
|
226141
|
-
...jsCheck("UNSAFE_INNERHTML", unsafeInnerHtmlRunner, "source_and_theme",
|
|
226200
|
+
...jsCheck("UNSAFE_INNERHTML", unsafeInnerHtmlRunner, "source_and_theme", 3),
|
|
226142
226201
|
analysisMode: "regex",
|
|
226143
226202
|
extensions: [...JAVASCRIPT_EXTENSIONS6, ".liquid", ".html"]
|
|
226144
226203
|
},
|
|
226145
226204
|
{
|
|
226146
|
-
...jsCheck("APP_PROXY_LIQUID_INJECTION", (context) => scanAppProxyLiquidInjection(context.sourceFiles), "source",
|
|
226205
|
+
...jsCheck("APP_PROXY_LIQUID_INJECTION", (context) => scanAppProxyLiquidInjection(context.sourceFiles), "source", 3),
|
|
226147
226206
|
requires: "app_proxy"
|
|
226148
226207
|
},
|
|
226149
226208
|
{
|
|
226150
|
-
...jsCheck("STATIC_FRAME_ANCESTORS", (context) => scanStaticFrameAncestors(context.sourceFiles), "app_source"),
|
|
226209
|
+
...jsCheck("STATIC_FRAME_ANCESTORS", (context) => scanStaticFrameAncestors(context.sourceFiles), "app_source", 2),
|
|
226151
226210
|
requires: "embedded_app"
|
|
226152
226211
|
}
|
|
226153
226212
|
];
|
|
@@ -226255,8 +226314,11 @@ function appSourceFiles(context) {
|
|
|
226255
226314
|
let themePaths = new Set(themeFiles(context).map((file) => file.path));
|
|
226256
226315
|
return context.sourceFiles.filter((file) => !themePaths.has(file.path));
|
|
226257
226316
|
}
|
|
226317
|
+
function isReactRouterFilePath(path4, context) {
|
|
226318
|
+
return context.detection.framework !== "react_router" || isReactRouterSourcePath(path4, context.reactRouterRoots, context.otherAppDirectories);
|
|
226319
|
+
}
|
|
226258
226320
|
function reactRouterFiles(context) {
|
|
226259
|
-
return appSourceFiles(context);
|
|
226321
|
+
return appSourceFiles(context).filter((file) => isReactRouterFilePath(file.path, context));
|
|
226260
226322
|
}
|
|
226261
226323
|
function selectedFiles(definition, context) {
|
|
226262
226324
|
let configurations = context.appTomls.map((toml) => relativePath(context.appRoot, toml.path).replace(/\\/g, "/"));
|
|
@@ -226363,8 +226425,8 @@ function manifestHasDependencies(manifest) {
|
|
|
226363
226425
|
return Object.keys(manifest.dependencies).length > 0 || Object.keys(manifest.devDependencies ?? {}).length > 0;
|
|
226364
226426
|
}
|
|
226365
226427
|
function skippedInputsForCheck(definition, context, skippedFiles2) {
|
|
226366
|
-
let themeDirectories = context.extensions.filter((extension) => extension.type === "theme").map((extension) => extension.path.replace(/shopify\.extension\.toml$/, "")), isThemePath = (path4) => themeDirectories.some((directory) => path4.startsWith(directory)) || path4.endsWith("shopify.extension.toml"), isSourcePath = (path4) => !isThemePath(path4) && !!definition.extensions?.some((extension) => path4.endsWith(extension)), isConfig = (path4) => /^shopify\.app(?:\.[^/]+)?\.toml$/.test(path4), isDependencyAutomationInput = (path4) => /(^|\/)package\.json$/.test(path4) || context.dependencyAutomation.files.some((file) => file.path === path4), isSecretInput = (file) => !file.detail?.includes("could not be parsed") && (context.sourceCandidates.some((candidate) => candidate.path === file.path) || context.sensitiveFiles.some((sensitiveFile) => sensitiveFile.path === file.path) || /(^|\/)(?:\.env(?:\.[^/]+)?|secrets\.json|credentials\.json)$/.test(file.path));
|
|
226367
|
-
return skippedFiles2.filter((file) => definition.target === "config" ? isConfig(file.path) : definition.target === "dependency_automation" ? isDependencyAutomationInput(file.path) : definition.target === "config_and_source" ? isConfig(file.path) ||
|
|
226428
|
+
let themeDirectories = context.extensions.filter((extension) => extension.type === "theme").map((extension) => extension.path.replace(/shopify\.extension\.toml$/, "")), isThemePath = (path4) => themeDirectories.some((directory) => path4.startsWith(directory)) || path4.endsWith("shopify.extension.toml"), isSourcePath = (path4) => !isThemePath(path4) && !!definition.extensions?.some((extension) => path4.endsWith(extension)), isReactRouterInput = (path4) => isSourcePath(path4) && isReactRouterFilePath(path4, context), isConfig = (path4) => /^shopify\.app(?:\.[^/]+)?\.toml$/.test(path4), isDependencyAutomationInput = (path4) => /(^|\/)package\.json$/.test(path4) || context.dependencyAutomation.files.some((file) => file.path === path4), isSecretInput = (file) => !file.detail?.includes("could not be parsed") && (context.sourceCandidates.some((candidate) => candidate.path === file.path) || context.sensitiveFiles.some((sensitiveFile) => sensitiveFile.path === file.path) || /(^|\/)(?:\.env(?:\.[^/]+)?|secrets\.json|credentials\.json)$/.test(file.path));
|
|
226429
|
+
return skippedFiles2.filter((file) => definition.target === "config" ? isConfig(file.path) : definition.target === "dependency_automation" ? isDependencyAutomationInput(file.path) : definition.target === "config_and_source" ? isConfig(file.path) || isReactRouterInput(file.path) : definition.target === "source" ? isReactRouterInput(file.path) : definition.target === "app_source" ? isSourcePath(file.path) : definition.target === "theme" ? isThemePath(file.path) : definition.target === "source_and_theme" ? isReactRouterInput(file.path) || isThemePath(file.path) : isSecretInput(file));
|
|
226368
226430
|
}
|
|
226369
226431
|
function skippedInputReason(definition, files) {
|
|
226370
226432
|
return {
|
|
@@ -226403,7 +226465,7 @@ async function scan(input, options = {}) {
|
|
|
226403
226465
|
scanDirectories,
|
|
226404
226466
|
explicitInputs: new Set(appConfigFilePath ? [appConfigFilePath] : [])
|
|
226405
226467
|
});
|
|
226406
|
-
let selectedAppConfigPath = appConfigFilePath ? normalizePath(relativePath(appRoot, appConfigFilePath)) : void 0, appToml = appConfigFilePath ? loadAppToml(appConfigFilePath, appRoot) : null, appTomls = appToml ? [appToml] : [], { paths: repositoryFiles, ignoredScanDirectories, otherAppDirectories, listingStatus } = await gatherScanPaths(input, options), extensions = findExtensions(appRoot, repositoryFiles), sourceCandidates = findSourceCandidates(repositoryFiles), sourceFiles = findAppSourceFiles(appRoot, repositoryFiles), sensitiveFiles = findSensitiveFiles(appRoot, repositoryFiles, selectedAppConfigPath), manifestPaths = findManifestPaths(repositoryFiles), manifests = findManifests(appRoot, manifestPaths), dependencyAutomation = manifests.some(manifestHasDependencies) ? findDependencyAutomationInputs(appRoot, repositoryFiles) : { files: [] }, capabilities = detectCapabilities(appToml, extensions, sourceFiles, appTomls),
|
|
226468
|
+
let selectedAppConfigPath = appConfigFilePath ? normalizePath(relativePath(appRoot, appConfigFilePath)) : void 0, appToml = appConfigFilePath ? loadAppToml(appConfigFilePath, appRoot) : null, appTomls = appToml ? [appToml] : [], { paths: repositoryFiles, ignoredScanDirectories, otherAppDirectories, listingStatus } = await gatherScanPaths(input, options), extensions = findExtensions(appRoot, repositoryFiles), sourceCandidates = findSourceCandidates(repositoryFiles), sourceFiles = findAppSourceFiles(appRoot, repositoryFiles), sensitiveFiles = findSensitiveFiles(appRoot, repositoryFiles, selectedAppConfigPath), manifestPaths = findManifestPaths(repositoryFiles), manifests = findManifests(appRoot, manifestPaths), dependencyAutomation = manifests.some(manifestHasDependencies) ? findDependencyAutomationInputs(appRoot, repositoryFiles) : { files: [] }, capabilities = detectCapabilities(appToml, extensions, sourceFiles, appTomls), relativeOtherAppDirectories = otherAppDirectories.map((directory) => normalizePath(relativePath(appRoot, directory))), reactRouterRoots = detectReactRouterRoots(manifests, sourceCandidates, relativeOtherAppDirectories), detection = detectProject(extensions, sourceCandidates, reactRouterRoots), context = {
|
|
226407
226469
|
appRoot,
|
|
226408
226470
|
appToml,
|
|
226409
226471
|
appTomls,
|
|
@@ -226414,6 +226476,8 @@ async function scan(input, options = {}) {
|
|
|
226414
226476
|
sensitiveFiles,
|
|
226415
226477
|
capabilities,
|
|
226416
226478
|
detection,
|
|
226479
|
+
reactRouterRoots,
|
|
226480
|
+
otherAppDirectories: relativeOtherAppDirectories,
|
|
226417
226481
|
sourceCandidates,
|
|
226418
226482
|
gitIgnoreListing: listingStatus
|
|
226419
226483
|
}, issues = [], checksExecuted = [], requiredCheckIds = /* @__PURE__ */ new Set();
|
|
@@ -228932,7 +228996,7 @@ var SecurityCheck = class _SecurityCheck extends base_command_default {
|
|
|
228932
228996
|
env: "SHOPIFY_FLAG_YES"
|
|
228933
228997
|
}),
|
|
228934
228998
|
"skip-instructions": import_core32.Flags.boolean({
|
|
228935
|
-
description: "Don't offer
|
|
228999
|
+
description: "Don't offer the coding-agent instructions after the scan. Use it when you want only the deterministic scan results.",
|
|
228936
229000
|
default: !1,
|
|
228937
229001
|
exclusive: ["yes"],
|
|
228938
229002
|
env: "SHOPIFY_FLAG_APP_SECURITY_SKIP_INSTRUCTIONS"
|