shopify 4.9.3 → 4.9.4
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-APDNTBDE.js +1 -0
- package/dist/analytics-LX52KYSE.js +1 -0
- package/dist/bootstrap.js +1 -1
- package/dist/{chunk-ISU7Q2EB.js → chunk-2PDVBGQH.js} +1 -1
- package/dist/{chunk-3YAD263J.js → chunk-2YVSZFXJ.js} +1 -1
- package/dist/{chunk-KWKRJYTY.js → chunk-35WQTGN3.js} +1 -1
- package/dist/chunk-37BGAWAW.js +2 -0
- package/dist/{chunk-3FCN4NS6.js → chunk-3C4WVKSO.js} +1 -1
- package/dist/{chunk-4LTQEQ4O.js → chunk-3G2ZQ2EF.js} +1 -1
- package/dist/{chunk-XBCFYD3T.js → chunk-3LHE4GIX.js} +1 -1
- package/dist/{chunk-OSGXBBUJ.js → chunk-3V4VL3SD.js} +2 -2
- package/dist/{chunk-MBE23GGW.js → chunk-43URS56H.js} +1 -1
- package/dist/{chunk-H4PXFC7T.js → chunk-45SZ5PSE.js} +1 -1
- package/dist/{chunk-IN634BRF.js → chunk-47IQIIFX.js} +91 -91
- package/dist/{chunk-ADGPOQ5M.js → chunk-4ES7BJEK.js} +1 -1
- package/dist/{chunk-H3N5LKJD.js → chunk-4IFY7V7W.js} +1 -1
- package/dist/{chunk-6BVQSX7D.js → chunk-4JGWKUJN.js} +1 -1
- package/dist/{chunk-SZLNUMWO.js → chunk-4O5GQ3DS.js} +1 -1
- package/dist/{chunk-OUO2T3CC.js → chunk-4O7LK5RG.js} +1 -1
- package/dist/{chunk-6JOZHD34.js → chunk-4OCVXE5Y.js} +1 -1
- package/dist/{chunk-MAQ3SRQF.js → chunk-4TFJ5RW7.js} +1 -1
- package/dist/{chunk-ACGPDJ6Q.js → chunk-4TZCRMBJ.js} +1 -1
- package/dist/{chunk-4Q5TSWQL.js → chunk-4ZNBEMRT.js} +3 -3
- package/dist/{chunk-C4G7OKJD.js → chunk-5BTLE67Z.js} +1 -1
- package/dist/{chunk-JTXQPPKQ.js → chunk-5JIJDUXX.js} +1 -1
- package/dist/{chunk-6ZSI3C4G.js → chunk-66YRAUWX.js} +1 -1
- package/dist/{chunk-GIGA7EU3.js → chunk-67EBF76X.js} +1 -1
- package/dist/{chunk-4J7HQGWI.js → chunk-6A4H3HPY.js} +1 -1
- package/dist/{chunk-DHWY73OS.js → chunk-6HPUOCFF.js} +1 -1
- package/dist/chunk-6IDEDOXW.js +2 -0
- package/dist/{chunk-YWS2OTSC.js → chunk-6IMHGT7F.js} +1 -1
- package/dist/{chunk-ZIL2P7FL.js → chunk-6MC2BEW4.js} +1 -1
- package/dist/chunk-6YDLNP4G.js +15 -0
- package/dist/{chunk-KI3VRWBP.js → chunk-73D5O7A3.js} +1 -1
- package/dist/{chunk-J6BK6SNB.js → chunk-7556AIVG.js} +1 -1
- package/dist/{chunk-4Q7NZI5A.js → chunk-7A3RW2VM.js} +1 -1
- package/dist/{chunk-TFB4JJFQ.js → chunk-7HNRK2CR.js} +1 -1
- package/dist/{chunk-6HWDGTSX.js → chunk-7HWMURCW.js} +1 -1
- package/dist/{chunk-5LONOHNN.js → chunk-7SJHVJ3W.js} +1 -1
- package/dist/{chunk-UGJN5Q6Y.js → chunk-7VAKNVFD.js} +1 -1
- package/dist/{chunk-BWCPDFEY.js → chunk-A2VCTDKH.js} +1 -1
- package/dist/{chunk-3JN6OWIO.js → chunk-ALK66U3O.js} +1 -1
- package/dist/{chunk-CTVOXVXG.js → chunk-AMLCWRHU.js} +1 -1
- package/dist/{chunk-5RJ6KQHZ.js → chunk-AVMNIZAJ.js} +1 -1
- package/dist/{chunk-O7YHDFOO.js → chunk-AYIU6Z4C.js} +1 -1
- package/dist/{chunk-KZ54OSBH.js → chunk-B3M3KIOU.js} +1 -1
- package/dist/{chunk-RJGFFGEN.js → chunk-BIWG3FQQ.js} +1 -1
- package/dist/{chunk-7NHCYOKI.js → chunk-BOE2ST6J.js} +1 -1
- package/dist/chunk-BQ3U3CQV.js +1 -0
- package/dist/{chunk-MWVT5HYW.js → chunk-BVB3DTNE.js} +1 -1
- package/dist/{chunk-FMBC7LZB.js → chunk-C6IBY7NW.js} +1 -1
- package/dist/{chunk-NO3QDX7R.js → chunk-CLD3NZYD.js} +1 -1
- package/dist/{chunk-XMPLZWXP.js → chunk-CQF5NDTJ.js} +1 -1
- package/dist/{chunk-52ZHZWF7.js → chunk-CVFPY2SH.js} +1 -1
- package/dist/{chunk-7IYSJGFF.js → chunk-D3KFKT4D.js} +2 -2
- package/dist/{chunk-IKN6ICJR.js → chunk-DEF2G7S4.js} +1 -1
- package/dist/{chunk-46I3BQZH.js → chunk-DMDLJRLW.js} +1 -1
- package/dist/{chunk-A75SA57D.js → chunk-DMNWSUKV.js} +1 -1
- package/dist/{chunk-HJPAHR2V.js → chunk-DSS4ECIH.js} +1 -1
- package/dist/{chunk-GOBTF2ZH.js → chunk-DYPZX7MW.js} +1 -1
- package/dist/{chunk-MNHZWEXI.js → chunk-EMH7OHCW.js} +1 -1
- package/dist/{chunk-A6KNDSNT.js → chunk-ENQXB532.js} +1 -1
- package/dist/{chunk-PFBESNYV.js → chunk-EO4YJFMY.js} +22 -22
- package/dist/{chunk-7AP5E5CF.js → chunk-F3PZGSOY.js} +1 -1
- package/dist/chunk-FEQHCE2E.js +3 -0
- package/dist/{chunk-UZL4B7LA.js → chunk-FK7GA4QQ.js} +1 -1
- package/dist/{chunk-KZWYFH7M.js → chunk-FO2R6KHG.js} +1 -1
- package/dist/{chunk-WNO2V53C.js → chunk-FOAV642V.js} +1 -1
- package/dist/{chunk-P4MNJULR.js → chunk-FQL2WEKX.js} +1 -1
- package/dist/{chunk-7KCHSZ6H.js → chunk-G7JW4UIM.js} +1 -1
- package/dist/{chunk-A36J5FWZ.js → chunk-GE4MLO3C.js} +1 -1
- package/dist/{chunk-GPJAWTR7.js → chunk-GM4EFJJF.js} +1 -1
- package/dist/chunk-GN66BRYF.js +1 -0
- package/dist/{chunk-HQ4JWC5M.js → chunk-GPRZYFDE.js} +1 -1
- package/dist/{chunk-YKK4PGCQ.js → chunk-GSE6FXZU.js} +1 -1
- package/dist/{chunk-43DSLBSX.js → chunk-GZ7VGCVW.js} +1 -1
- package/dist/{chunk-2WQGEYBB.js → chunk-HASIOUH2.js} +1 -1
- package/dist/chunk-HILYTMVA.js +1 -0
- package/dist/{chunk-MVFBLQ4J.js → chunk-HJHS23Q3.js} +1 -1
- package/dist/{chunk-MYP6QEJW.js → chunk-HLPAVELJ.js} +1 -1
- package/dist/{chunk-MROQ2FRU.js → chunk-HPM3HF7A.js} +1 -1
- package/dist/{chunk-P7YDAQ53.js → chunk-HQJ62PII.js} +1 -1
- package/dist/{chunk-A6NAQ2Y3.js → chunk-HQY6JM6G.js} +1 -1
- package/dist/{chunk-GQZ3KCWJ.js → chunk-HSYMUYEZ.js} +1 -1
- package/dist/{chunk-IJM576XA.js → chunk-I5PQZRYM.js} +1 -1
- package/dist/{chunk-YN3NT2RC.js → chunk-IDKRJ2F4.js} +1 -1
- package/dist/{chunk-QKJ542VO.js → chunk-IGDVKRB4.js} +1 -1
- package/dist/chunk-IPZ6LUJV.js +4 -0
- package/dist/{chunk-JQ3DMPI6.js → chunk-J4J2YXH3.js} +1 -1
- package/dist/{chunk-3U2OMG7L.js → chunk-J4NBPC57.js} +1 -1
- package/dist/chunk-J6LQR2F6.js +7 -0
- package/dist/{chunk-CNHO3T4F.js → chunk-JYGZD4HM.js} +1 -1
- package/dist/{chunk-3FPVTPQO.js → chunk-K3HUQLIC.js} +1 -1
- package/dist/{chunk-CQKZSB7Z.js → chunk-K7D53KSJ.js} +1 -1
- package/dist/{chunk-IH2CB73H.js → chunk-KQFA5YTT.js} +1 -1
- package/dist/{chunk-VWDVWHV7.js → chunk-KTD7VLHL.js} +1 -1
- package/dist/{chunk-JYWFR6VL.js → chunk-LJIJEGND.js} +1 -1
- package/dist/{chunk-AF36J7R7.js → chunk-LJZTSQRD.js} +1 -1
- package/dist/{chunk-CGCM5DB7.js → chunk-LKXFHYLI.js} +1 -1
- package/dist/{chunk-CB6QT5K5.js → chunk-LLPB6JN5.js} +1 -1
- package/dist/{chunk-NC64STMQ.js → chunk-LOWSU4BB.js} +1 -1
- package/dist/{chunk-MMCDBR23.js → chunk-LWU5TKNA.js} +1 -1
- package/dist/{chunk-XZHUUA6U.js → chunk-LYIXRMQJ.js} +1 -1
- package/dist/{chunk-KRL7FE6M.js → chunk-MCNK5SJ6.js} +1 -1
- package/dist/{chunk-ZBMEU2CE.js → chunk-MJZSSCGD.js} +1 -1
- package/dist/{chunk-A76YYGZT.js → chunk-MMQZO2W2.js} +1 -1
- package/dist/{chunk-ME4EYPKZ.js → chunk-MUM34KUM.js} +1 -1
- package/dist/{chunk-KZE6JT42.js → chunk-MW3IMZJ5.js} +1 -1
- package/dist/{chunk-EJ46P6A3.js → chunk-MW3JKGJH.js} +1 -1
- package/dist/{chunk-66JMKA7C.js → chunk-N55MSO3X.js} +5 -5
- package/dist/{chunk-ADL6JSX7.js → chunk-N5AZZYQA.js} +1 -1
- package/dist/{chunk-Z5M2VWQZ.js → chunk-NCRWOW6F.js} +1 -1
- package/dist/{chunk-YZXNXWDR.js → chunk-NCVGCVQ5.js} +1 -1
- package/dist/{chunk-H5ZPH2EC.js → chunk-NGSEF3UX.js} +1 -1
- package/dist/{chunk-Y45QFHVA.js → chunk-NT4A37HG.js} +1 -1
- package/dist/{chunk-EA5LKCRF.js → chunk-NU5WLZ6Y.js} +1 -1
- package/dist/{chunk-73LX4DOM.js → chunk-NYYXH2SX.js} +1 -1
- package/dist/{chunk-HHNMV6GX.js → chunk-O5FFMTJE.js} +1 -1
- package/dist/{chunk-NYVG34OW.js → chunk-OE3HSNSE.js} +1 -1
- package/dist/{chunk-E7PW5SVD.js → chunk-ONF7XXEL.js} +1 -1
- package/dist/{chunk-BHOZAIHS.js → chunk-PB5ZZET5.js} +1 -1
- package/dist/{chunk-KXZQKOKF.js → chunk-PFXNBHQQ.js} +1 -1
- package/dist/{chunk-ZRW47MJT.js → chunk-PG4S5HJJ.js} +1 -1
- package/dist/{chunk-7SIY6GZD.js → chunk-PHG4DASY.js} +1 -1
- package/dist/{chunk-LOTMFUSV.js → chunk-PO5T44H3.js} +1 -1
- package/dist/{chunk-B32O5KET.js → chunk-PP4VCR4Y.js} +1 -1
- package/dist/{chunk-YUM5XGSV.js → chunk-PVAZCZSY.js} +1 -1
- package/dist/chunk-PWS4VFSD.js +1 -0
- package/dist/{chunk-ZZWGNETX.js → chunk-Q2XLIWG4.js} +1 -1
- package/dist/{chunk-VDE7IIYQ.js → chunk-Q37WAD6F.js} +1 -1
- package/dist/{chunk-IR6OW5R2.js → chunk-Q43LBSPT.js} +1 -1
- package/dist/{chunk-XZOSPVMV.js → chunk-QAJZUJR5.js} +1 -1
- package/dist/{chunk-DE5BUQL5.js → chunk-QO5RNGWA.js} +1 -1
- package/dist/chunk-QPQS2YU3.js +1 -0
- package/dist/{chunk-XVOUKPGD.js → chunk-QTLGMJDW.js} +1 -1
- package/dist/{chunk-2E2NYRQH.js → chunk-QUXQM3S3.js} +1 -1
- package/dist/{chunk-PFPVFPHV.js → chunk-R3KSOCMB.js} +1 -1
- package/dist/{chunk-MYIN4HYW.js → chunk-R74WHKHH.js} +1 -1
- package/dist/chunk-R7YJNDXW.js +1 -0
- package/dist/chunk-RJSQMKKS.js +1 -0
- package/dist/{chunk-WS54VCDC.js → chunk-RK2V6OIJ.js} +1 -1
- package/dist/{chunk-O36EB75H.js → chunk-RSC5NQAT.js} +1 -1
- package/dist/{chunk-2S7WRX2J.js → chunk-RUHQNF2W.js} +1 -1
- package/dist/{chunk-CTDETNGR.js → chunk-RXTLNUPF.js} +1 -1
- package/dist/{chunk-YPGZANN3.js → chunk-S36VGP37.js} +1 -1
- package/dist/{chunk-FLPWT4AK.js → chunk-SNE4RHM4.js} +1 -1
- package/dist/{chunk-4RFKEFWW.js → chunk-SQAS5RVZ.js} +1 -1
- package/dist/{chunk-LIE6QSQ2.js → chunk-SZI6G247.js} +1 -1
- package/dist/{chunk-KY4WH4ZK.js → chunk-TFXUDHCP.js} +1 -1
- package/dist/{chunk-TWT5LVEM.js → chunk-TPHHQAUD.js} +1 -1
- package/dist/{chunk-T3LZJIZN.js → chunk-TVCWDEVS.js} +1 -1
- package/dist/{chunk-FKGN6F3F.js → chunk-UEBUGSBT.js} +1 -1
- package/dist/{chunk-UKINO57E.js → chunk-UIC7K2HX.js} +1 -1
- package/dist/{chunk-KEI3OJVP.js → chunk-UNHT4ZFP.js} +1 -1
- package/dist/{chunk-2GYIQINP.js → chunk-UQ6ALA3Y.js} +1 -1
- package/dist/{chunk-KFOU4WLJ.js → chunk-UU6BRRYS.js} +1 -1
- package/dist/{chunk-UYWRJHAJ.js → chunk-UWI76E2L.js} +2 -2
- package/dist/{chunk-GBG6SUK4.js → chunk-VGBUN3CZ.js} +1 -1
- package/dist/{chunk-ADVNRGOU.js → chunk-VMNOSVPT.js} +1 -1
- package/dist/{chunk-OVTWO3W7.js → chunk-VNFUCH6T.js} +1 -1
- package/dist/{chunk-7KVTQDRW.js → chunk-VS7QVU3Z.js} +1 -1
- package/dist/{chunk-CNA5YGXA.js → chunk-W4KE7AO6.js} +1 -1
- package/dist/{chunk-WEB63XPH.js → chunk-WEPO4J2F.js} +1 -1
- package/dist/{chunk-EM6KPZCM.js → chunk-WEXOA4C4.js} +1 -1
- package/dist/{chunk-QKSN6ZYW.js → chunk-WLWHCUPZ.js} +1 -1
- package/dist/{chunk-NL6DJQJG.js → chunk-XGKCPD6W.js} +1 -1
- package/dist/{chunk-YVX2OJ7N.js → chunk-XNPUZRWP.js} +1 -1
- package/dist/{chunk-DWDR7XMI.js → chunk-XNVOKAF2.js} +1 -1
- package/dist/{chunk-V4F4LQBQ.js → chunk-XSMCQLJW.js} +1 -1
- package/dist/chunk-XUUIBSLW.js +1 -0
- package/dist/{chunk-PQEFZTEJ.js → chunk-XWQM2NZ2.js} +1 -1
- package/dist/{chunk-4MUH4E2U.js → chunk-Y3PY5LP7.js} +1 -1
- package/dist/{chunk-WUGFTI5M.js → chunk-Y62EMOQA.js} +1 -1
- package/dist/{chunk-X5VBX2QA.js → chunk-Y743UVBS.js} +1 -1
- package/dist/{chunk-GB4WR6DY.js → chunk-YBSX53QP.js} +1 -1
- package/dist/{chunk-PE6H7U6N.js → chunk-YHCP326A.js} +1 -1
- package/dist/{chunk-VOOYVTYA.js → chunk-YHESTFMZ.js} +1 -1
- package/dist/{chunk-5LZ5L2FF.js → chunk-YPCXVZ2N.js} +1 -1
- package/dist/{chunk-VCAYMY7D.js → chunk-YT27FIQN.js} +2 -2
- package/dist/{chunk-32HWTEOM.js → chunk-YTR5JHNJ.js} +1 -1
- package/dist/{chunk-543SYIY6.js → chunk-YYA5VGDW.js} +1 -1
- package/dist/{chunk-XSGYQXWO.js → chunk-Z6X2GT6Q.js} +1 -1
- package/dist/{chunk-2NRFFAV4.js → chunk-ZEBMW5KS.js} +1 -1
- package/dist/{chunk-MDGF3K45.js → chunk-ZEE32UH6.js} +1 -1
- package/dist/{chunk-CPT2VBMR.js → chunk-ZTICX3AO.js} +1 -1
- package/dist/cli/commands/app/app-logs/sources.js +1 -1
- package/dist/cli/commands/app/build.js +1 -1
- package/dist/cli/commands/app/bulk/cancel.js +1 -1
- package/dist/cli/commands/app/bulk/execute.js +1 -1
- package/dist/cli/commands/app/bulk/status.js +1 -1
- package/dist/cli/commands/app/config/link.js +1 -1
- package/dist/cli/commands/app/config/pull.js +1 -1
- package/dist/cli/commands/app/config/use.js +1 -1
- package/dist/cli/commands/app/config/validate.js +1 -1
- package/dist/cli/commands/app/demo/watcher.js +1 -1
- package/dist/cli/commands/app/deploy.js +1 -1
- package/dist/cli/commands/app/dev/clean.js +1 -1
- package/dist/cli/commands/app/dev.js +1 -1
- package/dist/cli/commands/app/env/pull.js +1 -1
- package/dist/cli/commands/app/env/show.js +1 -1
- package/dist/cli/commands/app/execute.js +1 -1
- package/dist/cli/commands/app/function/build.js +1 -1
- package/dist/cli/commands/app/function/info.js +1 -1
- package/dist/cli/commands/app/function/replay.js +1 -1
- package/dist/cli/commands/app/function/run.js +1 -1
- package/dist/cli/commands/app/function/schema.js +1 -1
- package/dist/cli/commands/app/function/typegen.js +1 -1
- package/dist/cli/commands/app/generate/extension.js +1 -1
- package/dist/cli/commands/app/graphiql.js +1 -1
- package/dist/cli/commands/app/import/channel-config.js +1 -1
- package/dist/cli/commands/app/import/custom-data-definitions.js +1 -1
- package/dist/cli/commands/app/import/dashboard-extensions.js +1 -1
- package/dist/cli/commands/app/import-custom-data-definitions.js +1 -1
- package/dist/cli/commands/app/import-extensions.js +1 -1
- package/dist/cli/commands/app/info.js +1 -1
- package/dist/cli/commands/app/init.js +1 -1
- package/dist/cli/commands/app/logs.js +1 -1
- package/dist/cli/commands/app/release.js +1 -1
- package/dist/cli/commands/app/security/check.js +1 -1
- package/dist/cli/commands/app/security/clean.js +1 -1
- package/dist/cli/commands/app/security/instructions.js +1 -1
- package/dist/cli/commands/app/security/record.js +1 -1
- package/dist/cli/commands/app/security/review.js +1 -1
- package/dist/cli/commands/app/subscription-migrations/cancel.js +1 -1
- package/dist/cli/commands/app/subscription-migrations/list.js +1 -1
- package/dist/cli/commands/app/subscription-migrations/schedule.js +1 -1
- package/dist/cli/commands/app/subscription-migrations/status.js +1 -1
- package/dist/cli/commands/app/subscription-migrations/unschedule.js +1 -1
- package/dist/cli/commands/app/versions/list.js +1 -1
- package/dist/cli/commands/app/webhook/trigger.js +1 -1
- package/dist/cli/commands/auth/login.js +1 -1
- package/dist/cli/commands/auth/logout.js +1 -1
- package/dist/cli/commands/cache/clear.js +1 -1
- package/dist/cli/commands/config/autoupgrade/off.js +1 -1
- package/dist/cli/commands/config/autoupgrade/on.js +1 -1
- package/dist/cli/commands/config/autoupgrade/status.js +1 -1
- package/dist/cli/commands/debug/command-flags.js +1 -1
- package/dist/cli/commands/doc/fetch.js +1 -1
- package/dist/cli/commands/doc/search.js +1 -1
- package/dist/cli/commands/docs/generate.js +1 -1
- package/dist/cli/commands/doctor-release/doctor-release.js +1 -1
- package/dist/cli/commands/doctor-release/theme/index.js +1 -1
- package/dist/cli/commands/help.js +1 -1
- package/dist/cli/commands/kitchen-sink/async.js +1 -1
- package/dist/cli/commands/kitchen-sink/index.js +1 -1
- package/dist/cli/commands/kitchen-sink/prompts.js +1 -1
- package/dist/cli/commands/kitchen-sink/static.js +1 -1
- package/dist/cli/commands/notifications/generate.js +1 -1
- package/dist/cli/commands/notifications/list.js +1 -1
- package/dist/cli/commands/organization/list.js +1 -1
- package/dist/cli/commands/search.js +1 -1
- package/dist/cli/commands/send-analytics.js +1 -1
- package/dist/cli/commands/theme/check.js +1 -1
- package/dist/cli/commands/theme/console.js +1 -1
- package/dist/cli/commands/theme/delete.js +1 -1
- package/dist/cli/commands/theme/dev.js +1 -1
- package/dist/cli/commands/theme/duplicate.js +1 -1
- package/dist/cli/commands/theme/info.js +1 -1
- package/dist/cli/commands/theme/init.js +1 -1
- package/dist/cli/commands/theme/language-server.js +1 -1
- package/dist/cli/commands/theme/list.js +1 -1
- package/dist/cli/commands/theme/metafields/pull.js +1 -1
- package/dist/cli/commands/theme/open.js +1 -1
- package/dist/cli/commands/theme/package.js +1 -1
- package/dist/cli/commands/theme/preview.js +1 -1
- package/dist/cli/commands/theme/profile.js +1 -1
- package/dist/cli/commands/theme/publish.js +1 -1
- package/dist/cli/commands/theme/pull.js +1 -1
- package/dist/cli/commands/theme/push.js +1 -1
- package/dist/cli/commands/theme/rename.js +1 -1
- package/dist/cli/commands/theme/share.js +1 -1
- package/dist/cli/commands/upgrade.js +1 -1
- package/dist/cli/commands/version.js +1 -1
- package/dist/cli/help.js +1 -1
- package/dist/{command-json-schema-AWZEAYEU.js → command-json-schema-SS4GDJTQ.js} +1 -1
- package/dist/command-registry.js +1 -1
- package/dist/{conf-store-TBERV4YJ.js → conf-store-54YJCIDK.js} +1 -1
- package/dist/{custom-oclif-loader-RHFO4GO3.js → custom-oclif-loader-3UBH7Q7R.js} +1 -1
- package/dist/{deprecations-FNYLBULK.js → deprecations-ZX7ZKF6L.js} +1 -1
- package/dist/dist-JMNE7MP6.js +1 -0
- package/dist/dist-M2AXGBOL.js +1 -0
- package/dist/{environment-ZUTBXY7U.js → environment-RSUK27TA.js} +1 -1
- package/dist/environments-KD2IDYPG.js +1 -0
- package/dist/{error-SI2QH7DT.js → error-AA6LW3PN.js} +1 -1
- package/dist/error-handler-4VW3AV2G.js +1 -0
- package/dist/{fs-J67RRM6W.js → fs-4QZCAEP7.js} +1 -1
- package/dist/hooks/did-you-mean.js +1 -1
- package/dist/hooks/hydrogen-init.js +1 -1
- package/dist/hooks/postrun.js +1 -1
- package/dist/hooks/prerun.js +1 -1
- package/dist/hooks/public-metadata.js +1 -1
- package/dist/hooks/sensitive-metadata.js +1 -1
- package/dist/hooks/tunnel-provider.js +1 -1
- package/dist/hooks/tunnel-start.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{is-global-BSY2VR5F.js → is-global-FKG327CG.js} +1 -1
- package/dist/{json-error-BVX244JK.js → json-error-32ETFU3V.js} +1 -1
- package/dist/{local-VCJXKA4Z.js → local-R3UAQN7T.js} +1 -1
- package/dist/{metadata-G7M7BSIE.js → metadata-JWJG4JV4.js} +1 -1
- package/dist/{node-package-manager-PMTBCYAX.js → node-package-manager-M4XHUTQR.js} +1 -1
- package/dist/notifications-system-QVM2JWKB.js +1 -0
- package/dist/{os-MMNGCSNT.js → os-FEHBQQKP.js} +1 -1
- package/dist/{output-2Q6CSC33.js → output-2LS5AOAH.js} +1 -1
- package/dist/{path-6YMSTSWG.js → path-CPAYTWQW.js} +1 -1
- package/dist/system-TMD2LYZM.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{ui-QOSX5V7Z.js → ui-FNPICAAH.js} +1 -1
- package/dist/upgrade-YYPP5VZY.js +1 -0
- package/dist/version-OX3MORI6.js +1 -0
- package/dist/version-P7D5ME4Z.js +1 -0
- package/dist/{workerd-2RFRLQVH.js → workerd-TKVRZK2F.js} +1 -1
- package/oclif.manifest.json +34 -10
- package/package.json +7 -7
- package/dist/analytics-CLYTLX2B.js +0 -1
- package/dist/analytics-K6IFFIPX.js +0 -1
- package/dist/chunk-3B3CJS5I.js +0 -1
- package/dist/chunk-3VYOLEQE.js +0 -2
- package/dist/chunk-BZSCRXYR.js +0 -4
- package/dist/chunk-DPGUL2TW.js +0 -1
- package/dist/chunk-FHOBQUQT.js +0 -7
- package/dist/chunk-KD5AYVA2.js +0 -1
- package/dist/chunk-LGRZQTQL.js +0 -15
- package/dist/chunk-Q54LU5W4.js +0 -1
- package/dist/chunk-QZGVZMB5.js +0 -3
- package/dist/chunk-S34ZYEZS.js +0 -1
- package/dist/chunk-XGFY6FV4.js +0 -1
- package/dist/chunk-YMAVVMN5.js +0 -2
- package/dist/chunk-YS447QGN.js +0 -1
- package/dist/chunk-YTJ3EPD3.js +0 -1
- package/dist/dist-HV5W7PYL.js +0 -1
- package/dist/dist-VKKGDJQ2.js +0 -1
- package/dist/environments-XC6OMZY2.js +0 -1
- package/dist/error-handler-XOEXJ4TY.js +0 -1
- package/dist/notifications-system-O2R4VN7A.js +0 -1
- package/dist/system-DQ2SKK6C.js +0 -1
- package/dist/upgrade-7HYLPUVK.js +0 -1
- package/dist/version-BPEZT4N3.js +0 -1
- package/dist/version-VNCEYJRH.js +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{a as ve}from"./chunk-
|
|
1
|
+
import{g as on}from"./chunk-FEQHCE2E.js";import{a as ve}from"./chunk-PO5T44H3.js";import{A as Be,B as Qe,C as pn,D as un,Ea as yn,Ia as vn,da as mn,fa as gn,m as be,n as We,r as an,s as cn,t as ln}from"./chunk-UNHT4ZFP.js";import{L as Ve,X as dn,ea as hn,fa as fn,q as ze}from"./chunk-Y743UVBS.js";import{a as rn}from"./chunk-3G2ZQ2EF.js";import{da as Hs}from"./chunk-NCRWOW6F.js";import{a as Ge}from"./chunk-D3KFKT4D.js";import{a as Wt}from"./chunk-XUUIBSLW.js";import{E as en,P as nn}from"./chunk-47IQIIFX.js";import{a as Zt}from"./chunk-HILYTMVA.js";import{i as I,l as tn}from"./chunk-Q37WAD6F.js";import{Ia as je,Ka as Jt,La as Kt,Nb as sn,Xa as Yt,Ya as He,b as p,fb as Xt,qa as Bt,ra as Qt,sa as ce}from"./chunk-UWI76E2L.js";import{b as js}from"./chunk-I3J63RFV.js";import{c as b,d as R,e as T,f as w,g as Vt,h as S,i as k,j as O,n as P,p as L}from"./chunk-6IDEDOXW.js";import{e as zt,g as u}from"./chunk-VKWPEFWQ.js";u();var bn=zt(js(),1);function wn(e){return bn.Flags.boolean({description:"Scan --path as an app with no app configuration file. Config checks are skipped. Requires --client-id.",env:"SHOPIFY_FLAG_WITHOUT_APP_CONFIG",exclusive:["config"],...e})}var Gs={path:ve.path,config:ve.config,"client-id":ve["client-id"],"without-app-config":wn({dependsOn:["client-id"]})},$c={...Gs,"without-app-config":wn({relationships:[{type:"all",flags:[{name:"client-id",when:async e=>!e.all}]}]})};u();import{lstat as zs}from"node:fs/promises";var Ws={confirmScanWithoutAppConfig:e=>en({message:`No app configuration found at or above ${e}. Scan it without app configuration? Config checks will be skipped.`,confirmationMessage:"Yes, scan without app configuration",cancellationMessage:"No",defaultValue:!1}),pickClientId:async e=>(await gn(yn(e))).apiKey,pickConfigFile:async e=>(await cn(e)).valueOrAbort(),lookUpApp:async e=>{await mn({apiKey:e,offerReset:!1})}};function xn(e){return e.kind==="config"?k(e.appConfigFilePath):void 0}function id(e){return e.kind==="no-config"?e.clientId:e.clientIdOverride??e.configClientId}function sd(e){return e.kind==="no-config"?e.clientId:e.clientIdOverride??k(e.appConfigFilePath,".toml")}async function rd(e){let t=await we(e.path);if(e.withoutAppConfig)return t;try{return await ce((await Be.load(e.path)).directory)}catch(n){if(!(n instanceof Qe))throw n;Ke(e.path)}}async function od(e,t=Ws){if(e.withoutAppConfig){if(!e.clientId)throw new I("--without-app-config requires --client-id.");let n=await we(e.path);return await Je(e,t),{kind:"no-config",appDirectory:n,clientId:e.clientId,clientIdSource:"flag"}}await we(e.path);try{let n=e.config?void 0:await Vs(e,t),{app:i,project:s,activeConfig:r}=await vn({directory:e.path,userProvidedConfigName:e.config??n?.fileName,skipPrompts:!e.allowPrompts});await Je(e,t);let o=await ce(i.directory);return{kind:"config",appDirectory:o,appConfigFilePath:b(o,k(i.configPath)),appConfigDirectories:await Qs(s,r.file),configClientId:i.configuration.client_id||void 0,clientIdOverride:e.clientId,appConfigFilePicked:n?.picked}}catch(n){if(!(n instanceof Qe))throw n;return Bs(e,t)}}async function Vs(e,t){let{directory:n}=await Be.load(e.path),i=ln(n)?.configFile;if(i&&He(b(n,i)))return;if(He(b(n,ze.app)))return i?{fileName:ze.app,picked:!1}:void 0;let s=(await an(n)).map(r=>k(r));return s.length===1?{fileName:s[0],picked:!1}:((!e.allowPrompts||e.clientId)&&Ks(n,s,e.clientId),{fileName:await t.pickConfigFile(n),picked:!0})}async function Bs(e,t){e.allowPrompts||Ke(e.path);let n=await we(e.path);return await Je(e,t),await t.confirmScanWithoutAppConfig(e.path)||Ke(e.path),e.clientId?{kind:"no-config",appDirectory:n,clientId:e.clientId,clientIdSource:"flag"}:{kind:"no-config",appDirectory:n,clientId:await t.pickClientId(n),clientIdSource:"picker"}}async function Je(e,t){e.validateClientIdFlag&&e.clientId!==void 0&&await t.lookUpApp(e.clientId)}async function ad(e){let t=[];for(let n of e){let i=T(L(),n),s=await kn(i);if(s===void 0)throw new I(`--include-dir ${n}: directory doesn't exist.`);if(!await je(s))throw new I(`--include-dir ${n}: not a directory.`);t.push(s)}return t}async function Qs(e,t){let n=[...pn(e,t),...un(e,t)].map(s=>S(s.path)).filter(s=>!P(e.directory,s)),i=[];for(let s of new Set(n))await Js(e.directory,s)||i.push(await ce(s));return i.sort()}async function Js(e,t){let n=w(e,t).split("/"),i=b(e,...n.filter(s=>s===".."));for(let s of n.filter(r=>r!==".."))if(i=b(i,s),(await zs(i)).isSymbolicLink())return!0;return!1}function cd(e,t,n=[]){let i=a=>b(e,w(e,a))!==R(a),s=t.find(i);if(s)throw new I(`--include-dir ${s}: must be on the same drive as the app directory.`);let r=n.find(i);if(r)throw new I(`Extension or web directory ${r}: must be on the same drive as the app directory.`);let o=[{directory:e,origin:"app_directory"},...t.map(a=>({directory:a,origin:"include_dir"})),...n.map(a=>({directory:a,origin:"app_config_directory"}))].filter((a,c,l)=>l.findIndex(({directory:d})=>d===a.directory)===c);return{scanDirectories:o.filter(a=>!o.some(({directory:c})=>c!==a.directory&&P(c,a.directory))),requestedScanDirectories:o.map(({directory:a})=>a)}}function Ke(e){throw new I(`No app configuration found at or above ${e}.`,"Pass `--path` to your app directory, or scan without app configuration with `--without-app-config --client-id <client-id>`.")}function Ks(e,t,n){let i=t.map(s=>be(s)??s).join(", ");throw new I(`${t.length} app configurations found in ${e}, and none is selected.`,n?`\`--client-id\` can't be combined with \`--config\`, so first select one with \`shopify app config use <config>\`: ${i}.`:`Pass \`--config\` with one of: ${i}.`)}async function we(e){let t=await kn(e);if(t===void 0||!await je(t))throw new I(`--path ${e}: not a directory.`);return t}async function kn(e){return ce(e).catch(()=>{})}u();import{realpathSync as Ys}from"node:fs";var Xs={include_dirs:[],excludes:[],no_git_ignore:!1};function _n(e){try{return Ys(e)}catch{return T(e)}}function Zs(e){return _n(e)===_n(L())?[]:[{flag:"--path",value:w(L(),e)}]}function er(e){if(e.kind==="no-config")return[{flag:"--client-id",value:e.clientId},"--without-app-config"];if(e.clientIdOverride)return[{flag:"--client-id",value:e.clientIdOverride}];let t=xn(e),n=t?be(t):void 0;return n?[{flag:"--config",value:n}]:[]}function tr(e){return[...e.include_dirs.map(t=>({flag:"--include-dir",value:t})),...e.excludes.map(t=>({flag:"--exclude",value:t})),...e.no_git_ignore?["--no-git-ignore"]:[]]}function md(e,t,n=Xs){let i="shopify",s=r=>["app","security",r,...Zs(t),...er(e)];return{scan:{command:i,args:[...s("check"),...tr(n)]},record:{command:i,args:s("record"),stdinPlaceholder:"<findings.json>"},review:{command:i,args:s("review")},clean:{command:i,args:s("clean")}}}function Sn(e=process.platform,t=process.env){return e!=="win32"||nr(t)?"posix":ir(t)}function nr(e){if(e.MSYSTEM||e.CYGWIN)return!0;let t=e.SHELL??"";return/(?:^|[\\/])(bash|zsh|sh|fish|dash)(?:\.exe)?$/i.test(t)}function ir(e){return e.PROMPT?"cmd":e.POWERSHELL_DISTRIBUTION_CHANNEL||e.PSExecutionPolicyPreference||e.PSModulePath?"powershell":"cmd"}function sr(e,t){return t==="cmd"?rr(e):t==="powershell"?`'${e.replaceAll("'","''")}'`:`'${e.replaceAll("'","'\\''")}'`}function rr(e){return e.split("%").map(or).join("^%")}function or(e){let t=e.replace(/"/g,'""'),n=/\\+$/.exec(t)?.[0]??"";return`"${t}${n}"`}function gd(e,t=Sn()){let n=Tn(e,t);return e.stdinPlaceholder?t==="powershell"?`Get-Content -Raw -Encoding UTF8 ${e.stdinPlaceholder} | ${n}`:`${n} < ${e.stdinPlaceholder}`:n}function yd(e,t,n=Sn()){let i=Tn(e,n);if(n==="posix")return`${i} <<'EOF'
|
|
2
2
|
${t}
|
|
3
3
|
EOF`;if(n==="powershell")return`@'
|
|
4
4
|
${t}
|
|
5
|
-
'@ | ${i}`}function
|
|
5
|
+
'@ | ${i}`}function Tn(e,t){let n=e.args.map(i=>typeof i=="string"?i:`${i.flag} ${sr(i.value,t)}`);return[e.command,...n].join(" ")}u();var j={high:3,medium:2,low:1},C=1,Ye=1,de=1,D="shopify-app-security";u();u();var En=[`---
|
|
6
6
|
id: ACTIVE_UPLOADS_AND_PRIVILEGED_PREVIEWS
|
|
7
7
|
version: 1
|
|
8
8
|
severity: high
|
|
@@ -2156,7 +2156,7 @@ severity: high
|
|
|
2156
2156
|
# Weak Shop Validation
|
|
2157
2157
|
|
|
2158
2158
|
Inspect shop-domain validation across aliases and helpers. Report validation that accepts attacker-controlled domains because it is unanchored or otherwise fails to enforce the exact permitted Shopify hostname form.
|
|
2159
|
-
`],
|
|
2159
|
+
`],In=`The app security check is Shopify's local security review workflow for app source code. It lives in Shopify CLI, which owns the deterministic rules, detailed semantic check prompts, findings schema, redaction rules, and artifact formats. Your job is to orchestrate the CLI, investigate the agent checks it generates, and record your findings back to it with a command\u2014not to recreate its security checks from memory.
|
|
2160
2160
|
|
|
2161
2161
|
## Scope
|
|
2162
2162
|
|
|
@@ -2313,9 +2313,9 @@ When the user explicitly wants a fast local or CI scan without semantic investig
|
|
|
2313
2313
|
For a pass/fail gate, add \`--blocking <severity>\`: the command exits 1 when a check with findings is at or above that severity. Do not describe a deterministic-only scan as the full app security check.
|
|
2314
2314
|
|
|
2315
2315
|
Route authentication retains a template-oriented heuristic. Calls using \`context.shopify.authenticate.admin(...)\` are deferred to the \`UNAUTHENTICATED_ENDPOINT\` agent review, with unresolved coverage rather than a missing-auth finding or a pass. The heuristic does not establish binding provenance, control-flow safety, or tenant/object authorization.
|
|
2316
|
-
`;u();u();var
|
|
2317
|
-
`);for(let[c,l]of a.entries())for(let d of An)d.regex.test(l)&&i.push({id:"COMMITTED_SECRET",pattern_id:d.name,severity:"high",points:-50,title:`Hardcoded ${d.name} detected`,message:`A ${d.name} pattern was found in repository text. Never hardcode secrets \u2014 use environment variables.`,location:{file:o.path,line:c+1},snippet:Dn(l,d),fix:{automated:!1,description:"Move the secret to an environment variable and rotate it immediately"}})}return i}async function On(e,t){let n=await H(e,t);return{exitCode:n?.exitCode,out:n?.stdout.trim()??""}}async function ur(e){let t=async f=>On(S(e.absolutePath),f),n=k(e.absolutePath),i=await t(["rev-parse","--is-inside-work-tree"]);if(i.exitCode!==0||i.out!=="true")return{tracked:void 0,ignored:void 0,reason:"not a git repository, or git unavailable",evidence:["git rev-parse --is-inside-work-tree \u2192 not a work tree"]};let s=[],r=await t(["ls-files","--error-unmatch","--",n]),o;r.exitCode===0&&r.out.length>0?o=!0:r.exitCode===1&&(o=!1);let a="unknown";o===!0?a="TRACKED":o===!1&&(a="untracked"),s.push(`git ls-files --error-unmatch ${e.path} \u2192 ${a}`);let c=await t(["check-ignore","-q","--",n]),l;c.exitCode===0?l=!0:c.exitCode===1&&(l=!1);let d="unknown";return l===!0?d="ignored":l===!1&&(d="not ignored"),s.push(`git check-ignore -q ${e.path} \u2192 ${d}`),{tracked:o,ignored:l,...o===void 0||l===void 0?{reason:"git status command failed"}:{},evidence:s}}u();var v="https://shopify.dev/docs/apps/build/security/app-security-checks",G=[{id:"DEPRECATED_SCRIPT_TAG_SCOPE",title:"Deprecated ScriptTag capability",severity:"medium",points:-10,description:"Detects the deprecated write_script_tags OAuth scope in shopify.app.toml.",fix:"Remove write_script_tags from scopes and migrate to app embeds.",guide:"https://shopify.dev/docs/apps/online-store/app-embeds",docsUrl:`${v}/deprecated-script-tag-scope`},{id:"SCOPE_OVER_REQUEST",title:"OAuth scope may be over-requested",severity:"high",points:-12,description:"Detects declared OAuth scopes that don't appear to be used by the app code.",fix:"Remove unused scopes or add the code path that needs them.",guide:"https://shopify.dev/docs/api/usage/access-scopes",docsUrl:`${v}/scope-over-request`},{id:"UNSAFE_INNERHTML",title:"Unsafe HTML assignment",severity:"high",points:-25,description:"Detects innerHTML, outerHTML, and insertAdjacentHTML receiving non-literal values.",fix:"Use textContent where possible or sanitize with DOMPurify before rendering.",guide:"https://shopify.dev/docs/apps/online-store/security#xss-prevention",requires:"theme_app_extension",docsUrl:`${v}/unsafe-innerhtml`},{id:"LIQUID_UNSAFE_RENDER",title:"Unsafe Liquid metafield or setting output",severity:"medium",points:-10,description:"Detects metafield and merchant-setting output without context-appropriate escaping or serialization.",fix:"Use escape for HTML text/attributes, json for JavaScript data, or metafield_tag for supported rich content.",requires:"theme_app_extension",docsUrl:`${v}/liquid-unsafe-render`},{id:"INSECURE_WEBHOOK_URL",title:"Configured callback URL is not HTTPS",severity:"high",points:-12,description:"Detects webhook subscriptions and OAuth redirect URLs configured with unsafe absolute URLs.",fix:"Use HTTPS or a supported Shopify-relative path for every configured callback URL.",guide:"https://shopify.dev/docs/apps/webhooks",docsUrl:`${v}/insecure-webhook-url`},{id:"COMMITTED_SECRET",title:"Committed secret or environment file",severity:"high",points:-50,description:"Detects environment files and common API key/token patterns in source files.",fix:"Remove committed secrets, rotate exposed values, and store them in environment variables.",docsUrl:`${v}/committed-secret`},{id:"CREDENTIAL_LOG_LEAKAGE",title:"Credential reaches a log sink",severity:"high",points:-20,description:"Detects direct credential flows to console and logger sinks.",fix:"Remove credentials from logs; emit only safe redacted, hashed, or boolean-derived values.",docsUrl:`${v}/credential-log-leakage`},{id:"CREDENTIAL_BROWSER_LEAKAGE",title:"Credential reaches the client browser",severity:"high",points:-20,description:"Detects direct credential flows into responses, loaders, globals, DOM, and external requests.",fix:"Keep credentials server-side and return only non-sensitive derived data.",docsUrl:`${v}/credential-browser-leakage`},{id:"EXPIRING_OFFLINE_TOKEN",title:"Expiring offline access tokens explicitly disabled",severity:"medium",points:-10,description:"Verifies React Router expiring offline-token configuration and refresh-metadata storage compatibility.",fix:"Enable expiringOfflineAccessTokens and ensure session storage persists expiry and refresh metadata.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens",docsUrl:`${v}/expiring-offline-token`},{id:"METAFIELD_OFFLINE_TOKEN",title:"Metafield write in an unverified offline token context",severity:"high",points:-15,description:"Investigates metafield and metaobject writes made with an offline Admin API context that no verified request for the same shop authorizes.",fix:"Make the write from a request verified with authenticate.admin(request), or derive the shop and values from a verified source.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens",docsUrl:`${v}/metafield-offline-token`},{id:"MISSING_EMBEDDED_CSP",title:"Embedded app missing frame-ancestors CSP directive",severity:"medium",points:-10,description:"Detects embedded apps that set document response headers but omit frame-ancestors.",fix:"Add frame-ancestors to CSP, restricted to the authenticated shop domain and admin.shopify.com.",guide:"https://shopify.dev/docs/apps/build/security/set-up-iframe-protection",docsUrl:`${v}/missing-embedded-csp`},{id:"MISSING_COMPLIANCE_WEBHOOKS",title:"Missing mandatory GDPR compliance webhooks",severity:"medium",points:-10,description:"Detects apps missing mandatory compliance webhooks (shop/redact, customers/data_request, customers/redact).",fix:"Add the missing webhook subscriptions to shopify.app.toml.",guide:"https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks",docsUrl:`${v}/missing-compliance-webhooks`},{id:"MISSING_DEPENDENCY_SECURITY_AUTOMATION",title:"Repository-level dependency management configuration not detected",severity:"low",points:-5,description:"Looks for a local Dependabot or Renovate configuration file at the repository root without validating its contents.",fix:"Add a Dependabot or Renovate configuration file at the repository root, or verify existing coverage.",docsUrl:`${v}/missing-dependency-security-automation`},{id:"EOL_API_VERSION",title:"End-of-life API version",severity:"low",points:-5,description:"Detects parsed config and React Router server API versions past the 12-month support window and 30-day grace period.",fix:"Update api_version in shopify.app.toml and the ApiVersion enum in shopify.server.ts.",guide:"https://shopify.dev/docs/api/usage/versioning",docsUrl:`${v}/eol-api-version`},{id:"WEAK_SHOP_VALIDATION",title:"Shop domain validation regex is not anchored",severity:"high",points:-15,description:"Detects unanchored myshopify.com regexes that allow domain spoofing and token theft.",fix:"Anchor the regex with ^ and $ to prevent prefix/suffix attacks.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/get-access-tokens/oauth",docsUrl:`${v}/weak-shop-validation`},{id:"APP_PROXY_LIQUID_INJECTION",title:"App proxy Liquid injection risk",severity:"high",points:-20,description:"Detects app proxy endpoints that interpolate request parameters into active Liquid or HTML responses.",fix:"Don't interpolate user input into application/liquid responses. Use application/json or escape all input.",guide:"https://shopify.dev/docs/apps/online-store/app-proxies",docsUrl:`${v}/app-proxy-liquid-injection`},{id:"MISSING_TENANT_ISOLATION",title:"Database query may not be scoped by shop",severity:"high",points:-20,description:"Detects database queries in authenticated routes that don't reference the shop domain or session.",fix:"Add the authenticated shop to the query's where clause.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens",docsUrl:`${v}/missing-tenant-isolation`},{id:"OPEN_REDIRECT",title:"Open redirect in auth callback",severity:"medium",points:-12,description:"Detects redirect targets taken from request parameters without validation.",fix:"Validate the redirect URL against an allowlist of permitted domains.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization",docsUrl:`${v}/open-redirect`},{id:"UNAUTHENTICATED_ENDPOINT",title:"Route handler lacks recognized auth verification",severity:"high",points:-15,description:"Uses the template route-auth heuristic, with agent review for context.shopify.authenticate.admin calls instead of treating them as missing authentication.",fix:"Complete the verification appropriate to the entry point before protected operations, and stop those operations when verification fails.",guide:"https://shopify.dev/docs/apps/auth",requires:"has_backend",docsUrl:`${v}/unauthenticated-endpoint`},{id:"REQUEST_CONTROLLED_ADMIN_CONTEXT",title:"Request input selects Admin API shop context",severity:"high",points:-20,description:"Detects request-derived shop values passed into unauthenticated.admin(...), allowing callers to select another shop's Admin API context.",fix:"Use the Admin API context returned by authenticate.admin(request). Never pass form, JSON, query, or route input into unauthenticated.admin(...).",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/online-access-tokens",docsUrl:`${v}/request-controlled-admin-context`},{id:"APP_PROXY_UNVERIFIED_SIGNATURE",title:"App proxy request trusted without signature verification",severity:"high",points:-15,description:"Detects app proxy parameters read without visible signature verification.",fix:"Verify app proxy signatures before trusting shop, customer, or path parameters.",guide:"https://shopify.dev/docs/apps/build/online-store/app-proxies/authenticate-app-proxies",requires:"app_proxy",docsUrl:`${v}/app-proxy-unverified-signature`},{id:"REQUEST_DERIVED_SHOP_SCOPE",title:"Query scoped by shop value taken from request input",severity:"high",points:-15,description:"Detects queries whose shop scope comes from request params rather than the authenticated session. Presence of a shop key is not proof of scoping when its value is attacker-controlled.",fix:"Scope the query with the shop from the authenticated session, or verify the supplied shop against a signed session or HMAC first.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens",docsUrl:`${v}/request-derived-shop-scope`},{id:"UNSCOPED_SHOP_CONFIG_WRITE",title:"Config write trusts shop from request input",severity:"high",points:-15,description:"Detects settings or metafield writes scoped by request-controlled shop input without visible auth verification.",fix:"Derive the target shop from an authenticated session or verified HMAC.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens",docsUrl:`${v}/unscoped-shop-config-write`},{id:"STATIC_FRAME_ANCESTORS",title:"Embedded app frame-ancestors uses a wildcard",severity:"high",points:-12,description:"Detects wildcard or static cross-shop frame-ancestors policies in embedded app code, whether written literally or built from variables.",fix:"Restrict frame-ancestors to Shopify Admin and the authenticated shop origin.",guide:"https://shopify.dev/docs/apps/build/security/set-up-iframe-protection",requires:"embedded_app",docsUrl:`${v}/static-frame-ancestors`},{id:"ACTIVE_UPLOADS_AND_PRIVILEGED_PREVIEWS",title:"Active upload or privileged preview may execute untrusted content",severity:"high",points:-15,description:"Investigates uploaded or imported active content that may execute in storefront, embedded admin, customer-account, or operator preview surfaces.",fix:"Sanitize or re-encode active content and isolate previews from privileged origins.",docsUrl:`${v}/active-uploads-and-privileged-previews`},{id:"DEPENDENCY_REACHABILITY",title:"Vulnerable dependency may be reachable in app code",severity:"medium",points:-10,description:"Investigates whether a vulnerable dependency version is actually used through the affected API, helper, or configuration.",fix:"Upgrade the dependency or remove the reachable vulnerable code path.",docsUrl:`${v}/dependency-reachability`},{id:"SESSION_LIFECYCLE_AND_REPLAY",title:"Session lifecycle or replay control may be missing",severity:"high",points:-15,description:"Investigates stale sessions, replayable signed links, and missing invalidation or idempotency across lifecycle transitions.",fix:"Invalidate stale authority and enforce replay protection or re-authorization before sensitive actions.",docsUrl:`${v}/session-lifecycle-and-replay`},{id:"OVERBROAD_DATA_ACCESS",title:"Data access may exceed the current request",severity:"medium",points:-10,description:"Investigates reads that may expose data outside the authenticated shop or user boundary.",fix:"Scope data access to the authenticated shop and minimum necessary records.",docsUrl:`${v}/overbroad-data-access`},{id:"CROSS_SITE_SCRIPTING",title:"Cross-site scripting risk",severity:"high",points:-15,description:"Investigates lower-trust data rendered as executable browser content in app-owned pages.",fix:"Use context-appropriate escaping, safe serialization, URL validation, or HTML sanitization at the final sink.",guide:"https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html",docsUrl:`${v}/cross-site-scripting`},{id:"SQL_INJECTION",title:"SQL injection risk",severity:"high",points:-15,description:"Investigates lower-trust input that changes SQL syntax in a reachable database operation.",fix:"Bind query values and use trusted mappings or safe identifier quoting for dynamic SQL structure.",guide:"https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html",docsUrl:`${v}/sql-injection`},{id:"SSRF_REQUEST_FORGERY",title:"Server-side request forgery risk",severity:"high",points:-15,description:"Investigates server-side requests whose destinations may be controlled by request input.",fix:"Allowlist destinations and reject private, local, and unsafe network targets.",docsUrl:`${v}/ssrf-request-forgery`},{id:"THEME_EXTENSION_XSS",title:"Theme extension cross-site scripting risk",severity:"high",points:-15,description:"Investigates untrusted values rendered without safe escaping in theme extensions.",fix:"Escape untrusted values and avoid raw HTML rendering.",requires:"theme_app_extension",docsUrl:`${v}/theme-extension-xss`},{id:"CSRF_MISSING_PROTECTION",title:"Cross-site request forgery protection may be missing",severity:"medium",points:-10,description:"Investigates state-changing endpoints for missing request authenticity protections.",fix:"Require an authenticated session and CSRF protection on state-changing requests.",docsUrl:`${v}/csrf-missing-protection`},{id:"MISSING_AUTHORIZATION_CHECK",title:"Authorization check may be missing",severity:"high",points:-15,description:"Investigates authenticated handlers that may not authorize access to the requested resource.",fix:"Authorize the actor and resource after authentication.",docsUrl:`${v}/missing-authorization-check`},{id:"SCRIPT_TAG_URL_INJECTION",title:"ScriptTag URL may be request controlled",severity:"high",points:-15,description:"Investigates ScriptTag writes whose source URL may be controlled by untrusted input.",fix:"Remove ScriptTag usage or restrict sources to trusted versioned assets.",docsUrl:`${v}/script-tag-url-injection`},{id:"TEXT_SETTING_HTML_SMUGGLING",title:"Text setting may be rendered as unsafe HTML",severity:"high",points:-15,description:"Investigates merchant-configurable text rendered as HTML without sanitization.",fix:"Render settings as text or sanitize with a strict allowlist.",docsUrl:`${v}/text-setting-html-smuggling`}];function hr(e){return G.find(t=>t.id===e)?.docsUrl}var fr=e=>e==="high"||e==="medium"||e==="low",mr=e=>e==="union"||e==="prefer-agent",gr=(e,t)=>{if(t===void 0)return"union";if(!mr(t))throw new Error(`Invalid precedence for agent check ${e}: ${t} (expected union or prefer-agent)`);return t},yr=e=>{let t=/^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(e);if(!t)return{meta:{},body:e};let[,n="",i=""]=t,s={};for(let r of n.split(`
|
|
2318
|
-
`)){let o=r.indexOf(":");o!==-1&&(s[r.slice(0,o).trim()]=r.slice(o+1).trim())}return{meta:s,body:i.trim()}},Ze=(e=
|
|
2316
|
+
`;u();u();var ar={fileSystemMonitor:{args:["-c","core.fsmonitor="]},bareRepository:{args:["-c","safe.bareRepository=explicit"]},lazyFetch:{env:{GIT_NO_LAZY_FETCH:"1"}},transports:{env:{GIT_ALLOW_PROTOCOL:""}},emptyRemoteHelper:{args:["-c","alias.remote-="]}},cr="2.38.0",An=Object.values(ar),dr=An.flatMap(e=>e.args??[]);function lr(){return Object.fromEntries(An.flatMap(e=>Object.entries(e.env??{})))}async function H(e,t){await on(cr,{cwd:e});try{let n=await nn("git",[...dr,...t],{cwd:e,env:lr()});return{exitCode:n.exitCode,stdout:n.stdout}}catch{return}}var Dn={regex:/shp(?:(?:at|ca|pa|ss)_[a-fA-F0-9]{16,}|(?:rt|sb|tka|ua)_[a-zA-Z0-9]{16,})/,name:"Shopify token",wholeMatch:!0},Rn=[Dn],pr=[Dn,{regex:/(?:sk|rk)_(?:live|test)_[a-zA-Z0-9]{20,}/,name:"Stripe API key",wholeMatch:!0},{regex:/AKIA[0-9A-Z]{16}/,name:"AWS access key",wholeMatch:!0},{regex:/aws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*['"]([A-Za-z0-9/+=]{40})['"]/i,name:"AWS secret access key"},{regex:/gh[pousr]_[A-Za-z0-9]{36,}/,name:"GitHub token",wholeMatch:!0},{regex:/AIza[0-9A-Za-z_-]{35}/,name:"Google API key",wholeMatch:!0},{regex:/xox[baprs]-[0-9A-Za-z-]{10,}/,name:"Slack token",wholeMatch:!0},{regex:/-----BEGIN ((?:(?:RSA|EC|OPENSSH) )?PRIVATE KEY|PGP PRIVATE KEY BLOCK)-----[\s\S]*?-----END \1-----/,name:"private key block",wholeMatch:!0},{regex:/-----BEGIN (?:(?:RSA|EC|OPENSSH) )?PRIVATE KEY-----|-----BEGIN PGP PRIVATE KEY BLOCK-----/,name:"private key",wholeMatch:!0,redactEntireInput:!0}];function Nn(e,t){let n=t.regex.exec(e);if(!n)return"[REDACTED]";if(t.redactEntireInput)return"[REDACTED LINE]";let i=t.wholeMatch?n[0]:n[1];if(!i)return"[REDACTED]";let s=Math.min(4,Math.floor(i.length/4)),r=s>0?i.slice(0,s):"",o=e.split(i).join(`${r}[REDACTED:${i.length}]`);return o.includes(i)?"[REDACTED LINE]":o}function y(e){let t=e;for(let n of pr){for(let i=0;i<100&&n.regex.exec(t);i++){let r=Nn(t,n);if(r===t)return"[REDACTED TEXT]";t=r}if(n.regex.test(t))return"[REDACTED TEXT]"}return t}function xe(e){return{include_dirs:e.include_dirs.map(y),excludes:e.excludes.map(y),no_git_ignore:e.no_git_ignore}}var Cn=/(^|\/)(?:\.env\.(?:secrets|keys)|(?:secrets|credentials)\.json)$/;function ur(e){let t=e.slice(e.lastIndexOf("/")+1);if(t===".env"||t.startsWith(".env."))return t}function Pn(e){return ur(e)!==void 0}async function qn(e){let t=await Fn(e,["rev-parse","--show-toplevel"]);return t.exitCode===0&&t.out!==""?t.out:void 0}async function hr(e,t,n){if(t==="failed")return{reason:"listing-failed",evidence:[]};if(t==="not-a-repository")return{reason:"unknown",evidence:[]};let i=await qn(S(e.absolutePath)),s=await n(),r=i!==void 0&&s!==void 0,o=r&&i!==s,a="unknown";return o?a="differs":r&&(a="same"),{reason:o?"nested-repository":"unknown",evidence:[`git rev-parse --show-toplevel \u2192 ${a} for ${e.path} and the app root`]}}function Ln(e,t,n,i){let s=n?"Environment file with secrets":"Secret file",r=t.tracked===!0,o=t.tracked===!1&&t.ignored===!1,a=t.tracked===!1&&t.ignored===!0,c,l,d;return r?(c=`${s} is tracked by git`,l=`${e.path} IS TRACKED BY GIT (confirmed via git ls-files), so its contents are in the repository history. Rotate every exposed secret and purge the file from history.`,d=`git rm --cached ${e.path}, add it to .gitignore, purge it from history, and rotate every exposed secret`):o?(c=`${s} is not ignored by git`,l=`${e.path} is untracked but not ignored. If committed, its contents enter repository history.`,d=`Add ${e.path} to .gitignore, confirm with 'git check-ignore ${e.path}', and rotate any exposed secrets`):i==="nested-repository"?(c=`${s} is inside a nested git repository`,l=`${e.path} belongs to a nested git repository (its top level differs from the app's), so this app's ignore rules don't protect it and it was scanned.`,d=`Ignore ${e.path} in the nested repository and rotate any exposed secrets`):i==="listing-failed"?(c=`${s} is ignored by git but was scanned`,l=`${e.path} is ignored by git, but the app security check could not list the ignored files for this app, so it was scanned.`,d="Confirm the repository is healthy with 'git status' and rotate any exposed secrets"):a?(c=`${s} is ignored by git but was scanned`,l=`${e.path} is ignored by git but was still scanned; the app security check couldn't determine why. Confirm the rule ignoring it belongs to the repository that owns this app before treating this as clean.`,d=`Confirm with 'git check-ignore -v ${e.path}' that this app's repository ignores it, and rotate any exposed secrets`):(c=`${s} could not be confirmed as ignored`,l=`${e.path} could not be confirmed as untracked-and-ignored${t.reason?` (${t.reason})`:""}. Confirm it is gitignored before treating this as clean.`,d=`Add ${e.path} to .gitignore, confirm with 'git ls-files ${e.path}', and rotate any exposed secrets`),{id:"COMMITTED_SECRET",pattern_id:`${n?"environment-file":"secret-file"}:${r?"tracked":"unconfirmed"}`,severity:"high",points:-50,title:c,message:l,location:{file:e.path},detection_evidence:t.evidence,fix:{automated:!1,description:d}}}async function On(e,t,n){let i=[],s,r=()=>(s??=qn(t),s);for(let o of e){let a=o.content;if(a===void 0)continue;let c=Pn(o.path),l=Cn.test(o.path);if(!c&&!l)continue;let d=Rn.some(E=>E.regex.test(a)),f=l&&a.trim()==="";if(!d&&!f)continue;let h=await fr(o);if(!d){f&&h.tracked===!0&&i.push(Ln(o,h,c,"unknown"));continue}let g="unknown",x=h.evidence??[];if(h.tracked===!1&&h.ignored===!0){if(n==="git-ignore-off")continue;let E=await hr(o,n,r);g=E.reason,x=[...x,...E.evidence]}i.push(Ln(o,{...h,evidence:x},c,g))}for(let o of e){if(!o.content||Pn(o.path)||Cn.test(o.path))continue;let a=o.content.split(`
|
|
2317
|
+
`);for(let[c,l]of a.entries())for(let d of Rn)d.regex.test(l)&&i.push({id:"COMMITTED_SECRET",pattern_id:d.name,severity:"high",points:-50,title:`Hardcoded ${d.name} detected`,message:`A ${d.name} pattern was found in repository text. Never hardcode secrets \u2014 use environment variables.`,location:{file:o.path,line:c+1},snippet:Nn(l,d),fix:{automated:!1,description:"Move the secret to an environment variable and rotate it immediately"}})}return i}async function Fn(e,t){let n=await H(e,t);return{exitCode:n?.exitCode,out:n?.stdout.trim()??""}}async function fr(e){let t=async f=>Fn(S(e.absolutePath),f),n=k(e.absolutePath),i=await t(["rev-parse","--is-inside-work-tree"]);if(i.exitCode!==0||i.out!=="true")return{tracked:void 0,ignored:void 0,reason:"not a git repository, or git unavailable",evidence:["git rev-parse --is-inside-work-tree \u2192 not a work tree"]};let s=[],r=await t(["ls-files","--error-unmatch","--",n]),o;r.exitCode===0&&r.out.length>0?o=!0:r.exitCode===1&&(o=!1);let a="unknown";o===!0?a="TRACKED":o===!1&&(a="untracked"),s.push(`git ls-files --error-unmatch ${e.path} \u2192 ${a}`);let c=await t(["check-ignore","-q","--",n]),l;c.exitCode===0?l=!0:c.exitCode===1&&(l=!1);let d="unknown";return l===!0?d="ignored":l===!1&&(d="not ignored"),s.push(`git check-ignore -q ${e.path} \u2192 ${d}`),{tracked:o,ignored:l,...o===void 0||l===void 0?{reason:"git status command failed"}:{},evidence:s}}u();var v="https://shopify.dev/docs/apps/build/security/app-security-checks",G=[{id:"DEPRECATED_SCRIPT_TAG_SCOPE",title:"Deprecated ScriptTag capability",severity:"medium",points:-10,description:"Detects the deprecated write_script_tags OAuth scope in shopify.app.toml.",fix:"Remove write_script_tags from scopes and migrate to app embeds.",guide:"https://shopify.dev/docs/apps/online-store/app-embeds",docsUrl:`${v}/deprecated-script-tag-scope`},{id:"SCOPE_OVER_REQUEST",title:"OAuth scope may be over-requested",severity:"high",points:-12,description:"Detects declared OAuth scopes that don't appear to be used by the app code.",fix:"Remove unused scopes or add the code path that needs them.",guide:"https://shopify.dev/docs/api/usage/access-scopes",docsUrl:`${v}/scope-over-request`},{id:"UNSAFE_INNERHTML",title:"Unsafe HTML assignment",severity:"high",points:-25,description:"Detects innerHTML, outerHTML, and insertAdjacentHTML receiving non-literal values.",fix:"Use textContent where possible or sanitize with DOMPurify before rendering.",guide:"https://shopify.dev/docs/apps/online-store/security#xss-prevention",requires:"theme_app_extension",docsUrl:`${v}/unsafe-innerhtml`},{id:"LIQUID_UNSAFE_RENDER",title:"Unsafe Liquid metafield or setting output",severity:"medium",points:-10,description:"Detects metafield and merchant-setting output without context-appropriate escaping or serialization.",fix:"Use escape for HTML text/attributes, json for JavaScript data, or metafield_tag for supported rich content.",requires:"theme_app_extension",docsUrl:`${v}/liquid-unsafe-render`},{id:"INSECURE_WEBHOOK_URL",title:"Configured callback URL is not HTTPS",severity:"high",points:-12,description:"Detects webhook subscriptions and OAuth redirect URLs configured with unsafe absolute URLs.",fix:"Use HTTPS or a supported Shopify-relative path for every configured callback URL.",guide:"https://shopify.dev/docs/apps/webhooks",docsUrl:`${v}/insecure-webhook-url`},{id:"COMMITTED_SECRET",title:"Committed secret or environment file",severity:"high",points:-50,description:"Detects environment files and common API key/token patterns in source files.",fix:"Remove committed secrets, rotate exposed values, and store them in environment variables.",docsUrl:`${v}/committed-secret`},{id:"CREDENTIAL_LOG_LEAKAGE",title:"Credential reaches a log sink",severity:"high",points:-20,description:"Detects direct credential flows to console and logger sinks.",fix:"Remove credentials from logs; emit only safe redacted, hashed, or boolean-derived values.",docsUrl:`${v}/credential-log-leakage`},{id:"CREDENTIAL_BROWSER_LEAKAGE",title:"Credential reaches the client browser",severity:"high",points:-20,description:"Detects direct credential flows into responses, loaders, globals, DOM, and external requests.",fix:"Keep credentials server-side and return only non-sensitive derived data.",docsUrl:`${v}/credential-browser-leakage`},{id:"EXPIRING_OFFLINE_TOKEN",title:"Expiring offline access tokens explicitly disabled",severity:"medium",points:-10,description:"Verifies React Router expiring offline-token configuration and refresh-metadata storage compatibility.",fix:"Enable expiringOfflineAccessTokens and ensure session storage persists expiry and refresh metadata.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens",docsUrl:`${v}/expiring-offline-token`},{id:"METAFIELD_OFFLINE_TOKEN",title:"Metafield write in an unverified offline token context",severity:"high",points:-15,description:"Investigates metafield and metaobject writes made with an offline Admin API context that no verified request for the same shop authorizes.",fix:"Make the write from a request verified with authenticate.admin(request), or derive the shop and values from a verified source.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens",docsUrl:`${v}/metafield-offline-token`},{id:"MISSING_EMBEDDED_CSP",title:"Embedded app missing frame-ancestors CSP directive",severity:"medium",points:-10,description:"Detects embedded apps that set document response headers but omit frame-ancestors.",fix:"Add frame-ancestors to CSP, restricted to the authenticated shop domain and admin.shopify.com.",guide:"https://shopify.dev/docs/apps/build/security/set-up-iframe-protection",docsUrl:`${v}/missing-embedded-csp`},{id:"MISSING_COMPLIANCE_WEBHOOKS",title:"Missing mandatory GDPR compliance webhooks",severity:"medium",points:-10,description:"Detects apps missing mandatory compliance webhooks (shop/redact, customers/data_request, customers/redact).",fix:"Add the missing webhook subscriptions to shopify.app.toml.",guide:"https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks",docsUrl:`${v}/missing-compliance-webhooks`},{id:"MISSING_DEPENDENCY_SECURITY_AUTOMATION",title:"Repository-level dependency management configuration not detected",severity:"low",points:-5,description:"Looks for a local Dependabot or Renovate configuration file at the repository root without validating its contents.",fix:"Add a Dependabot or Renovate configuration file at the repository root, or verify existing coverage.",docsUrl:`${v}/missing-dependency-security-automation`},{id:"EOL_API_VERSION",title:"End-of-life API version",severity:"low",points:-5,description:"Detects parsed config and React Router server API versions past the 12-month support window and 30-day grace period.",fix:"Update api_version in shopify.app.toml and the ApiVersion enum in shopify.server.ts.",guide:"https://shopify.dev/docs/api/usage/versioning",docsUrl:`${v}/eol-api-version`},{id:"WEAK_SHOP_VALIDATION",title:"Shop domain validation regex is not anchored",severity:"high",points:-15,description:"Detects unanchored myshopify.com regexes that allow domain spoofing and token theft.",fix:"Anchor the regex with ^ and $ to prevent prefix/suffix attacks.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/get-access-tokens/oauth",docsUrl:`${v}/weak-shop-validation`},{id:"APP_PROXY_LIQUID_INJECTION",title:"App proxy Liquid injection risk",severity:"high",points:-20,description:"Detects app proxy endpoints that interpolate request parameters into active Liquid or HTML responses.",fix:"Don't interpolate user input into application/liquid responses. Use application/json or escape all input.",guide:"https://shopify.dev/docs/apps/online-store/app-proxies",docsUrl:`${v}/app-proxy-liquid-injection`},{id:"MISSING_TENANT_ISOLATION",title:"Database query may not be scoped by shop",severity:"high",points:-20,description:"Detects database queries in authenticated routes that don't reference the shop domain or session.",fix:"Add the authenticated shop to the query's where clause.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens",docsUrl:`${v}/missing-tenant-isolation`},{id:"OPEN_REDIRECT",title:"Open redirect in auth callback",severity:"medium",points:-12,description:"Detects redirect targets taken from request parameters without validation.",fix:"Validate the redirect URL against an allowlist of permitted domains.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization",docsUrl:`${v}/open-redirect`},{id:"UNAUTHENTICATED_ENDPOINT",title:"Route handler lacks recognized auth verification",severity:"high",points:-15,description:"Uses the template route-auth heuristic, with agent review for context.shopify.authenticate.admin calls instead of treating them as missing authentication.",fix:"Complete the verification appropriate to the entry point before protected operations, and stop those operations when verification fails.",guide:"https://shopify.dev/docs/apps/auth",requires:"has_backend",docsUrl:`${v}/unauthenticated-endpoint`},{id:"REQUEST_CONTROLLED_ADMIN_CONTEXT",title:"Request input selects Admin API shop context",severity:"high",points:-20,description:"Detects request-derived shop values passed into unauthenticated.admin(...), allowing callers to select another shop's Admin API context.",fix:"Use the Admin API context returned by authenticate.admin(request). Never pass form, JSON, query, or route input into unauthenticated.admin(...).",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/online-access-tokens",docsUrl:`${v}/request-controlled-admin-context`},{id:"APP_PROXY_UNVERIFIED_SIGNATURE",title:"App proxy request trusted without signature verification",severity:"high",points:-15,description:"Detects app proxy parameters read without visible signature verification.",fix:"Verify app proxy signatures before trusting shop, customer, or path parameters.",guide:"https://shopify.dev/docs/apps/build/online-store/app-proxies/authenticate-app-proxies",requires:"app_proxy",docsUrl:`${v}/app-proxy-unverified-signature`},{id:"REQUEST_DERIVED_SHOP_SCOPE",title:"Query scoped by shop value taken from request input",severity:"high",points:-15,description:"Detects queries whose shop scope comes from request params rather than the authenticated session. Presence of a shop key is not proof of scoping when its value is attacker-controlled.",fix:"Scope the query with the shop from the authenticated session, or verify the supplied shop against a signed session or HMAC first.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens",docsUrl:`${v}/request-derived-shop-scope`},{id:"UNSCOPED_SHOP_CONFIG_WRITE",title:"Config write trusts shop from request input",severity:"high",points:-15,description:"Detects settings or metafield writes scoped by request-controlled shop input without visible auth verification.",fix:"Derive the target shop from an authenticated session or verified HMAC.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens",docsUrl:`${v}/unscoped-shop-config-write`},{id:"STATIC_FRAME_ANCESTORS",title:"Embedded app frame-ancestors uses a wildcard",severity:"high",points:-12,description:"Detects wildcard or static cross-shop frame-ancestors policies in embedded app code, whether written literally or built from variables.",fix:"Restrict frame-ancestors to Shopify Admin and the authenticated shop origin.",guide:"https://shopify.dev/docs/apps/build/security/set-up-iframe-protection",requires:"embedded_app",docsUrl:`${v}/static-frame-ancestors`},{id:"ACTIVE_UPLOADS_AND_PRIVILEGED_PREVIEWS",title:"Active upload or privileged preview may execute untrusted content",severity:"high",points:-15,description:"Investigates uploaded or imported active content that may execute in storefront, embedded admin, customer-account, or operator preview surfaces.",fix:"Sanitize or re-encode active content and isolate previews from privileged origins.",docsUrl:`${v}/active-uploads-and-privileged-previews`},{id:"DEPENDENCY_REACHABILITY",title:"Vulnerable dependency may be reachable in app code",severity:"medium",points:-10,description:"Investigates whether a vulnerable dependency version is actually used through the affected API, helper, or configuration.",fix:"Upgrade the dependency or remove the reachable vulnerable code path.",docsUrl:`${v}/dependency-reachability`},{id:"SESSION_LIFECYCLE_AND_REPLAY",title:"Session lifecycle or replay control may be missing",severity:"high",points:-15,description:"Investigates stale sessions, replayable signed links, and missing invalidation or idempotency across lifecycle transitions.",fix:"Invalidate stale authority and enforce replay protection or re-authorization before sensitive actions.",docsUrl:`${v}/session-lifecycle-and-replay`},{id:"OVERBROAD_DATA_ACCESS",title:"Data access may exceed the current request",severity:"medium",points:-10,description:"Investigates reads that may expose data outside the authenticated shop or user boundary.",fix:"Scope data access to the authenticated shop and minimum necessary records.",docsUrl:`${v}/overbroad-data-access`},{id:"CROSS_SITE_SCRIPTING",title:"Cross-site scripting risk",severity:"high",points:-15,description:"Investigates lower-trust data rendered as executable browser content in app-owned pages.",fix:"Use context-appropriate escaping, safe serialization, URL validation, or HTML sanitization at the final sink.",guide:"https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html",docsUrl:`${v}/cross-site-scripting`},{id:"SQL_INJECTION",title:"SQL injection risk",severity:"high",points:-15,description:"Investigates lower-trust input that changes SQL syntax in a reachable database operation.",fix:"Bind query values and use trusted mappings or safe identifier quoting for dynamic SQL structure.",guide:"https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html",docsUrl:`${v}/sql-injection`},{id:"SSRF_REQUEST_FORGERY",title:"Server-side request forgery risk",severity:"high",points:-15,description:"Investigates server-side requests whose destinations may be controlled by request input.",fix:"Allowlist destinations and reject private, local, and unsafe network targets.",docsUrl:`${v}/ssrf-request-forgery`},{id:"THEME_EXTENSION_XSS",title:"Theme extension cross-site scripting risk",severity:"high",points:-15,description:"Investigates untrusted values rendered without safe escaping in theme extensions.",fix:"Escape untrusted values and avoid raw HTML rendering.",requires:"theme_app_extension",docsUrl:`${v}/theme-extension-xss`},{id:"CSRF_MISSING_PROTECTION",title:"Cross-site request forgery protection may be missing",severity:"medium",points:-10,description:"Investigates state-changing endpoints for missing request authenticity protections.",fix:"Require an authenticated session and CSRF protection on state-changing requests.",docsUrl:`${v}/csrf-missing-protection`},{id:"MISSING_AUTHORIZATION_CHECK",title:"Authorization check may be missing",severity:"high",points:-15,description:"Investigates authenticated handlers that may not authorize access to the requested resource.",fix:"Authorize the actor and resource after authentication.",docsUrl:`${v}/missing-authorization-check`},{id:"SCRIPT_TAG_URL_INJECTION",title:"ScriptTag URL may be request controlled",severity:"high",points:-15,description:"Investigates ScriptTag writes whose source URL may be controlled by untrusted input.",fix:"Remove ScriptTag usage or restrict sources to trusted versioned assets.",docsUrl:`${v}/script-tag-url-injection`},{id:"TEXT_SETTING_HTML_SMUGGLING",title:"Text setting may be rendered as unsafe HTML",severity:"high",points:-15,description:"Investigates merchant-configurable text rendered as HTML without sanitization.",fix:"Render settings as text or sanitize with a strict allowlist.",docsUrl:`${v}/text-setting-html-smuggling`}];function mr(e){return G.find(t=>t.id===e)?.docsUrl}var gr=e=>e==="high"||e==="medium"||e==="low",yr=e=>e==="union"||e==="prefer-agent",vr=(e,t)=>{if(t===void 0)return"union";if(!yr(t))throw new Error(`Invalid precedence for agent check ${e}: ${t} (expected union or prefer-agent)`);return t},br=e=>{let t=/^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(e);if(!t)return{meta:{},body:e};let[,n="",i=""]=t,s={};for(let r of n.split(`
|
|
2318
|
+
`)){let o=r.indexOf(":");o!==-1&&(s[r.slice(0,o).trim()]=r.slice(o+1).trim())}return{meta:s,body:i.trim()}},Ze=(e=En)=>{let t=new Map;for(let n of e){let{meta:i,body:s}=br(n);if(i.id){if(t.has(i.id))throw new Error(`Duplicate agent stable ID: ${i.id}`);t.set(i.id,{id:i.id,version:Number(i.version??1),tier:"agentic",severity:gr(i.severity)?i.severity:"medium",prompt:s,prompt_hash:`sha256:${Zt(s).toString("hex")}`,precedence:vr(i.id,i.precedence)})}}return t},wr=`Each check below is a prompt for you to run against this
|
|
2319
2319
|
codebase. For each one, explore the repository, find real instances of what
|
|
2320
2320
|
it describes, and report them with evidence. Each check's docs_url is its
|
|
2321
2321
|
page on shopify.dev: link it when you explain a finding to the user.
|
|
@@ -2353,27 +2353,27 @@ Rules:
|
|
|
2353
2353
|
- An unsupported or unresolved check didn't pass. Never describe it as passing or complete.
|
|
2354
2354
|
- 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.
|
|
2355
2355
|
- 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.
|
|
2356
|
-
- Don't report things you couldn't confirm \u2014 uncertainty is not a finding.`,Bn=e=>{let t=G.find(n=>n.id===e);if(!t)throw new Error(`Agent check has no catalog entry: ${e}`);return t},Qn=e=>({schema_version:Ye,engine:{name:D,version:e},generated_at:new Date().toISOString(),checks:[...Ze().values()].map(t=>({id:t.id,version:t.version,severity:t.severity,docs_url:Bn(t.id).docsUrl,prompt:t.prompt})),instructions:vr}),_e=4e3,Fn=4e3,Mn=8e3,Un=4e3,$n=100,jn=50,Hn=4e3,ke=1024,Gn=1e3,br=["executed","not_applicable","unresolved"],N=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Xe=e=>Number.isInteger(e)&&e>=1,zn=e=>!e||e.startsWith("/")||/^[a-zA-Z]:[\\/]/.test(e)?!1:!e.includes("\0"),wr=e=>{if(!N(e))return"finding must be an object";let t=e;if(typeof t.check_id!="string"||!t.check_id)return"missing or invalid check_id";if(!Xe(t.check_version))return"missing or invalid check_version";if(typeof t.file!="string"||!t.file)return"missing or invalid file";if(!Number.isInteger(t.line)||t.line<1)return`invalid line number: ${t.line}`;if(typeof t.message!="string"||!t.message)return"missing or invalid message";if(!Array.isArray(t.evidence)||t.evidence.length===0)return"finding requires at least one evidence citation";if(t.evidence.length>jn)return`finding exceeds ${jn} evidence citations`;if(t.file.length>ke)return`file path exceeds ${ke} characters`;if(!zn(t.file))return`unsafe file path (must be relative): ${t.file}`;if(t.message.length>_e)return`message exceeds ${_e} characters`;if(t.snippet!==void 0&&typeof t.snippet!="string")return"snippet must be a string";if(typeof t.snippet=="string"&&t.snippet.length>Fn)return`snippet exceeds ${Fn} characters`;if(t.reasoning!==void 0&&typeof t.reasoning!="string")return"reasoning must be a string";if(typeof t.reasoning=="string"&&t.reasoning.length>Mn)return`reasoning exceeds ${Mn} characters`;if(t.confidence!==void 0&&(typeof t.confidence!="string"||!["high","medium","low"].includes(t.confidence)))return"confidence must be high, medium, or low";if(t.suppression!==void 0){let n=t.suppression;if(!N(n)||typeof n.justification!="string"||!n.justification.trim())return"suppression requires a justification";if(n.justification.length>Un)return`suppression justification exceeds ${Un} characters`}for(let n of t.evidence){if(!N(n))return"evidence citation must be an object";if(typeof n.file!="string"||!n.file)return"evidence file must be a non-empty string";if(!zn(n.file))return`unsafe evidence file path: ${n.file}`;if(n.file.length>ke)return`evidence file path exceeds ${ke} characters`;if(n.line!==void 0&&!Xe(n.line))return`invalid evidence line number: ${n.line}`;if(n.quote!==void 0&&typeof n.quote!="string")return"evidence quote must be a string";if(typeof n.quote=="string"&&n.quote.length>Hn)return`evidence quote exceeds ${Hn} characters`}},xr=(e,t,n)=>{if(!Xe(e.check_version))return"missing or invalid check_version";if(typeof t!="string"||!br.includes(t))return"status must be executed, not_applicable, or unresolved";let i=e.reason;if(i===void 0)return t==="executed"?void 0:`${t} requires a reason`;if(!N(i)||typeof i.code!="string"||!i.code||typeof i.message!="string"||!i.message.trim())return"reason requires a code and message";if(i.code.length>$n)return`reason code exceeds ${$n} characters`;if(i.message.length>_e)return`reason message exceeds ${_e} characters`;if(t==="not_applicable"&&n.has(e.check_id))return"a not_applicable check can't have findings"};function kr(e,t,n=Ze()){let i=[],s=[],r=new Set,o=new Set(t.filter(N).map(a=>a.check_id));return e.length>n.size?(i.push(`checks_executed contains ${e.length} entries, exceeding the limit of ${n.size}`),{reports:s,errors:i}):(e.forEach((a,c)=>{let l=`checks_executed[${c}]`;if(!N(a)){i.push(`${l}: executed check must be an object`);return}if(typeof a.check_id!="string"||!a.check_id){i.push(`${l}: missing or invalid check_id`);return}let d=`${l} (${a.check_id})`;if(!n.has(a.check_id)){i.push(`${d}: unknown check_id`);return}if(r.has(a.check_id)){i.push(`${d}: duplicate check_id in checks_executed`);return}r.add(a.check_id);let f=a.status??"executed",h=xr(a,f,o);if(h){i.push(`${d}: ${h}`);return}let g=a.reason;s.push({check_id:a.check_id,check_version:a.check_version,status:f,...g?{reason:{code:y(g.code),message:y(g.message)}}:{}})}),{reports:s,errors:i})}var Wn=e=>y(e.replace(/\\/g,"/")),_r=e=>({location:{file:Wn(e.file),line:e.line},message:y(e.message),evidence:e.evidence.map(t=>({location:{file:Wn(t.file),...t.line===void 0?{}:{line:t.line}},...t.quote===void 0?{}:{quote:y(t.quote)}})),...e.snippet===void 0?{}:{snippet:y(e.snippet)},...e.confidence===void 0?{}:{confidence:e.confidence},...e.reasoning===void 0?{}:{reasoning:y(e.reasoning)},...e.suppression===void 0?{}:{suppression:{justification:y(e.suppression.justification)}}});function Sr(e,t){let n=[],i=new Map;for(let o of t)i.set(o.check_id,[...i.get(o.check_id)??[],o]);let s=new Map(e.map(o=>[o.check_id,o])),r=e.map(o=>({report:o,findings:i.get(o.check_id)??[]}));for(let[o,a]of i){let c=[...new Set(a.map(f=>f.check_version))].sort((f,h)=>f-h),l=s.get(o);if(l){c.some(f=>f!==l.check_version)&&n.push(`${o}: findings claim check_version ${c.join(", ")}, but its checks_executed entry claims ${l.check_version}`);continue}let[d]=c;if(d!==void 0){if(c.length>1){n.push(`${o}: findings claim different check_version values (${c.join(", ")})`);continue}r.push({report:{check_id:o,check_version:d,status:"executed"},findings:a})}}return{groups:r,errors:n}}function Tr(e){let t=Bn(e.id);return{title:t.title,severity:e.severity,description:t.description,...t.guide?{guide:t.guide}:{},docs_url:t.docsUrl,current_version:e.version,precedence:e.precedence}}var Er=e=>{if(!N(e))return{error:"scope is required and must be an object"};let{include_dirs:t,excludes:n,no_git_ignore:i}=e,s=r=>Array.isArray(r)&&r.every(o=>typeof o=="string");return s(t)?s(n)?typeof i!="boolean"?{error:"scope.no_git_ignore must be a boolean"}:{scope:xe({include_dirs:t,excludes:n,no_git_ignore:i})}:{error:"scope.excludes must be an array of strings"}:{error:"scope.include_dirs must be an array of strings"}},Vn=(e,t,n)=>{let i=e[t];return i===void 0?[]:Array.isArray(i)?i:(n.push(`${t} must be an array`),[])};function Ir(e,t){if(!N(e))return{ok:!1,errors:["The findings document must be a JSON object."]};let n=[];e.schema_version!==de&&n.push(`schema_version must be ${de}`);let i=Er(e.scope);"error"in i&&n.push(i.error);let s=Vn(e,"checks_executed",n),r=Vn(e,"findings",n),o=Ze(),a=kr(s,r,o);n.push(...a.errors);let c=[];r.length>Gn?n.push(`findings contains ${r.length} findings, exceeding the limit of ${Gn}`):r.forEach((f,h)=>{let g=N(f)&&typeof f.check_id=="string"?f.check_id:void 0,x=g?`findings[${h}] (${g})`:`findings[${h}]`,E=wr(f)??(o.has(g??"")?void 0:"unknown check_id");if(E){n.push(`${x}: ${E}`);return}c.push(f)});let l=Sr(a.reports,c);if(n.push(...l.errors),n.length>0||"error"in i)return{ok:!1,errors:n.map(y)};let d=l.groups.map(({report:f,findings:h})=>({id:f.check_id,version:f.check_version,status:f.status,...f.reason?{reason:f.reason}:{},snapshot:Tr(o.get(f.check_id)),findings:h.map(_r)})).sort((f,h)=>f.id.localeCompare(h.id));return{ok:!0,document:{schema_version:C,source:"agent",engine:{name:D,version:t.engineVersion},generated_at:t.generatedAt??new Date().toISOString(),scope:i.scope,checks:d}}}u();function z(){return Wt}u();u();u();u();function F(e){return e instanceof Error&&"code"in e&&(e.code==="ENOENT"||e.code==="ENOTDIR")}function q(e,t){let n=t instanceof Error&&"code"in t&&typeof t.code=="string"?t.code:void 0;return n?`Could not inspect ${e} (${n})`:`Could not inspect ${e}`}u();u();import{lstatSync as Ar}from"node:fs";function Se(e){let t=e;for(;;){try{let i=Ar(b(t,".git"));return i.isDirectory()||i.isFile()?{status:"found",directory:t}:{status:"ambiguous",reason:"Could not determine repository ownership from .git"}}catch(i){if(!F(i))return{status:"ambiguous",reason:q(".git",i)}}let n=S(t);if(n===t)return{status:"none"};t=n}}import{lstatSync as Rr,realpathSync as Cr}from"node:fs";function Jn(e){let t=Cr(L());return{excludePatterns:e.excludePatterns.filter(n=>n!=="").map(n=>Lr(n,t)),gitFiltering:!e.noGitIgnore,workingDirectory:t}}var Pr=/[*?[\]{}()!#\\]/;function Lr(e,t){let n=e.split("/"),i=n.findIndex(c=>Pr.test(c)),s=i===-1?n:n.slice(0,i),r=s.join("/")||(e.startsWith("/")?"/":"");if(r==="")return e;let o=w(t,T(t,r)),a=n.slice(s.length).join("/");return a===""?o:o===""?a:`${o}/${a}`}function et(e,t){return t.status==="listed"?{directory:e,paths:new Set(t.paths)}:void 0}function Kn(e,t,n){return e.gitFiltering&&(k(n.absolutePath)===".git"||t&&Dr(t,n))?!0:tt(e,n.absolutePath)}function Yn(e,t,n){let i=n.split("/");return i.some((s,r)=>{let o=b(t,...i.slice(0,r+1));return e.gitFiltering&&s===".git"||tt(e,o)})}function Te(e,t){let n=w(e.workingDirectory,t).split("/");return n.some((i,s)=>tt(e,b(e.workingDirectory,...n.slice(0,s+1))))}function Dr(e,t){let n=w(e.directory,t.absolutePath);return e.paths.has(t.isDirectory?`${n}/`:n)}function tt(e,t){if(e.excludePatterns.length===0)return!1;let n=w(e.workingDirectory,t);return e.excludePatterns.some(i=>Xt(n,i))}async function nt(e){let t=await Nr(e);return t.status!=="listed"&&sn(`app security check: git ignore listing skipped (${t.status})`),t}async function Nr(e){let t=await H(e,["rev-parse","--is-inside-work-tree"]);if(t===void 0)return{status:"failed"};if(t.exitCode!==0)return Se(e).status==="none"?{status:"not-a-repository"}:{status:"failed"};if(t.stdout.trim()==="false")return{status:"not-a-repository"};if(t.stdout.trim()!=="true")return{status:"failed"};let n=await H(e,["ls-files","-z","--others","--ignored","--exclude-standard","--directory","--","."]);return n===void 0||n.exitCode!==0?{status:"failed"}:{status:"listed",paths:ti(n.stdout)}}async function Xn(e){let t=S(e);return t===e?!1:(await H(t,["check-ignore","--no-index","-q","--",k(e)]))?.exitCode===0}async function Zn(e){let t=await H(e,["ls-files","-z","--cached","--","."]);if(!(t===void 0||t.exitCode!==0))return ti(t.stdout)}async function ei(e){try{let t=Rr(b(e,".git"));return t.isDirectory()||t.isFile()?await nt(e):{status:"failed"}}catch(t){return F(t)?void 0:{status:"failed"}}}function ti(e){return e.split("\0").filter(t=>t!=="")}u();var ni=[".github/dependabot.yml",".github/dependabot.yaml","renovate.json","renovate.jsonc","renovate.json5",".github/renovate.json",".github/renovate.jsonc",".github/renovate.json5",".gitlab/renovate.json",".gitlab/renovate.jsonc",".gitlab/renovate.json5",".renovaterc",".renovaterc.json",".renovaterc.jsonc",".renovaterc.json5"];function qr(e){return Object.keys(e.dependencies).length>0||Object.keys(e.devDependencies??{}).length>0}function Or(e){return e.replace(/\\/g,"/")}function Fr(e){let t=e.map(i=>Or(i.path)),n=t.find(i=>i==="package.json");return n||[...t].sort((i,s)=>{let r=i.split("/").length-s.split("/").length;return r===0?i.localeCompare(s):r})[0]}function ii(e){let t=e.manifests.filter(qr);if(t.length===0)return{issues:[]};let{files:n,unresolvedReason:i}=e.dependencyAutomation;return i?{issues:[],unresolvedReason:i}:n.length>0?{issues:[]}:{issues:[{id:"MISSING_DEPENDENCY_SECURITY_AUTOMATION",severity:"low",points:-5,title:"Repository-level dependency management configuration not detected",message:"No recognized Dependabot or Renovate configuration file was found at the repository root. Add dependency update automation there, or verify that an existing integration covers this app. This check only looks for local configuration files; it does not validate their contents or inspect hosted integrations, CI workflows, or execution results.",location:{file:Fr(t)},fix:{automated:!1,description:"Add a Dependabot or Renovate configuration file at the repository root, or verify existing coverage."}}]}}import{lstatSync as W,readdirSync as Mr,realpathSync as le}from"node:fs";function ri(e,t){let n=dt(e);if(n===void 0)return null;try{let i=Ge(n);return jr(i,e,n,t)}catch{return V(e,{ok:!1,reason:"unreadable",detail:"TOML could not be parsed"}),null}}var Ur=p.object({api_version:p.string().optional(),privacy_compliance:p.object({customer_deletion_url:p.string().optional(),customer_data_request_url:p.string().optional(),shop_deletion_url:p.string().optional()}).optional(),subscriptions:p.array(p.unknown()).optional()}),$r=cn.extend({uri:p.preprocess(e=>Ve(e),p.string({invalid_type_error:"Value must be string"}))});function jr(e,t,n,i){let s=Hr(e.access_scopes,t,i),r=Gr(e.auth,t,i),o=zr(e.webhooks,t,i);return{raw:e,path:t,content:n,scopes:s,apiVersion:o.apiVersion,redirectUrls:r,webhooks:o.subscriptions}}function Hr(e,t,n){if(e===void 0)return;let i=un.safeParse(e);if(!i.success){Ee(n,t,"access_scopes section could not be parsed");return}let s=i.data.scopes?.split(/[\s,]+/).filter(Boolean)??[],r=[...new Set([...s,...i.data.required_scopes??[]])];return r.length>0?r.join(","):void 0}function Gr(e,t,n){if(e===void 0)return[];let i=hn.safeParse(e);return i.success?i.data.redirect_urls:(Ee(n,t,"auth section could not be parsed"),[])}function zr(e,t,n){if(e===void 0)return{subscriptions:[]};let i=Ur.safeParse(e);if(!i.success)return Ee(n,t,"webhooks section could not be parsed"),{subscriptions:[]};let s=(i.data.subscriptions??[]).flatMap(a=>Vr(a,t,n)),r=i.data.privacy_compliance,o=[{topic:"customers/redact",uri:r?.customer_deletion_url},{topic:"customers/data_request",uri:r?.customer_data_request_url},{topic:"shop/redact",uri:r?.shop_deletion_url}].flatMap(({topic:a,uri:c})=>c?[{topics:[a],uri:c}]:[]);return{apiVersion:i.data.api_version,subscriptions:[...s,...o]}}var Wr=p.object({uri:p.preprocess(e=>Ve(e),p.string({invalid_type_error:"Value must be string"}))});function Vr(e,t,n){let i=$r.safeParse(e);if(i.success)return[{topics:[...i.data.topics??[],...i.data.compliance_topics??[]],uri:i.data.uri}];Ee(n,t,"webhook subscription could not be parsed");let s=Wr.safeParse(e);return s.success?[{topics:[],uri:s.data.uri}]:[]}function Ee(e,t,n){e&&V(t,{ok:!1,reason:"unreadable",detail:n})}function Br(e,t){try{return Mr(e,{withFileTypes:!0})}catch(n){V(e,{ok:!1,reason:"unreadable",detail:q(t?"scan directory":Ie(e),n)});return}}async function oi({appDirectory:e,scanDirectories:t,requestedScanDirectories:n,selectedAppConfigFilePath:i,rules:s}){let r=[];if(s.gitFiltering)for(let l of n)Te(s,l)||await Xn(l)&&r.push(l);let o=[];for(let l of t)o.push(await Jr(l,s,r.includes(l)));let a=[...o.flatMap(l=>l.absolutePaths),...i?[i]:[]],c=[...new Set(a.map(l=>R(w(e,l))))].sort();return{paths:c,ignoredScanDirectories:r,otherAppDirectories:Qr(e,c),listingStatus:o[0]?.listingStatus??(s.gitFiltering?"tracked-only":"git-ignore-off")}}function Qr(e,t){let n=t.filter(i=>We(k(i))).map(i=>S(i)).filter(i=>i!==".");return[...new Set(n)].map(i=>b(e,i))}async function Jr(e,t,n){if(!t.gitFiltering)return{absolutePaths:await si(e,t,void 0),listingStatus:"git-ignore-off"};if(n){if(Te(t,e))return{absolutePaths:[],listingStatus:"tracked-only"};let s=await Zn(e);if(s===void 0)throw new I(`Couldn't list the files Git tracks in ${w(L(),e)||"."}.`);return{absolutePaths:s.filter(r=>!Yn(t,e,r)).map(r=>b(e,r)).filter(Kr),listingStatus:"tracked-only"}}let i=await nt(e);return{absolutePaths:await si(e,t,et(e,i)),listingStatus:i.status}}function Kr(e){try{return!W(e).isDirectory()}catch{return!1}}async function si(e,t,n){if(Te(t,e))return[];let i=[],s=[{directory:e,repository:n}];for(let{directory:r,repository:o}of s){let a=Br(r,r===e);if(a!==void 0)for(let c of a){let l=b(r,c.name),d=c.isDirectory();if(Kn(t,o,{absolutePath:l,isDirectory:d}))continue;if(!d){i.push(l);continue}let f=t.gitFiltering?await ei(l):void 0;s.push({directory:l,repository:f?et(l,f):o})}}return i}function Yr(e,t){let n=new Map([...t].map(i=>[i,[]]));for(let i of e){if(!pi(i))continue;let s=S(i);for(;s!==".";)n.get(s)?.push(i),s=S(s);n.get(".")?.push(i)}return n}function ai(e,t){let n=t.filter(s=>k(s)==="shopify.extension.toml");if(n.length===0)return[];let i=Yr(t,new Set(n.map(s=>S(s))));return n.flatMap(s=>{let r=b(e,s),o=dt(r);if(o===void 0)return[];try{let c=Ge(o).type,l=lt(e,i.get(S(s))??[]);return[{path:s,type:c,content:o,files:l}]}catch{return V(r,{ok:!1,reason:"unreadable",detail:"TOML could not be parsed"}),[]}})}var Xr=5e5,ot=[],it=new Map,st;function at(e){st=e,ot=[],it.clear()}function pe(){if(!st)throw new tn("The repository reader was used before it was configured.");return st}function ct(){return[...ot]}function V(e,t){let n=w(pe().appDirectory,e).replace(/\\/g,"/");ot.push({path:n.length>0?n:e,reason:t.reason,...t.sizeBytes===void 0?{}:{size_bytes:t.sizeBytes},...t.detail?{detail:t.detail}:{}})}function ci(e){try{let t=Kt(e);return t>Xr?{ok:!1,reason:"too_large",sizeBytes:t}:{ok:!0,content:Qt(e)}}catch(t){return{ok:!1,reason:"unreadable",detail:t instanceof Error?t.message:String(t)}}}function X(e){return{ok:!1,reason:"unreadable",detail:e}}function Ie(e){let t=R(w(pe().appDirectory,e));return t.length===0||t==="."||Vt(t)?"path":t}function di(e,t){let n=T(e),i=T(n,t),s=Ie(i);if(!P(n,i))return{status:"unresolved",reason:`${s} escapes the scan directory`};let r;try{r=le(n)}catch(a){return{status:"unresolved",reason:q("scan directory",a)}}let o=R(w(n,i)).split("/").filter(a=>a.length>0&&a!==".");if(o.includes(".."))return{status:"unresolved",reason:`${s} escapes the scan directory`};if(o.length===0)return{status:"unresolved",reason:`${s} is not a file`};try{let a=le(i);return P(r,a)?W(a).isFile()?{status:"file",path:a}:{status:"unresolved",reason:`${s} is not a file`}:{status:"unresolved",reason:`${s} resolves outside the scan directory`}}catch(a){return F(a)?Zr(r,o,s):{status:"unresolved",reason:q(s,a)}}}function Zr(e,t,n){let i=e;for(let[s,r]of t.entries()){i=b(i,r);try{W(i);let o=le(i);if(!P(e,o))return{status:"unresolved",reason:`${n} resolves outside the scan directory`};let a=W(o);if(s===t.length-1){if(!a.isFile())return{status:"unresolved",reason:`${n} is not a file`}}else if(!a.isDirectory())return{status:"unresolved",reason:`${n} contains an entry that is not a directory`};i=o}catch(o){if(F(o)){try{if(W(i).isSymbolicLink())return{status:"unresolved",reason:`${n} contains a dangling symbolic link`}}catch{return{status:"missing"}}return{status:"missing"}}return{status:"unresolved",reason:q(n,o)}}}return{status:"file",path:i}}function eo(e){let t=pe().scanDirectories.find(i=>P(i,e));if(t===void 0)return{failure:X(`${Ie(e)} is outside every scan directory`)};let n=di(t,e);return n.status==="file"?{path:n.path}:n.status==="missing"?{failure:X("path does not exist")}:{failure:X(n.reason)}}function to(e){let t=Ie(e);try{let n=le(e);return W(n).isFile()?{path:n}:{failure:X(`${t} is not a file`)}}catch(n){return{failure:X(F(n)?"path does not exist":q(t,n))}}}function Ae(e){let t=T(e),n=it.get(t);if(n)return n;let i=pe().explicitInputs.has(t)?to(t):eo(t),s=i.path?ci(i.path):i.failure;return it.set(t,s),s.ok||V(t,s),s}function no(e,t){let n=di(e,t);if(n.status==="missing")return;let i=n.status==="file"?ci(n.path):X(n.reason);return i.ok||V(b(e,t),i),i}function dt(e){let t=Ae(e);return t.ok?t.content.toString():void 0}var rt={".js":{name:"javascript",supported:!0},".jsx":{name:"javascript",supported:!0},".mjs":{name:"javascript",supported:!0},".cjs":{name:"javascript",supported:!0},".ts":{name:"typescript",supported:!0},".tsx":{name:"typescript",supported:!0},".mts":{name:"typescript",supported:!0},".cts":{name:"typescript",supported:!0},".prisma":{name:"prisma",supported:!0},".liquid":{name:"liquid",supported:!0},".html":{name:"html",supported:!0},".ejs":{name:"ejs",supported:!1},".erb":{name:"erb",supported:!1},".hbs":{name:"handlebars",supported:!1},".jinja2":{name:"jinja2",supported:!1},".twig":{name:"twig",supported:!1},".rb":{name:"ruby",supported:!1},".php":{name:"php",supported:!1},".py":{name:"python",supported:!1},".java":{name:"java",supported:!1},".kt":{name:"kotlin",supported:!1},".kts":{name:"kotlin",supported:!1},".go":{name:"go",supported:!1},".rs":{name:"rust",supported:!1},".cs":{name:"csharp",supported:!1},".swift":{name:"swift",supported:!1},".scala":{name:"scala",supported:!1},".ex":{name:"elixir",supported:!1},".exs":{name:"elixir",supported:!1},".vue":{name:"vue",supported:!1},".svelte":{name:"svelte",supported:!1}};function li(e){let t=O(e);return t in rt?rt[t]:void 0}function pi(e){return li(e)?.supported===!0}function ui(e){return e.flatMap(t=>{let n=li(t);return n?[{path:t,extension:O(t),language:n.name,supported:n.supported}]:[]}).sort((t,n)=>t.path.localeCompare(n.path))}function lt(e,t){return t.filter(pi).map(n=>{let i=b(e,n),s=Ae(i);return{path:n,absolutePath:i,ext:O(n),content:s.ok?s.content.toString():void 0}})}var io=new Set(["package-lock.json","pnpm-lock.yaml","yarn.lock"]),so=new Set([...Object.keys(rt),".md",".mdx",".markdown",".yaml",".yml",".json",".jsonc",".toml",".sh",".bash",".zsh",".fish",".properties",".ini",".cfg",".conf",".xml",".graphql",".gql",".sql",".txt",".pem"]),ro=new Set(["Dockerfile","Containerfile","Gemfile","Rakefile","Procfile","Makefile"]);function oo(e){if(so.has(O(e)))return!0;let t=k(e);return t===".env"||t.startsWith(".env.")||ro.has(t)||t.startsWith("Dockerfile.")||t.startsWith("Containerfile.")}function ao(e){let t=e.subarray(0,Math.min(e.length,8e3));if(t.includes(0))return!0;let n=0;for(let i of t)(i<8||i>13&&i<32)&&n++;return t.length>0&&n/t.length>.1}function hi(e,t,n){return t.filter(oo).filter(s=>!io.has(k(s))).filter(s=>!We(k(s))||s===n).flatMap(s=>{let r=b(e,s),o=Ae(r);return o.ok?ao(o.content)?[]:[{path:s,absolutePath:r,ext:O(s),content:o.content.toString()}]:[{path:s,absolutePath:r,ext:O(s),content:void 0}]})}function co(e){let t=Se(e);return t.status==="ambiguous"?{unresolvedReason:t.reason}:{directory:t.status==="found"?t.directory:e}}function fi(e,t){let n;try{if(n=le(T(e)),!W(n).isDirectory())return{files:[],unresolvedReason:"App root is not a directory"}}catch(d){return{files:[],unresolvedReason:q("app root",d)}}let i=co(n);if("unresolvedReason"in i)return{files:[],unresolvedReason:i.unresolvedReason};let s=i.directory,{scanDirectories:r}=pe(),o=d=>s===n||r.some(f=>P(f,d)),a=new Set(t),c=[],l;for(let d of ni){let f=b(s,d),h=R(w(n,f)),g=o(f);if(g&&!a.has(h))continue;let x=g?Ae(f):no(s,d);if(x!==void 0){if(!x.ok){l??=x.reason==="too_large"?`${h} is too large to inspect`:x.detail??`Could not read ${h}`;continue}c.push({path:h,absolutePath:f,ext:O(h),content:x.content.toString()});break}}return c.length>0?{files:c}:{files:c,...l?{unresolvedReason:l}:{}}}function mi(e){return e.filter(t=>k(t)==="package.json")}var lo=p.object({dependencies:p.record(p.string()).optional(),devDependencies:p.record(p.string()).optional()});function gi(e,t){let n=[],i=t.filter(s=>k(s)==="package.json");for(let s of i){let r=b(e,s),o=dt(r);if(o!==void 0)try{let a=lo.parse(JSON.parse(o));n.push({path:s,absolutePath:r,type:"npm",content:o,dependencies:a.dependencies??{},devDependencies:a.devDependencies??{}})}catch{n.push({path:s,absolutePath:r,type:"npm",content:o,dependencies:{},devDependencies:{}}),V(r,{ok:!1,reason:"unreadable",detail:"manifest could not be parsed"})}}return n}u();var yi="@shopify/shopify-app-react-router";function vi(e,t,n,i=e?[e]:[]){let s=t.some(d=>d.type==="theme"),r=t.some(d=>d.type==="theme"&&mo(d)),o=i.some(d=>d.raw.embedded===!0),a=n.some(d=>d.content?/script[_-]?tags?|ScriptTag/i.test(d.content):!1),c=n.some(d=>d.content?/metafields?Set|metafields?\/.*(?:POST|PUT|create|update)|write.*metafield|metafield.*write/i.test(d.content):!1),l=n.some(go);return{theme_app_extension:s,app_embed:r,embedded_app:o,script_tags:a,webhooks:!!e?.webhooks.length,app_proxy:!!e?.raw?.app_proxy,storefront_metafield_writes:c,has_backend:l,declared_ip_allowlist:!1,checkout_extension:t.some(d=>d.type==="checkout_ui"||d.type==="checkout_ui_extension")}}function bi(e,t,n=[]){let i=e.filter(o=>o.dependencies[yi]!==void 0||o.devDependencies?.[yi]!==void 0);if(i.length===0)return[];let s=t.map(o=>o.path);return[...new Set([".",...i.map(o=>S(o.path))])].filter(o=>uo(o,s)).filter(o=>!ho(o,n)).sort()}function pt(e,t){let n=Z(e,t);return n!==void 0&&/^app\/shopify\.server\.[cm]?[jt]sx?$/.test(n)}function wi(e,t,n){let i=xi(n).filter(r=>!po(r,t));return(!ut(e)||t.some(r=>Z(r,e)!==void 0))&&!i.some(r=>Z(r,e)!==void 0)}function po(e,t){return t.some(n=>n==="."?!ut(e):e===n||Z(n,e)!==void 0)}function ut(e){return e===".."||e.startsWith("../")}function Z(e,t){if(e===".")return t;if(!t.startsWith(`${e}/`))return;let n=t.slice(e.length+1);return ut(n)?void 0:n}function uo(e,t){return t.some(n=>Z(e,n)?.startsWith("app/routes/"))&&t.some(n=>pt(e,n))}function ho(e,t){return e==="."?!1:t.includes(e)||xi(t).some(n=>e===n||Z(n,e)!==void 0)}function xi(e){return e.flatMap(t=>fo(t)?[`${t}/app`,`${t}/web`]:[t])}function fo(e){return e.split("/").every(t=>t==="..")}function ki(e,t,n){let i=n.length>0,s=e.filter(h=>h.type==="theme"),r=s.length>0,o=new Set(s.flatMap(h=>h.files.map(g=>g.path))),a=t.length>0,c;i&&r?c="mixed":i?c="react_router":r&&t.some(h=>!o.has(h.path))?c="mixed":r?c="theme_app_extension":a?c="unknown":c="config_only";let l;i?l="react_router":c==="config_only"||c==="theme_app_extension"?l="none":c==="mixed"?l="mixed":l="unknown";let d=new Map;for(let h of t){let g=d.get(h.language)??{supported:h.supported,files:[]};g.supported&&=h.supported,g.files.push(h.path),d.set(h.language,g)}let f=[...d.entries()].map(([h,g])=>({name:h,support:g.supported?"supported":"unsupported",files:g.files.sort()})).sort((h,g)=>h.name.localeCompare(g.name));return{framework:l,surface:c,languages:f}}function mo(e){return e.files.some(t=>t.ext===".liquid"&&t.content?.includes('"target"')&&t.content?.includes("body"))}function go(e){let t=e.content;return t?!!(/\b(?:app|router)\.(get|post|put|delete|patch)\s*\(/.test(t)||/export\s+(?:async\s+)?(?:function|const)\s+(?:loader|action)\b/.test(t)):!1}u();function _i(e,t,n,i,s,r){return{timestamp:new Date().toISOString(),security_version:z(),files_scanned:e,rules_run:t,rules_skipped:n,files_skipped_count:i.length,...i.length>0?{files_skipped:i}:{},coverage_gaps:r,checks_executed:s}}u();function yo(e){return e.split(/[\s,]+/).map(t=>t.trim()).filter(Boolean)}var ht={id:"DEPRECATED_SCRIPT_TAG_SCOPE",title:"Deprecated ScriptTag capability",severity:"medium",points:-10,check(e){return e.appTomls.flatMap(t=>yo(t.scopes??"").filter(i=>i==="write_script_tags"||i==="read_script_tags").map(i=>({id:this.id,severity:"medium",points:-10,title:this.title,message:`Scope "${i}" is deprecated. Apps should use app embeds instead of the ScriptTag API.`,location:{file:w(e.appRoot,t.path).replace(/\\/g,"/")},fix:{automated:!1,description:`Remove "${i}" from scopes and migrate to app embeds`,guide:"https://shopify.dev/docs/apps/online-store/app-embeds"}})))}},Si={id:"INSECURE_WEBHOOK_URL",title:"Configured callback URL is not HTTPS",severity:"high",points:-12,check(e){let t=[];for(let n of e.appTomls){let i=[...n.webhooks.map(s=>({kind:"Webhook URI",value:s.uri})),...n.redirectUrls.map(s=>({kind:"OAuth redirect URI",value:s}))];for(let{kind:s,value:r}of i)vo(r,s==="Webhook URI")||t.push({id:this.id,severity:this.severity,points:this.points,title:this.title,message:`${s} is not a conservative secure URL. Use a Shopify-relative path or an HTTPS URL with an exact host and path.`,location:{file:w(e.appRoot,n.path).replace(/\\/g,"/")},fix:{automated:!1,description:"Use an HTTPS URL with an exact host and path, or a Shopify-relative path where supported.",guide:s==="Webhook URI"?"https://shopify.dev/docs/apps/webhooks":"https://shopify.dev/docs/apps/build/authentication-authorization"}})}return t}};function vo(e,t){if(/^\/(?!\/)[^\s*]*$/.test(e)||t&&/^(?:pubsub|eventbridge):\/\/[^\s*]+$/i.test(e)||t&&/^arn:aws:events:[a-z0-9-]+:\d*:event-source\/[^\s*]+$/i.test(e))return!0;try{let n=new URL(e);return n.protocol!=="https:"||!n.hostname||n.hostname.includes("*")||n.username||n.password||n.hash?!1:n.pathname.startsWith("/")&&!n.pathname.includes("*")}catch{return!1}}u();var bo=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"]),ee="[A-Za-z_$][\\w$]*",B="shop(?:Domain)?",mt=/\b(?:accessToken|access_token|sessionToken|session_token|apiSecret|api_secret|clientSecret|client_secret|SHOPIFY_API_SECRET|SHOPIFY_ACCESS_TOKEN)\b/i;function Ei(e){let t=[],n=new Set;for(let i of e){if(!ne(i)||!i.path.includes("/routes/")||/auth[._/-]?(?:login|callback)/i.test(i.path))continue;let s=ue(i.content),r=/export\s+(?:async\s+)?(?:function\s+|const\s+)(loader|action)\b/g,o=r.exec(s);for(;o;){let a=s.slice(o.index,Io(s,r.lastIndex)),c=/\bawait\s+authenticate\.(?:admin|public\.[A-Za-z_$][\w$]*|webhook)\s*\(/.exec(a),l=/\b(?:admin\.graphql|prisma\.|db\.|session\.|metafields?Set|unauthenticated\.admin)\b/.test(a);/\bcontext\s*\.\s*shopify\s*\.\s*authenticate\s*\.\s*admin\s*\(/.test(a)?n.add(i.path):!c&&l&&t.push(Re("UNAUTHENTICATED_ENDPOINT",i,o.index,"Route handler lacks recognized auth verification","The React Router loader/action doesn't have a recognized awaited Shopify authentication barrier.","Call and await authenticate.admin(request) (or the applicable Shopify authenticator) before protected access.",-15)),o=r.exec(s)}}return{issues:t,...n.size>0?{unresolvedReason:`Context-based route authentication needs agent review in ${[...n].join(", ")}. A context.shopify.authenticate.admin call is neither proof of verification nor a missing-auth finding.`,unresolvedReasonCode:"agent_investigation_required"}:{}}}function Ii(e){let t=[];for(let n of e){if(!ne(n))continue;let i=Ro(wt(n.content)),s=Ai(i,["request","req"],wo(i)),r=/\bunauthenticated\.admin\s*\(/g,o=r.exec(i);for(;o;){let a=Ao(i,r.lastIndex);a&>(a.text,s)&&t.push(Re("REQUEST_CONTROLLED_ADMIN_CONTEXT",n,o.index,"Request input selects Admin API shop context","A request-derived shop value reaches unauthenticated.admin(...). Authentication elsewhere in the route does not prove that this independently supplied shop belongs to that session.","Use the Admin API context returned by authenticate.admin(request), or derive the shop directly from its verified session.",-20)),r.lastIndex=a?.end??r.lastIndex,o=r.exec(i)}}return t}function Ai(e,t,n=new Set){let i=t.map(te).join("|"),s=new Set,r=new Set,o=new Set,a=new Set,c=new RegExp(`(?:const|let|var)\\s+(${ee})\\s*=\\s*([\\s\\S]{1,500}?)(?:;|\\n(?=\\s*(?:const|let|var|return|await|})))`,"g"),l=c.exec(e);for(;l;){let g=l[1],x=l[2].trim();(new RegExp(`^(?:await\\s+)?(?:${i})\\.json\\s*\\(\\s*\\)$`).test(x)||new RegExp(`^(?:${i})\\.(?:body|query|params)$`).test(x)||Ti(x,i,n))&&r.add(g),new RegExp(`^(?:await\\s+)?(?:${i})\\.formData\\s*\\(\\s*\\)$`).test(x)&&o.add(g),new RegExp(`^new\\s+URL\\s*\\(\\s*(?:${i})\\.url\\s*\\)$`).test(x)&&a.add(g),l=c.exec(e)}let d={requestPattern:i,requestControlled:s,requestContainers:r,formDataContainers:o,urlContainers:a,requestShopHelpers:n},f=/(?:const|let|var)\s*\{([\s\S]{1,300}?)\}\s*=\s*([\s\S]{1,300}?)(?:;|\n)/g,h=f.exec(e);for(;h;){let g=h[2].trim();if(new RegExp(`^(?:await\\s+)?(?:${i})\\.json\\s*\\(\\s*\\)$`).test(g)||new RegExp(`^(?:${i})\\.(?:body|query|params)$`).test(g)||r.has(g)||Ti(g,i,n))for(let E of h[1].split(",")){let ye=new RegExp(`^\\s*${B}(?:\\s*:\\s*(${ee}))?\\s*$`).exec(E);ye&&s.add(ye[1]??E.trim())}h=f.exec(e)}for(let g=0;g<5;g++){let x=!1;for(c.lastIndex=0,l=c.exec(e);l;){let E=l[1];!s.has(E)&>(l[2].trim(),d)&&(s.add(E),x=!0),l=c.exec(e)}if(!x)break}return d}function wo(e){let t=new Set,n="(?:\\s*:\\s*[^,)]+)?",i="(?:\\s*:\\s*(?:[^{}]|\\{[^{}]*\\})*)?",s=[new RegExp(`\\bfunction\\s+(${ee})\\s*\\(\\s*(${ee})${n}[^)]*\\)${i}\\s*\\{`,"g"),new RegExp(`\\b(?:const|let|var)\\s+(${ee})\\s*=\\s*(?:async\\s+)?\\(\\s*(${ee})${n}[^)]*\\)${i}\\s*=>\\s*\\{`,"g")];for(let r of s){let o=r.exec(e);for(;o;){let a=Ci(e,r.lastIndex-1);a&&xo(a.text,o[2])&&t.add(o[1]),r.lastIndex=a?.end??r.lastIndex,o=r.exec(e)}}return t}function xo(e,t){let n=Ai(e,[t]);return ko(e).some(i=>gt(i,n))}function ko(e){let t=[],n=/\breturn\s*\{/g,i=n.exec(e);for(;i;){let s=Ci(e,n.lastIndex-1);if(!s)break;let r=new RegExp(`(?:^|,)\\s*(${B})(?:\\s*:\\s*([^,}]+))?\\s*(?=,|$)`,"g"),o=r.exec(s.text);for(;o;)t.push((o[2]??o[1]).trim()),o=r.exec(s.text);n.lastIndex=s.end,i=n.exec(e)}return t}function Ti(e,t,n){return[...n].some(i=>new RegExp(`^${Ri(i,t)}$`).test(e))}function Ri(e,t){return`(?:await\\s+)?${te(e)}\\s*\\(\\s*(?:${t})(?:\\s*,[\\s\\S]*?)?\\s*\\)`}function gt(e,t){if(new RegExp(`(?:${t.requestPattern})\\.(?:body|query|params)(?:\\?\\.|\\.|\\[\\s*["'])${B}`).test(e)||/(?:searchParams|formData)\.get\s*\(\s*["']shop(?:Domain)?["']\s*\)/.test(e)||new RegExp(`new\\s+URL\\s*\\(\\s*(?:${t.requestPattern})\\.url\\s*\\)\\.searchParams\\.get\\s*\\(\\s*["']shop(?:Domain)?["']`).test(e)||_o(e,t))return!0;for(let i of t.requestContainers)if(new RegExp(`\\b${te(i)}(?:\\?\\.|\\.|\\[\\s*["'])${B}`).test(e))return!0;for(let i of t.formDataContainers)if(new RegExp(`\\b${te(i)}\\.get\\s*\\(\\s*["']${B}["']\\s*\\)`).test(e))return!0;for(let i of t.urlContainers)if(new RegExp(`\\b${te(i)}\\.searchParams\\.get\\s*\\(\\s*["']${B}["']\\s*\\)`).test(e))return!0;return[...t.requestControlled].some(i=>So(e,i))}function _o(e,t){return[...t.requestShopHelpers].some(n=>{let i=`(?:\\(\\s*)?${Ri(n,t.requestPattern)}(?:\\s*\\))?`;return new RegExp(`${i}\\s*(?:\\?\\.|\\.|\\[\\s*["'])${B}`).test(e)})}function So(e,t){let n=new RegExp(`\\b${te(t)}\\b`,"g"),i=n.exec(e);for(;i;){let s=e[i.index-1],r=e.slice(i.index+t.length);if(s!=="."&&s!=='"'&&s!=="'"&&s!=="`"&&!/^\s*:/.test(r))return!0;i=n.exec(e)}return!1}function Ci(e,t){let n=0,i;for(let s=t+1;s<e.length;s++){let r=e[s];if(i){r==="\\"?s++:r===i&&(i=void 0);continue}if(r==='"'||r==="'"||r==="`"){i=r;continue}if(r==="{")n++;else if(r==="}"){if(n===0)return{text:e.slice(t+1,s),end:s+1};n--}}}function Pi(e){return Di(e,"CREDENTIAL_LOG_LEAKAGE",/\b(?:console\.(?:log|info|warn|error|debug|dir)|logger\.(?:log|info|warn|error|debug))\s*\(/g,"Credential reaches a log sink")}function Li(e){let t="Credential reaches the client browser",n=Di(e,"CREDENTIAL_BROWSER_LEAKAGE",/\b(?:json|defer|(?:new\s+)?Response|(?:localStorage|sessionStorage)\.setItem)\s*\(/g,t),i=Eo(e,"CREDENTIAL_BROWSER_LEAKAGE",/(?:\breturn\s+\{|\b(?:window|globalThis|document)(?:\.[\w$]+|\[[^\]]+\])?\s*=|\.(?:innerHTML|outerHTML|textContent|value)\s*=)([^;\n]{0,600})/g,t),s=To(e);return[...new Map([...n,...i,...s].map(r=>[`${r.location.file}:${r.location.line}`,r])).values()]}function Di(e,t,n,i){let s=[];for(let r of e){if(!ne(r))continue;let o=ue(r.content,{preserveTemplateExpressions:!0});n.lastIndex=0;let a=n.exec(o);for(;a;){let c=ft(o,n.lastIndex);c&&mt.test(vt(c.text))&&s.push(yt(t,r,a.index,i)),n.lastIndex=c?.end??n.lastIndex,a=n.exec(o)}}return s}function To(e){let t=[];for(let n of e){if(!ne(n))continue;let i=wt(n.content),s=ue(n.content,{preserveTemplateExpressions:!0}),r=/\bfetch\s*\(/g,o=r.exec(i);for(;o;){let a=ft(i,r.lastIndex),c=ft(s,r.lastIndex);a&&c&&/^\s*["']https?:\/\//.test(a.text)&&mt.test(vt(c.text))&&t.push(yt("CREDENTIAL_BROWSER_LEAKAGE",n,o.index,"Credential reaches the client browser")),r.lastIndex=a?.end??r.lastIndex,o=r.exec(i)}}return t}function Eo(e,t,n,i){let s=[];for(let r of e){if(!ne(r))continue;let o=ue(r.content,{preserveTemplateExpressions:!0});n.lastIndex=0;let a=n.exec(o);for(;a;){let c=vt(a[0]);mt.test(c)&&s.push(yt(t,r,a.index,i)),a=n.exec(o)}}return s}function yt(e,t,n,i){return Re(e,t,n,i,"A credential value flows directly into a disclosure sink.",e==="CREDENTIAL_LOG_LEAKAGE"?"Remove the credential from logs; log only a boolean or a deliberately redacted/hash-derived value.":"Keep credentials server-side and return only non-sensitive derived data to the browser.",-20)}function vt(e){let t="(?:accessToken|access_token|sessionToken|session_token|apiSecret|api_secret|clientSecret|client_secret|SHOPIFY_API_SECRET|SHOPIFY_ACCESS_TOKEN)";return e.replace(new RegExp(`\\b(?:redact|mask|hash|digest)\\s*\\(\\s*${t}\\s*\\)`,"gi"),"").replace(new RegExp(`\\bBoolean\\s*\\(\\s*${t}\\s*\\)|!!\\s*${t}`,"gi"),"").replace(new RegExp(`\\b${t}\\s*(?:===?|!==?)\\s*(?:undefined|null|true|false)|(?:undefined|null)\\s*(?:===?|!==?)\\s*${t}`,"gi"),"").replace(new RegExp(`\\bcreateHash\\s*\\([^)]*\\)\\s*\\.update\\s*\\(\\s*${t}\\s*\\)\\s*\\.digest\\s*\\([^)]*\\)`,"gi"),"").replace(new RegExp(`\\b${t}\\s*:`,"gi"),"")}function bt(e){let t=[],n=[/\beval\s*\(\s*(?:await\s+)?[A-Za-z_$({][^)]*\)/g,/\bnew\s+Function\s*\(\s*(?:await\s+)?[A-Za-z_$({][^)]*\)/g,/\.(?:innerHTML|outerHTML)\s*=\s*(?:await\s+)?[A-Za-z_$({][^;\n]*/g,/\.insertAdjacentHTML\s*\([^,]+,\s*(?:await\s+)?[A-Za-z_$({][^)]*\)/g,/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(?:await\s+)?[A-Za-z_$({][^}]*/g,/(?:createElement\s*\(\s*["']script["']\s*\)[\s\S]{0,300}?\.src|\bscript\.src)\s*=\s*(?:await\s+)?[A-Za-z_$({][^;\n]*/g];for(let i of e){if(!ne(i))continue;let s=ue(i.content,{preserveTemplateExpressions:!0});for(let r of n){r.lastIndex=0;let o=r.exec(s);for(;o;)/\b(?:sanitize|DOMPurify\.sanitize|escapeHtml)\s*\(/.test(o[0])||t.push(Re("UNSAFE_INNERHTML",i,o.index,"Unsafe HTML assignment","A non-literal value is written to an HTML, script, or evaluation sink.","Avoid executable sinks, or apply a context-appropriate allowlist sanitizer immediately before the sink.",-25)),o=r.exec(s)}}return[...new Map(t.map(i=>[`${i.location.file}:${i.location.line}`,i])).values()]}function ne(e){return!!e.content&&bo.has(e.ext)}function Io(e,t){let n=/export\s+(?:async\s+)?(?:function\s+|const\s+)(?:loader|action)\b/g;return n.lastIndex=t,n.exec(e)?.index??e.length}function ft(e,t){let n=0,i;for(let s=t;s<e.length;s++){let r=e[s];if(i){r==="\\"?s++:r===i&&(i=void 0);continue}if(r==='"'||r==="'"||r==="`"){i=r;continue}if(r==="("||r==="["||r==="{")n++;else if(r===")"||r==="]"||r==="}"){if(r===")"&&n===0)return{text:e.slice(t,s),end:s+1};n--}}}function Ao(e,t){let n=0,i;for(let s=t;s<e.length;s++){let r=e[s];if(i){r==="\\"?s++:r===i&&(i=void 0);continue}if(r==='"'||r==="'"||r==="`"){i=r;continue}if(r==="("||r==="["||r==="{")n++;else if(r===")"||r==="]"||r==="}"){if(r===")"&&n===0)return{text:e.slice(t,s).trim(),end:s+1};n--}else if(r===","&&n===0)return{text:e.slice(t,s).trim(),end:s+1}}}function wt(e){let t=[...e],n;for(let i=0;i<t.length;i++){let s=t[i];if(n){s==="\\"?i++:s===n&&(n=void 0);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="/"&&t[i+1]==="/")for(;i<t.length&&t[i]!==`
|
|
2356
|
+
- Don't report things you couldn't confirm \u2014 uncertainty is not a finding.`,Qn=e=>{let t=G.find(n=>n.id===e);if(!t)throw new Error(`Agent check has no catalog entry: ${e}`);return t},Jn=e=>({schema_version:Ye,engine:{name:D,version:e},generated_at:new Date().toISOString(),checks:[...Ze().values()].map(t=>({id:t.id,version:t.version,severity:t.severity,docs_url:Qn(t.id).docsUrl,prompt:t.prompt})),instructions:wr}),_e=4e3,Mn=4e3,Un=8e3,$n=4e3,jn=100,Hn=50,Gn=4e3,ke=1024,zn=1e3,xr=["executed","not_applicable","unresolved"],N=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Xe=e=>Number.isInteger(e)&&e>=1,Wn=e=>!e||e.startsWith("/")||/^[a-zA-Z]:[\\/]/.test(e)?!1:!e.includes("\0"),kr=e=>{if(!N(e))return"finding must be an object";let t=e;if(typeof t.check_id!="string"||!t.check_id)return"missing or invalid check_id";if(!Xe(t.check_version))return"missing or invalid check_version";if(typeof t.file!="string"||!t.file)return"missing or invalid file";if(!Number.isInteger(t.line)||t.line<1)return`invalid line number: ${t.line}`;if(typeof t.message!="string"||!t.message)return"missing or invalid message";if(!Array.isArray(t.evidence)||t.evidence.length===0)return"finding requires at least one evidence citation";if(t.evidence.length>Hn)return`finding exceeds ${Hn} evidence citations`;if(t.file.length>ke)return`file path exceeds ${ke} characters`;if(!Wn(t.file))return`unsafe file path (must be relative): ${t.file}`;if(t.message.length>_e)return`message exceeds ${_e} characters`;if(t.snippet!==void 0&&typeof t.snippet!="string")return"snippet must be a string";if(typeof t.snippet=="string"&&t.snippet.length>Mn)return`snippet exceeds ${Mn} characters`;if(t.reasoning!==void 0&&typeof t.reasoning!="string")return"reasoning must be a string";if(typeof t.reasoning=="string"&&t.reasoning.length>Un)return`reasoning exceeds ${Un} characters`;if(t.confidence!==void 0&&(typeof t.confidence!="string"||!["high","medium","low"].includes(t.confidence)))return"confidence must be high, medium, or low";if(t.suppression!==void 0){let n=t.suppression;if(!N(n)||typeof n.justification!="string"||!n.justification.trim())return"suppression requires a justification";if(n.justification.length>$n)return`suppression justification exceeds ${$n} characters`}for(let n of t.evidence){if(!N(n))return"evidence citation must be an object";if(typeof n.file!="string"||!n.file)return"evidence file must be a non-empty string";if(!Wn(n.file))return`unsafe evidence file path: ${n.file}`;if(n.file.length>ke)return`evidence file path exceeds ${ke} characters`;if(n.line!==void 0&&!Xe(n.line))return`invalid evidence line number: ${n.line}`;if(n.quote!==void 0&&typeof n.quote!="string")return"evidence quote must be a string";if(typeof n.quote=="string"&&n.quote.length>Gn)return`evidence quote exceeds ${Gn} characters`}},_r=(e,t,n)=>{if(!Xe(e.check_version))return"missing or invalid check_version";if(typeof t!="string"||!xr.includes(t))return"status must be executed, not_applicable, or unresolved";let i=e.reason;if(i===void 0)return t==="executed"?void 0:`${t} requires a reason`;if(!N(i)||typeof i.code!="string"||!i.code||typeof i.message!="string"||!i.message.trim())return"reason requires a code and message";if(i.code.length>jn)return`reason code exceeds ${jn} characters`;if(i.message.length>_e)return`reason message exceeds ${_e} characters`;if(t==="not_applicable"&&n.has(e.check_id))return"a not_applicable check can't have findings"};function Sr(e,t,n=Ze()){let i=[],s=[],r=new Set,o=new Set(t.filter(N).map(a=>a.check_id));return e.length>n.size?(i.push(`checks_executed contains ${e.length} entries, exceeding the limit of ${n.size}`),{reports:s,errors:i}):(e.forEach((a,c)=>{let l=`checks_executed[${c}]`;if(!N(a)){i.push(`${l}: executed check must be an object`);return}if(typeof a.check_id!="string"||!a.check_id){i.push(`${l}: missing or invalid check_id`);return}let d=`${l} (${a.check_id})`;if(!n.has(a.check_id)){i.push(`${d}: unknown check_id`);return}if(r.has(a.check_id)){i.push(`${d}: duplicate check_id in checks_executed`);return}r.add(a.check_id);let f=a.status??"executed",h=_r(a,f,o);if(h){i.push(`${d}: ${h}`);return}let g=a.reason;s.push({check_id:a.check_id,check_version:a.check_version,status:f,...g?{reason:{code:y(g.code),message:y(g.message)}}:{}})}),{reports:s,errors:i})}var Vn=e=>y(e.replace(/\\/g,"/")),Tr=e=>({location:{file:Vn(e.file),line:e.line},message:y(e.message),evidence:e.evidence.map(t=>({location:{file:Vn(t.file),...t.line===void 0?{}:{line:t.line}},...t.quote===void 0?{}:{quote:y(t.quote)}})),...e.snippet===void 0?{}:{snippet:y(e.snippet)},...e.confidence===void 0?{}:{confidence:e.confidence},...e.reasoning===void 0?{}:{reasoning:y(e.reasoning)},...e.suppression===void 0?{}:{suppression:{justification:y(e.suppression.justification)}}});function Er(e,t){let n=[],i=new Map;for(let o of t)i.set(o.check_id,[...i.get(o.check_id)??[],o]);let s=new Map(e.map(o=>[o.check_id,o])),r=e.map(o=>({report:o,findings:i.get(o.check_id)??[]}));for(let[o,a]of i){let c=[...new Set(a.map(f=>f.check_version))].sort((f,h)=>f-h),l=s.get(o);if(l){c.some(f=>f!==l.check_version)&&n.push(`${o}: findings claim check_version ${c.join(", ")}, but its checks_executed entry claims ${l.check_version}`);continue}let[d]=c;if(d!==void 0){if(c.length>1){n.push(`${o}: findings claim different check_version values (${c.join(", ")})`);continue}r.push({report:{check_id:o,check_version:d,status:"executed"},findings:a})}}return{groups:r,errors:n}}function Ir(e){let t=Qn(e.id);return{title:t.title,severity:e.severity,description:t.description,...t.guide?{guide:t.guide}:{},docs_url:t.docsUrl,current_version:e.version,precedence:e.precedence}}var Ar=e=>{if(!N(e))return{error:"scope is required and must be an object"};let{include_dirs:t,excludes:n,no_git_ignore:i}=e,s=r=>Array.isArray(r)&&r.every(o=>typeof o=="string");return s(t)?s(n)?typeof i!="boolean"?{error:"scope.no_git_ignore must be a boolean"}:{scope:xe({include_dirs:t,excludes:n,no_git_ignore:i})}:{error:"scope.excludes must be an array of strings"}:{error:"scope.include_dirs must be an array of strings"}},Bn=(e,t,n)=>{let i=e[t];return i===void 0?[]:Array.isArray(i)?i:(n.push(`${t} must be an array`),[])};function Rr(e,t){if(!N(e))return{ok:!1,errors:["The findings document must be a JSON object."]};let n=[];e.schema_version!==de&&n.push(`schema_version must be ${de}`);let i=Ar(e.scope);"error"in i&&n.push(i.error);let s=Bn(e,"checks_executed",n),r=Bn(e,"findings",n),o=Ze(),a=Sr(s,r,o);n.push(...a.errors);let c=[];r.length>zn?n.push(`findings contains ${r.length} findings, exceeding the limit of ${zn}`):r.forEach((f,h)=>{let g=N(f)&&typeof f.check_id=="string"?f.check_id:void 0,x=g?`findings[${h}] (${g})`:`findings[${h}]`,E=kr(f)??(o.has(g??"")?void 0:"unknown check_id");if(E){n.push(`${x}: ${E}`);return}c.push(f)});let l=Er(a.reports,c);if(n.push(...l.errors),n.length>0||"error"in i)return{ok:!1,errors:n.map(y)};let d=l.groups.map(({report:f,findings:h})=>({id:f.check_id,version:f.check_version,status:f.status,...f.reason?{reason:f.reason}:{},snapshot:Ir(o.get(f.check_id)),findings:h.map(Tr)})).sort((f,h)=>f.id.localeCompare(h.id));return{ok:!0,document:{schema_version:C,source:"agent",engine:{name:D,version:t.engineVersion},generated_at:t.generatedAt??new Date().toISOString(),scope:i.scope,checks:d}}}u();function z(){return Wt}u();u();u();u();function F(e){return e instanceof Error&&"code"in e&&(e.code==="ENOENT"||e.code==="ENOTDIR")}function q(e,t){let n=t instanceof Error&&"code"in t&&typeof t.code=="string"?t.code:void 0;return n?`Could not inspect ${e} (${n})`:`Could not inspect ${e}`}u();u();import{lstatSync as Cr}from"node:fs";function Se(e){let t=e;for(;;){try{let i=Cr(b(t,".git"));return i.isDirectory()||i.isFile()?{status:"found",directory:t}:{status:"ambiguous",reason:"Could not determine repository ownership from .git"}}catch(i){if(!F(i))return{status:"ambiguous",reason:q(".git",i)}}let n=S(t);if(n===t)return{status:"none"};t=n}}import{lstatSync as Pr,realpathSync as Lr}from"node:fs";function Kn(e){let t=Lr(L());return{excludePatterns:e.excludePatterns.filter(n=>n!=="").map(n=>Nr(n,t)),gitFiltering:!e.noGitIgnore,workingDirectory:t}}var Dr=/[*?[\]{}()!#\\]/;function Nr(e,t){let n=e.split("/"),i=n.findIndex(c=>Dr.test(c)),s=i===-1?n:n.slice(0,i),r=s.join("/")||(e.startsWith("/")?"/":"");if(r==="")return e;let o=w(t,T(t,r)),a=n.slice(s.length).join("/");return a===""?o:o===""?a:`${o}/${a}`}function et(e,t){return t.status==="listed"?{directory:e,paths:new Set(t.paths)}:void 0}function Yn(e,t,n){return e.gitFiltering&&(k(n.absolutePath)===".git"||t&&qr(t,n))?!0:tt(e,n.absolutePath)}function Xn(e,t,n){let i=n.split("/");return i.some((s,r)=>{let o=b(t,...i.slice(0,r+1));return e.gitFiltering&&s===".git"||tt(e,o)})}function Te(e,t){let n=w(e.workingDirectory,t).split("/");return n.some((i,s)=>tt(e,b(e.workingDirectory,...n.slice(0,s+1))))}function qr(e,t){let n=w(e.directory,t.absolutePath);return e.paths.has(t.isDirectory?`${n}/`:n)}function tt(e,t){if(e.excludePatterns.length===0)return!1;let n=w(e.workingDirectory,t);return e.excludePatterns.some(i=>Xt(n,i))}async function nt(e){let t=await Or(e);return t.status!=="listed"&&sn(`app security check: git ignore listing skipped (${t.status})`),t}async function Or(e){let t=await H(e,["rev-parse","--is-inside-work-tree"]);if(t===void 0)return{status:"failed"};if(t.exitCode!==0)return Se(e).status==="none"?{status:"not-a-repository"}:{status:"failed"};if(t.stdout.trim()==="false")return{status:"not-a-repository"};if(t.stdout.trim()!=="true")return{status:"failed"};let n=await H(e,["ls-files","-z","--others","--ignored","--exclude-standard","--directory","--","."]);return n===void 0||n.exitCode!==0?{status:"failed"}:{status:"listed",paths:ni(n.stdout)}}async function Zn(e){let t=S(e);return t===e?!1:(await H(t,["check-ignore","--no-index","-q","--",k(e)]))?.exitCode===0}async function ei(e){let t=await H(e,["ls-files","-z","--cached","--","."]);if(!(t===void 0||t.exitCode!==0))return ni(t.stdout)}async function ti(e){let t;try{t=Pr(b(e,".git"))}catch(n){return F(n)?void 0:{status:"failed"}}return t.isDirectory()||t.isFile()?nt(e):{status:"failed"}}function ni(e){return e.split("\0").filter(t=>t!=="")}u();var ii=[".github/dependabot.yml",".github/dependabot.yaml","renovate.json","renovate.jsonc","renovate.json5",".github/renovate.json",".github/renovate.jsonc",".github/renovate.json5",".gitlab/renovate.json",".gitlab/renovate.jsonc",".gitlab/renovate.json5",".renovaterc",".renovaterc.json",".renovaterc.jsonc",".renovaterc.json5"];function Fr(e){return Object.keys(e.dependencies).length>0||Object.keys(e.devDependencies??{}).length>0}function Mr(e){return e.replace(/\\/g,"/")}function Ur(e){let t=e.map(i=>Mr(i.path)),n=t.find(i=>i==="package.json");return n||[...t].sort((i,s)=>{let r=i.split("/").length-s.split("/").length;return r===0?i.localeCompare(s):r})[0]}function si(e){let t=e.manifests.filter(Fr);if(t.length===0)return{issues:[]};let{files:n,unresolvedReason:i}=e.dependencyAutomation;return i?{issues:[],unresolvedReason:i}:n.length>0?{issues:[]}:{issues:[{id:"MISSING_DEPENDENCY_SECURITY_AUTOMATION",severity:"low",points:-5,title:"Repository-level dependency management configuration not detected",message:"No recognized Dependabot or Renovate configuration file was found at the repository root. Add dependency update automation there, or verify that an existing integration covers this app. This check only looks for local configuration files; it does not validate their contents or inspect hosted integrations, CI workflows, or execution results.",location:{file:Ur(t)},fix:{automated:!1,description:"Add a Dependabot or Renovate configuration file at the repository root, or verify existing coverage."}}]}}import{lstatSync as W,readdirSync as $r,realpathSync as le}from"node:fs";function oi(e,t){let n=dt(e);if(n===void 0)return null;try{let i=Ge(n);return Gr(i,e,n,t)}catch{return V(e,{ok:!1,reason:"unreadable",detail:"TOML could not be parsed"}),null}}var jr=p.object({api_version:p.string().optional(),privacy_compliance:p.object({customer_deletion_url:p.string().optional(),customer_data_request_url:p.string().optional(),shop_deletion_url:p.string().optional()}).optional(),subscriptions:p.array(p.unknown()).optional()}),Hr=dn.extend({uri:p.preprocess(e=>Ve(e),p.string({invalid_type_error:"Value must be string"}))});function Gr(e,t,n,i){let s=zr(e.access_scopes,t,i),r=Wr(e.auth,t,i),o=Vr(e.webhooks,t,i);return{raw:e,path:t,content:n,scopes:s,apiVersion:o.apiVersion,redirectUrls:r,webhooks:o.subscriptions}}function zr(e,t,n){if(e===void 0)return;let i=hn.safeParse(e);if(!i.success){Ee(n,t,"access_scopes section could not be parsed");return}let s=i.data.scopes?.split(/[\s,]+/).filter(Boolean)??[],r=[...new Set([...s,...i.data.required_scopes??[]])];return r.length>0?r.join(","):void 0}function Wr(e,t,n){if(e===void 0)return[];let i=fn.safeParse(e);return i.success?i.data.redirect_urls:(Ee(n,t,"auth section could not be parsed"),[])}function Vr(e,t,n){if(e===void 0)return{subscriptions:[]};let i=jr.safeParse(e);if(!i.success)return Ee(n,t,"webhooks section could not be parsed"),{subscriptions:[]};let s=(i.data.subscriptions??[]).flatMap(a=>Qr(a,t,n)),r=i.data.privacy_compliance,o=[{topic:"customers/redact",uri:r?.customer_deletion_url},{topic:"customers/data_request",uri:r?.customer_data_request_url},{topic:"shop/redact",uri:r?.shop_deletion_url}].flatMap(({topic:a,uri:c})=>c?[{topics:[a],uri:c}]:[]);return{apiVersion:i.data.api_version,subscriptions:[...s,...o]}}var Br=p.object({uri:p.preprocess(e=>Ve(e),p.string({invalid_type_error:"Value must be string"}))});function Qr(e,t,n){let i=Hr.safeParse(e);if(i.success)return[{topics:[...i.data.topics??[],...i.data.compliance_topics??[]],uri:i.data.uri}];Ee(n,t,"webhook subscription could not be parsed");let s=Br.safeParse(e);return s.success?[{topics:[],uri:s.data.uri}]:[]}function Ee(e,t,n){e&&V(t,{ok:!1,reason:"unreadable",detail:n})}function Jr(e,t){try{return $r(e,{withFileTypes:!0})}catch(n){V(e,{ok:!1,reason:"unreadable",detail:q(t?"scan directory":Ie(e),n)});return}}async function ai({appDirectory:e,scanDirectories:t,requestedScanDirectories:n,selectedAppConfigFilePath:i,rules:s}){let r=[];if(s.gitFiltering)for(let l of n)Te(s,l)||await Zn(l)&&r.push(l);let o=[];for(let l of t)o.push(await Yr(l,s,r.includes(l)));let a=[...o.flatMap(l=>l.absolutePaths),...i?[i]:[]],c=[...new Set(a.map(l=>R(w(e,l))))].sort();return{paths:c,ignoredScanDirectories:r,otherAppDirectories:Kr(e,c),listingStatus:o[0]?.listingStatus??(s.gitFiltering?"tracked-only":"git-ignore-off")}}function Kr(e,t){let n=t.filter(i=>We(k(i))).map(i=>S(i)).filter(i=>i!==".");return[...new Set(n)].map(i=>b(e,i))}async function Yr(e,t,n){if(!t.gitFiltering)return{absolutePaths:await ri(e,t,void 0),listingStatus:"git-ignore-off"};if(n){if(Te(t,e))return{absolutePaths:[],listingStatus:"tracked-only"};let s=await ei(e);if(s===void 0)throw new I(`Couldn't list the files Git tracks in ${w(L(),e)||"."}.`);return{absolutePaths:s.filter(r=>!Xn(t,e,r)).map(r=>b(e,r)).filter(Xr),listingStatus:"tracked-only"}}let i=await nt(e);return{absolutePaths:await ri(e,t,et(e,i)),listingStatus:i.status}}function Xr(e){try{return!W(e).isDirectory()}catch{return!1}}async function ri(e,t,n){if(Te(t,e))return[];let i=[],s=[{directory:e,repository:n}];for(let{directory:r,repository:o}of s){let a=Jr(r,r===e);if(a!==void 0)for(let c of a){let l=b(r,c.name),d=c.isDirectory();if(Yn(t,o,{absolutePath:l,isDirectory:d}))continue;if(!d){i.push(l);continue}let f=t.gitFiltering?await ti(l):void 0;s.push({directory:l,repository:f?et(l,f):o})}}return i}function Zr(e,t){let n=new Map([...t].map(i=>[i,[]]));for(let i of e){if(!ui(i))continue;let s=S(i);for(;s!==".";)n.get(s)?.push(i),s=S(s);n.get(".")?.push(i)}return n}function ci(e,t){let n=t.filter(s=>k(s)==="shopify.extension.toml");if(n.length===0)return[];let i=Zr(t,new Set(n.map(s=>S(s))));return n.flatMap(s=>{let r=b(e,s),o=dt(r);if(o===void 0)return[];try{let c=Ge(o).type,l=lt(e,i.get(S(s))??[]);return[{path:s,type:c,content:o,files:l}]}catch{return V(r,{ok:!1,reason:"unreadable",detail:"TOML could not be parsed"}),[]}})}var eo=5e5,ot=[],it=new Map,st;function at(e){st=e,ot=[],it.clear()}function pe(){if(!st)throw new tn("The repository reader was used before it was configured.");return st}function ct(){return[...ot]}function V(e,t){let n=w(pe().appDirectory,e).replace(/\\/g,"/");ot.push({path:n.length>0?n:e,reason:t.reason,...t.sizeBytes===void 0?{}:{size_bytes:t.sizeBytes},...t.detail?{detail:t.detail}:{}})}function di(e){try{let t=Kt(e);return t>eo?{ok:!1,reason:"too_large",sizeBytes:t}:{ok:!0,content:Qt(e)}}catch(t){return{ok:!1,reason:"unreadable",detail:t instanceof Error?t.message:String(t)}}}function X(e){return{ok:!1,reason:"unreadable",detail:e}}function Ie(e){let t=R(w(pe().appDirectory,e));return t.length===0||t==="."||Vt(t)?"path":t}function li(e,t){let n=T(e),i=T(n,t),s=Ie(i);if(!P(n,i))return{status:"unresolved",reason:`${s} escapes the scan directory`};let r;try{r=le(n)}catch(a){return{status:"unresolved",reason:q("scan directory",a)}}let o=R(w(n,i)).split("/").filter(a=>a.length>0&&a!==".");if(o.includes(".."))return{status:"unresolved",reason:`${s} escapes the scan directory`};if(o.length===0)return{status:"unresolved",reason:`${s} is not a file`};try{let a=le(i);return P(r,a)?W(a).isFile()?{status:"file",path:a}:{status:"unresolved",reason:`${s} is not a file`}:{status:"unresolved",reason:`${s} resolves outside the scan directory`}}catch(a){return F(a)?to(r,o,s):{status:"unresolved",reason:q(s,a)}}}function to(e,t,n){let i=e;for(let[s,r]of t.entries()){i=b(i,r);try{W(i);let o=le(i);if(!P(e,o))return{status:"unresolved",reason:`${n} resolves outside the scan directory`};let a=W(o);if(s===t.length-1){if(!a.isFile())return{status:"unresolved",reason:`${n} is not a file`}}else if(!a.isDirectory())return{status:"unresolved",reason:`${n} contains an entry that is not a directory`};i=o}catch(o){if(F(o)){try{if(W(i).isSymbolicLink())return{status:"unresolved",reason:`${n} contains a dangling symbolic link`}}catch{return{status:"missing"}}return{status:"missing"}}return{status:"unresolved",reason:q(n,o)}}}return{status:"file",path:i}}function no(e){let t=pe().scanDirectories.find(i=>P(i,e));if(t===void 0)return{failure:X(`${Ie(e)} is outside every scan directory`)};let n=li(t,e);return n.status==="file"?{path:n.path}:n.status==="missing"?{failure:X("path does not exist")}:{failure:X(n.reason)}}function io(e){let t=Ie(e);try{let n=le(e);return W(n).isFile()?{path:n}:{failure:X(`${t} is not a file`)}}catch(n){return{failure:X(F(n)?"path does not exist":q(t,n))}}}function Ae(e){let t=T(e),n=it.get(t);if(n)return n;let i=pe().explicitInputs.has(t)?io(t):no(t),s=i.path?di(i.path):i.failure;return it.set(t,s),s.ok||V(t,s),s}function so(e,t){let n=li(e,t);if(n.status==="missing")return;let i=n.status==="file"?di(n.path):X(n.reason);return i.ok||V(b(e,t),i),i}function dt(e){let t=Ae(e);return t.ok?t.content.toString():void 0}var rt={".js":{name:"javascript",supported:!0},".jsx":{name:"javascript",supported:!0},".mjs":{name:"javascript",supported:!0},".cjs":{name:"javascript",supported:!0},".ts":{name:"typescript",supported:!0},".tsx":{name:"typescript",supported:!0},".mts":{name:"typescript",supported:!0},".cts":{name:"typescript",supported:!0},".prisma":{name:"prisma",supported:!0},".liquid":{name:"liquid",supported:!0},".html":{name:"html",supported:!0},".ejs":{name:"ejs",supported:!1},".erb":{name:"erb",supported:!1},".hbs":{name:"handlebars",supported:!1},".jinja2":{name:"jinja2",supported:!1},".twig":{name:"twig",supported:!1},".rb":{name:"ruby",supported:!1},".php":{name:"php",supported:!1},".py":{name:"python",supported:!1},".java":{name:"java",supported:!1},".kt":{name:"kotlin",supported:!1},".kts":{name:"kotlin",supported:!1},".go":{name:"go",supported:!1},".rs":{name:"rust",supported:!1},".cs":{name:"csharp",supported:!1},".swift":{name:"swift",supported:!1},".scala":{name:"scala",supported:!1},".ex":{name:"elixir",supported:!1},".exs":{name:"elixir",supported:!1},".vue":{name:"vue",supported:!1},".svelte":{name:"svelte",supported:!1}};function pi(e){let t=O(e);return t in rt?rt[t]:void 0}function ui(e){return pi(e)?.supported===!0}function hi(e){return e.flatMap(t=>{let n=pi(t);return n?[{path:t,extension:O(t),language:n.name,supported:n.supported}]:[]}).sort((t,n)=>t.path.localeCompare(n.path))}function lt(e,t){return t.filter(ui).map(n=>{let i=b(e,n),s=Ae(i);return{path:n,absolutePath:i,ext:O(n),content:s.ok?s.content.toString():void 0}})}var ro=new Set(["package-lock.json","pnpm-lock.yaml","yarn.lock"]),oo=new Set([...Object.keys(rt),".md",".mdx",".markdown",".yaml",".yml",".json",".jsonc",".toml",".sh",".bash",".zsh",".fish",".properties",".ini",".cfg",".conf",".xml",".graphql",".gql",".sql",".txt",".pem"]),ao=new Set(["Dockerfile","Containerfile","Gemfile","Rakefile","Procfile","Makefile"]);function co(e){if(oo.has(O(e)))return!0;let t=k(e);return t===".env"||t.startsWith(".env.")||ao.has(t)||t.startsWith("Dockerfile.")||t.startsWith("Containerfile.")}function lo(e){let t=e.subarray(0,Math.min(e.length,8e3));if(t.includes(0))return!0;let n=0;for(let i of t)(i<8||i>13&&i<32)&&n++;return t.length>0&&n/t.length>.1}function fi(e,t,n){return t.filter(co).filter(s=>!ro.has(k(s))).filter(s=>!We(k(s))||s===n).flatMap(s=>{let r=b(e,s),o=Ae(r);return o.ok?lo(o.content)?[]:[{path:s,absolutePath:r,ext:O(s),content:o.content.toString()}]:[{path:s,absolutePath:r,ext:O(s),content:void 0}]})}function po(e){let t=Se(e);return t.status==="ambiguous"?{unresolvedReason:t.reason}:{directory:t.status==="found"?t.directory:e}}function mi(e,t){let n;try{if(n=le(T(e)),!W(n).isDirectory())return{files:[],unresolvedReason:"App root is not a directory"}}catch(d){return{files:[],unresolvedReason:q("app root",d)}}let i=po(n);if("unresolvedReason"in i)return{files:[],unresolvedReason:i.unresolvedReason};let s=i.directory,{scanDirectories:r}=pe(),o=d=>s===n||r.some(f=>P(f,d)),a=new Set(t),c=[],l;for(let d of ii){let f=b(s,d),h=R(w(n,f)),g=o(f);if(g&&!a.has(h))continue;let x=g?Ae(f):so(s,d);if(x!==void 0){if(!x.ok){l??=x.reason==="too_large"?`${h} is too large to inspect`:x.detail??`Could not read ${h}`;continue}c.push({path:h,absolutePath:f,ext:O(h),content:x.content.toString()});break}}return c.length>0?{files:c}:{files:c,...l?{unresolvedReason:l}:{}}}function gi(e){return e.filter(t=>k(t)==="package.json")}var uo=p.object({dependencies:p.record(p.string()).optional(),devDependencies:p.record(p.string()).optional()});function yi(e,t){let n=[],i=t.filter(s=>k(s)==="package.json");for(let s of i){let r=b(e,s),o=dt(r);if(o!==void 0)try{let a=uo.parse(JSON.parse(o));n.push({path:s,absolutePath:r,type:"npm",content:o,dependencies:a.dependencies??{},devDependencies:a.devDependencies??{}})}catch{n.push({path:s,absolutePath:r,type:"npm",content:o,dependencies:{},devDependencies:{}}),V(r,{ok:!1,reason:"unreadable",detail:"manifest could not be parsed"})}}return n}u();var vi="@shopify/shopify-app-react-router";function bi(e,t,n,i=e?[e]:[]){let s=t.some(d=>d.type==="theme"),r=t.some(d=>d.type==="theme"&&yo(d)),o=i.some(d=>d.raw.embedded===!0),a=n.some(d=>d.content?/script[_-]?tags?|ScriptTag/i.test(d.content):!1),c=n.some(d=>d.content?/metafields?Set|metafields?\/.*(?:POST|PUT|create|update)|write.*metafield|metafield.*write/i.test(d.content):!1),l=n.some(vo);return{theme_app_extension:s,app_embed:r,embedded_app:o,script_tags:a,webhooks:!!e?.webhooks.length,app_proxy:!!e?.raw?.app_proxy,storefront_metafield_writes:c,has_backend:l,declared_ip_allowlist:!1,checkout_extension:t.some(d=>d.type==="checkout_ui"||d.type==="checkout_ui_extension")}}function wi(e,t,n=[]){let i=e.filter(o=>o.dependencies[vi]!==void 0||o.devDependencies?.[vi]!==void 0);if(i.length===0)return[];let s=t.map(o=>o.path);return[...new Set([".",...i.map(o=>S(o.path))])].filter(o=>fo(o,s)).filter(o=>!mo(o,n)).sort()}function pt(e,t){let n=Z(e,t);return n!==void 0&&/^app\/shopify\.server\.[cm]?[jt]sx?$/.test(n)}function xi(e,t,n){let i=ki(n).filter(r=>!ho(r,t));return(!ut(e)||t.some(r=>Z(r,e)!==void 0))&&!i.some(r=>Z(r,e)!==void 0)}function ho(e,t){return t.some(n=>n==="."?!ut(e):e===n||Z(n,e)!==void 0)}function ut(e){return e===".."||e.startsWith("../")}function Z(e,t){if(e===".")return t;if(!t.startsWith(`${e}/`))return;let n=t.slice(e.length+1);return ut(n)?void 0:n}function fo(e,t){return t.some(n=>Z(e,n)?.startsWith("app/routes/"))&&t.some(n=>pt(e,n))}function mo(e,t){return e==="."?!1:t.includes(e)||ki(t).some(n=>e===n||Z(n,e)!==void 0)}function ki(e){return e.flatMap(t=>go(t)?[`${t}/app`,`${t}/web`]:[t])}function go(e){return e.split("/").every(t=>t==="..")}function _i(e,t,n){let i=n.length>0,s=e.filter(h=>h.type==="theme"),r=s.length>0,o=new Set(s.flatMap(h=>h.files.map(g=>g.path))),a=t.length>0,c;i&&r?c="mixed":i?c="react_router":r&&t.some(h=>!o.has(h.path))?c="mixed":r?c="theme_app_extension":a?c="unknown":c="config_only";let l;i?l="react_router":c==="config_only"||c==="theme_app_extension"?l="none":c==="mixed"?l="mixed":l="unknown";let d=new Map;for(let h of t){let g=d.get(h.language)??{supported:h.supported,files:[]};g.supported&&=h.supported,g.files.push(h.path),d.set(h.language,g)}let f=[...d.entries()].map(([h,g])=>({name:h,support:g.supported?"supported":"unsupported",files:g.files.sort()})).sort((h,g)=>h.name.localeCompare(g.name));return{framework:l,surface:c,languages:f}}function yo(e){return e.files.some(t=>t.ext===".liquid"&&t.content?.includes('"target"')&&t.content?.includes("body"))}function vo(e){let t=e.content;return t?!!(/\b(?:app|router)\.(get|post|put|delete|patch)\s*\(/.test(t)||/export\s+(?:async\s+)?(?:function|const)\s+(?:loader|action)\b/.test(t)):!1}u();function Si(e,t,n,i,s,r){return{timestamp:new Date().toISOString(),security_version:z(),files_scanned:e,rules_run:t,rules_skipped:n,files_skipped_count:i.length,...i.length>0?{files_skipped:i}:{},coverage_gaps:r,checks_executed:s}}u();function bo(e){return e.split(/[\s,]+/).map(t=>t.trim()).filter(Boolean)}var ht={id:"DEPRECATED_SCRIPT_TAG_SCOPE",title:"Deprecated ScriptTag capability",severity:"medium",points:-10,check(e){return e.appTomls.flatMap(t=>bo(t.scopes??"").filter(i=>i==="write_script_tags"||i==="read_script_tags").map(i=>({id:this.id,severity:"medium",points:-10,title:this.title,message:`Scope "${i}" is deprecated. Apps should use app embeds instead of the ScriptTag API.`,location:{file:w(e.appRoot,t.path).replace(/\\/g,"/")},fix:{automated:!1,description:`Remove "${i}" from scopes and migrate to app embeds`,guide:"https://shopify.dev/docs/apps/online-store/app-embeds"}})))}},Ti={id:"INSECURE_WEBHOOK_URL",title:"Configured callback URL is not HTTPS",severity:"high",points:-12,check(e){let t=[];for(let n of e.appTomls){let i=[...n.webhooks.map(s=>({kind:"Webhook URI",value:s.uri})),...n.redirectUrls.map(s=>({kind:"OAuth redirect URI",value:s}))];for(let{kind:s,value:r}of i)wo(r,s==="Webhook URI")||t.push({id:this.id,severity:this.severity,points:this.points,title:this.title,message:`${s} is not a conservative secure URL. Use a Shopify-relative path or an HTTPS URL with an exact host and path.`,location:{file:w(e.appRoot,n.path).replace(/\\/g,"/")},fix:{automated:!1,description:"Use an HTTPS URL with an exact host and path, or a Shopify-relative path where supported.",guide:s==="Webhook URI"?"https://shopify.dev/docs/apps/webhooks":"https://shopify.dev/docs/apps/build/authentication-authorization"}})}return t}};function wo(e,t){if(/^\/(?!\/)[^\s*]*$/.test(e)||t&&/^(?:pubsub|eventbridge):\/\/[^\s*]+$/i.test(e)||t&&/^arn:aws:events:[a-z0-9-]+:\d*:event-source\/[^\s*]+$/i.test(e))return!0;try{let n=new URL(e);return n.protocol!=="https:"||!n.hostname||n.hostname.includes("*")||n.username||n.password||n.hash?!1:n.pathname.startsWith("/")&&!n.pathname.includes("*")}catch{return!1}}u();var xo=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"]),ee="[A-Za-z_$][\\w$]*",B="shop(?:Domain)?",mt=/\b(?:accessToken|access_token|sessionToken|session_token|apiSecret|api_secret|clientSecret|client_secret|SHOPIFY_API_SECRET|SHOPIFY_ACCESS_TOKEN)\b/i;function Ii(e){let t=[],n=new Set;for(let i of e){if(!ne(i)||!i.path.includes("/routes/")||/auth[._/-]?(?:login|callback)/i.test(i.path))continue;let s=ue(i.content),r=/export\s+(?:async\s+)?(?:function\s+|const\s+)(loader|action)\b/g,o=r.exec(s);for(;o;){let a=s.slice(o.index,Ro(s,r.lastIndex)),c=/\bawait\s+authenticate\.(?:admin|public\.[A-Za-z_$][\w$]*|webhook)\s*\(/.exec(a),l=/\b(?:admin\.graphql|prisma\.|db\.|session\.|metafields?Set|unauthenticated\.admin)\b/.test(a);/\bcontext\s*\.\s*shopify\s*\.\s*authenticate\s*\.\s*admin\s*\(/.test(a)?n.add(i.path):!c&&l&&t.push(Re("UNAUTHENTICATED_ENDPOINT",i,o.index,"Route handler lacks recognized auth verification","The React Router loader/action doesn't have a recognized awaited Shopify authentication barrier.","Call and await authenticate.admin(request) (or the applicable Shopify authenticator) before protected access.",-15)),o=r.exec(s)}}return{issues:t,...n.size>0?{unresolvedReason:`Context-based route authentication needs agent review in ${[...n].join(", ")}. A context.shopify.authenticate.admin call is neither proof of verification nor a missing-auth finding.`,unresolvedReasonCode:"agent_investigation_required"}:{}}}function Ai(e){let t=[];for(let n of e){if(!ne(n))continue;let i=Po(wt(n.content)),s=Ri(i,["request","req"],ko(i)),r=/\bunauthenticated\.admin\s*\(/g,o=r.exec(i);for(;o;){let a=Co(i,r.lastIndex);a&>(a.text,s)&&t.push(Re("REQUEST_CONTROLLED_ADMIN_CONTEXT",n,o.index,"Request input selects Admin API shop context","A request-derived shop value reaches unauthenticated.admin(...). Authentication elsewhere in the route does not prove that this independently supplied shop belongs to that session.","Use the Admin API context returned by authenticate.admin(request), or derive the shop directly from its verified session.",-20)),r.lastIndex=a?.end??r.lastIndex,o=r.exec(i)}}return t}function Ri(e,t,n=new Set){let i=t.map(te).join("|"),s=new Set,r=new Set,o=new Set,a=new Set,c=new RegExp(`(?:const|let|var)\\s+(${ee})\\s*=\\s*([\\s\\S]{1,500}?)(?:;|\\n(?=\\s*(?:const|let|var|return|await|})))`,"g"),l=c.exec(e);for(;l;){let g=l[1],x=l[2].trim();(new RegExp(`^(?:await\\s+)?(?:${i})\\.json\\s*\\(\\s*\\)$`).test(x)||new RegExp(`^(?:${i})\\.(?:body|query|params)$`).test(x)||Ei(x,i,n))&&r.add(g),new RegExp(`^(?:await\\s+)?(?:${i})\\.formData\\s*\\(\\s*\\)$`).test(x)&&o.add(g),new RegExp(`^new\\s+URL\\s*\\(\\s*(?:${i})\\.url\\s*\\)$`).test(x)&&a.add(g),l=c.exec(e)}let d={requestPattern:i,requestControlled:s,requestContainers:r,formDataContainers:o,urlContainers:a,requestShopHelpers:n},f=/(?:const|let|var)\s*\{([\s\S]{1,300}?)\}\s*=\s*([\s\S]{1,300}?)(?:;|\n)/g,h=f.exec(e);for(;h;){let g=h[2].trim();if(new RegExp(`^(?:await\\s+)?(?:${i})\\.json\\s*\\(\\s*\\)$`).test(g)||new RegExp(`^(?:${i})\\.(?:body|query|params)$`).test(g)||r.has(g)||Ei(g,i,n))for(let E of h[1].split(",")){let ye=new RegExp(`^\\s*${B}(?:\\s*:\\s*(${ee}))?\\s*$`).exec(E);ye&&s.add(ye[1]??E.trim())}h=f.exec(e)}for(let g=0;g<5;g++){let x=!1;for(c.lastIndex=0,l=c.exec(e);l;){let E=l[1];!s.has(E)&>(l[2].trim(),d)&&(s.add(E),x=!0),l=c.exec(e)}if(!x)break}return d}function ko(e){let t=new Set,n="(?:\\s*:\\s*[^,)]+)?",i="(?:\\s*:\\s*(?:[^{}]|\\{[^{}]*\\})*)?",s=[new RegExp(`\\bfunction\\s+(${ee})\\s*\\(\\s*(${ee})${n}[^)]*\\)${i}\\s*\\{`,"g"),new RegExp(`\\b(?:const|let|var)\\s+(${ee})\\s*=\\s*(?:async\\s+)?\\(\\s*(${ee})${n}[^)]*\\)${i}\\s*=>\\s*\\{`,"g")];for(let r of s){let o=r.exec(e);for(;o;){let a=Pi(e,r.lastIndex-1);a&&_o(a.text,o[2])&&t.add(o[1]),r.lastIndex=a?.end??r.lastIndex,o=r.exec(e)}}return t}function _o(e,t){let n=Ri(e,[t]);return So(e).some(i=>gt(i,n))}function So(e){let t=[],n=/\breturn\s*\{/g,i=n.exec(e);for(;i;){let s=Pi(e,n.lastIndex-1);if(!s)break;let r=new RegExp(`(?:^|,)\\s*(${B})(?:\\s*:\\s*([^,}]+))?\\s*(?=,|$)`,"g"),o=r.exec(s.text);for(;o;)t.push((o[2]??o[1]).trim()),o=r.exec(s.text);n.lastIndex=s.end,i=n.exec(e)}return t}function Ei(e,t,n){return[...n].some(i=>new RegExp(`^${Ci(i,t)}$`).test(e))}function Ci(e,t){return`(?:await\\s+)?${te(e)}\\s*\\(\\s*(?:${t})(?:\\s*,[\\s\\S]*?)?\\s*\\)`}function gt(e,t){if(new RegExp(`(?:${t.requestPattern})\\.(?:body|query|params)(?:\\?\\.|\\.|\\[\\s*["'])${B}`).test(e)||/(?:searchParams|formData)\.get\s*\(\s*["']shop(?:Domain)?["']\s*\)/.test(e)||new RegExp(`new\\s+URL\\s*\\(\\s*(?:${t.requestPattern})\\.url\\s*\\)\\.searchParams\\.get\\s*\\(\\s*["']shop(?:Domain)?["']`).test(e)||To(e,t))return!0;for(let i of t.requestContainers)if(new RegExp(`\\b${te(i)}(?:\\?\\.|\\.|\\[\\s*["'])${B}`).test(e))return!0;for(let i of t.formDataContainers)if(new RegExp(`\\b${te(i)}\\.get\\s*\\(\\s*["']${B}["']\\s*\\)`).test(e))return!0;for(let i of t.urlContainers)if(new RegExp(`\\b${te(i)}\\.searchParams\\.get\\s*\\(\\s*["']${B}["']\\s*\\)`).test(e))return!0;return[...t.requestControlled].some(i=>Eo(e,i))}function To(e,t){return[...t.requestShopHelpers].some(n=>{let i=`(?:\\(\\s*)?${Ci(n,t.requestPattern)}(?:\\s*\\))?`;return new RegExp(`${i}\\s*(?:\\?\\.|\\.|\\[\\s*["'])${B}`).test(e)})}function Eo(e,t){let n=new RegExp(`\\b${te(t)}\\b`,"g"),i=n.exec(e);for(;i;){let s=e[i.index-1],r=e.slice(i.index+t.length);if(s!=="."&&s!=='"'&&s!=="'"&&s!=="`"&&!/^\s*:/.test(r))return!0;i=n.exec(e)}return!1}function Pi(e,t){let n=0,i;for(let s=t+1;s<e.length;s++){let r=e[s];if(i){r==="\\"?s++:r===i&&(i=void 0);continue}if(r==='"'||r==="'"||r==="`"){i=r;continue}if(r==="{")n++;else if(r==="}"){if(n===0)return{text:e.slice(t+1,s),end:s+1};n--}}}function Li(e){return Ni(e,"CREDENTIAL_LOG_LEAKAGE",/\b(?:console\.(?:log|info|warn|error|debug|dir)|logger\.(?:log|info|warn|error|debug))\s*\(/g,"Credential reaches a log sink")}function Di(e){let t="Credential reaches the client browser",n=Ni(e,"CREDENTIAL_BROWSER_LEAKAGE",/\b(?:json|defer|(?:new\s+)?Response|(?:localStorage|sessionStorage)\.setItem)\s*\(/g,t),i=Ao(e,"CREDENTIAL_BROWSER_LEAKAGE",/(?:\breturn\s+\{|\b(?:window|globalThis|document)(?:\.[\w$]+|\[[^\]]+\])?\s*=|\.(?:innerHTML|outerHTML|textContent|value)\s*=)([^;\n]{0,600})/g,t),s=Io(e);return[...new Map([...n,...i,...s].map(r=>[`${r.location.file}:${r.location.line}`,r])).values()]}function Ni(e,t,n,i){let s=[];for(let r of e){if(!ne(r))continue;let o=ue(r.content,{preserveTemplateExpressions:!0});n.lastIndex=0;let a=n.exec(o);for(;a;){let c=ft(o,n.lastIndex);c&&mt.test(vt(c.text))&&s.push(yt(t,r,a.index,i)),n.lastIndex=c?.end??n.lastIndex,a=n.exec(o)}}return s}function Io(e){let t=[];for(let n of e){if(!ne(n))continue;let i=wt(n.content),s=ue(n.content,{preserveTemplateExpressions:!0}),r=/\bfetch\s*\(/g,o=r.exec(i);for(;o;){let a=ft(i,r.lastIndex),c=ft(s,r.lastIndex);a&&c&&/^\s*["']https?:\/\//.test(a.text)&&mt.test(vt(c.text))&&t.push(yt("CREDENTIAL_BROWSER_LEAKAGE",n,o.index,"Credential reaches the client browser")),r.lastIndex=a?.end??r.lastIndex,o=r.exec(i)}}return t}function Ao(e,t,n,i){let s=[];for(let r of e){if(!ne(r))continue;let o=ue(r.content,{preserveTemplateExpressions:!0});n.lastIndex=0;let a=n.exec(o);for(;a;){let c=vt(a[0]);mt.test(c)&&s.push(yt(t,r,a.index,i)),a=n.exec(o)}}return s}function yt(e,t,n,i){return Re(e,t,n,i,"A credential value flows directly into a disclosure sink.",e==="CREDENTIAL_LOG_LEAKAGE"?"Remove the credential from logs; log only a boolean or a deliberately redacted/hash-derived value.":"Keep credentials server-side and return only non-sensitive derived data to the browser.",-20)}function vt(e){let t="(?:accessToken|access_token|sessionToken|session_token|apiSecret|api_secret|clientSecret|client_secret|SHOPIFY_API_SECRET|SHOPIFY_ACCESS_TOKEN)";return e.replace(new RegExp(`\\b(?:redact|mask|hash|digest)\\s*\\(\\s*${t}\\s*\\)`,"gi"),"").replace(new RegExp(`\\bBoolean\\s*\\(\\s*${t}\\s*\\)|!!\\s*${t}`,"gi"),"").replace(new RegExp(`\\b${t}\\s*(?:===?|!==?)\\s*(?:undefined|null|true|false)|(?:undefined|null)\\s*(?:===?|!==?)\\s*${t}`,"gi"),"").replace(new RegExp(`\\bcreateHash\\s*\\([^)]*\\)\\s*\\.update\\s*\\(\\s*${t}\\s*\\)\\s*\\.digest\\s*\\([^)]*\\)`,"gi"),"").replace(new RegExp(`\\b${t}\\s*:`,"gi"),"")}function bt(e){let t=[],n=[/\beval\s*\(\s*(?:await\s+)?[A-Za-z_$({][^)]*\)/g,/\bnew\s+Function\s*\(\s*(?:await\s+)?[A-Za-z_$({][^)]*\)/g,/\.(?:innerHTML|outerHTML)\s*=\s*(?:await\s+)?[A-Za-z_$({][^;\n]*/g,/\.insertAdjacentHTML\s*\([^,]+,\s*(?:await\s+)?[A-Za-z_$({][^)]*\)/g,/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(?:await\s+)?[A-Za-z_$({][^}]*/g,/(?:createElement\s*\(\s*["']script["']\s*\)[\s\S]{0,300}?\.src|\bscript\.src)\s*=\s*(?:await\s+)?[A-Za-z_$({][^;\n]*/g];for(let i of e){if(!ne(i))continue;let s=ue(i.content,{preserveTemplateExpressions:!0});for(let r of n){r.lastIndex=0;let o=r.exec(s);for(;o;)/\b(?:sanitize|DOMPurify\.sanitize|escapeHtml)\s*\(/.test(o[0])||t.push(Re("UNSAFE_INNERHTML",i,o.index,"Unsafe HTML assignment","A non-literal value is written to an HTML, script, or evaluation sink.","Avoid executable sinks, or apply a context-appropriate allowlist sanitizer immediately before the sink.",-25)),o=r.exec(s)}}return[...new Map(t.map(i=>[`${i.location.file}:${i.location.line}`,i])).values()]}function ne(e){return!!e.content&&xo.has(e.ext)}function Ro(e,t){let n=/export\s+(?:async\s+)?(?:function\s+|const\s+)(?:loader|action)\b/g;return n.lastIndex=t,n.exec(e)?.index??e.length}function ft(e,t){let n=0,i;for(let s=t;s<e.length;s++){let r=e[s];if(i){r==="\\"?s++:r===i&&(i=void 0);continue}if(r==='"'||r==="'"||r==="`"){i=r;continue}if(r==="("||r==="["||r==="{")n++;else if(r===")"||r==="]"||r==="}"){if(r===")"&&n===0)return{text:e.slice(t,s),end:s+1};n--}}}function Co(e,t){let n=0,i;for(let s=t;s<e.length;s++){let r=e[s];if(i){r==="\\"?s++:r===i&&(i=void 0);continue}if(r==='"'||r==="'"||r==="`"){i=r;continue}if(r==="("||r==="["||r==="{")n++;else if(r===")"||r==="]"||r==="}"){if(r===")"&&n===0)return{text:e.slice(t,s).trim(),end:s+1};n--}else if(r===","&&n===0)return{text:e.slice(t,s).trim(),end:s+1}}}function wt(e){let t=[...e],n;for(let i=0;i<t.length;i++){let s=t[i];if(n){s==="\\"?i++:s===n&&(n=void 0);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="/"&&t[i+1]==="/")for(;i<t.length&&t[i]!==`
|
|
2357
2357
|
`;)t[i]=" ",i++;else if(s==="/"&&t[i+1]==="*"){for(t[i]=" ",t[i+1]=" ",i+=2;i<t.length&&!(t[i]==="*"&&t[i+1]==="/");)t[i]!==`
|
|
2358
|
-
`&&(t[i]=" "),i++;i<t.length&&(t[i]=" ",t[i+1]=" ",i++)}}return t.join("")}function
|
|
2358
|
+
`&&(t[i]=" "),i++;i<t.length&&(t[i]=" ",t[i+1]=" ",i++)}}return t.join("")}function Po(e){return e.replace(/(["'])(?:\\.|(?!\1)[^\\\n])*\1|`(?:\\.|[^`\\])*`/g,t=>/^["']shop(?:Domain)?["']$/.test(t)?t:t.replace(/[^\n]/g," "))}function ue(e,t={}){return wt(e).replace(/(["'])(?:\\.|(?!\1)[^\\\n])*\1|`(?:\\.|[^`\\])*`/g,n=>{if(!t.preserveTemplateExpressions||!n.startsWith("`"))return n.replace(/[^\n]/g," ");let i=[...n],s=i.map(o=>o===`
|
|
2359
2359
|
`?`
|
|
2360
2360
|
`:" "),r=0;for(let o=0;o<i.length;o++){if(r===0&&i[o]==="$"&&i[o+1]==="{"){r=1,o++;continue}if(r!==0){if(i[o]==="{")r++;else if(i[o]==="}"){r--;continue}s[o]=i[o]}}return s.join("")})}function te(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Re(e,t,n,i,s,r,o){return{id:e,severity:"high",points:o,title:i,message:s,location:{file:t.path,line:t.content.slice(0,n).split(`
|
|
2361
|
-
`).length},fix:{automated:!1,description:r}}}u();var he=zt(
|
|
2361
|
+
`).length},fix:{automated:!1,description:r}}}u();var he=zt(Hs(),1);function xt(e){let t=[],n=[];for(let i of e){if(!i.content||![".liquid",".html"].includes(i.ext))continue;let s;try{s=(0,he.toLiquidHTMLAST)(i.content)}catch{n.push(i.path);continue}if(s instanceof Error){n.push(i.path);continue}t.push(...(0,he.visit)(s,Lo(i))),t.push(...(0,he.visit)(s,Do(i)))}return{issues:t,parserFailures:n}}function Lo(e){return{LiquidVariableOutput(t,n){let i=Fi(t);if(!/(?:\bmetafields?\b|\b(?:block|section)\.settings\b)/i.test(i))return;let s=Mi(t),r=qi(n);if(!No(s,r))return[Ui("LIQUID_UNSAFE_RENDER",e,t.position.start,"Unsafe Liquid metafield or setting output","A metafield or merchant-configurable setting is emitted without context-appropriate escaping or serialization. Liquid output is not automatically HTML-escaped.",qo(r),"medium",-10,r)]}}}function Do(e){return{LiquidVariableOutput(t,n){let i=qi(n);if(!(i!=="javascript"&&i!=="executable_attribute")&&!(i==="javascript"&&Mi(t).includes("json"))&&!Oo(t,n))return[Ui("UNSAFE_INNERHTML",e,t.position.start,"Unsafe HTML assignment",i==="javascript"?"Dynamic Liquid output is embedded in JavaScript without JSON serialization.":"Dynamic Liquid output is embedded in an event handler, srcdoc, or script src attribute. HTML escaping alone does not make executable attributes safe.",i==="javascript"?"Serialize the value with the json filter and consume it only as JavaScript data.":"Keep dynamic values out of executable attributes; use a fixed, versioned script asset and event listeners.","high",-25,i)]}}}function No(e,t){return t==="javascript"?e.includes("json"):t==="html_attribute"?e.some(n=>["escape","escape_once"].includes(n)):t==="executable_attribute"?!1:e.some(n=>["escape","escape_once","metafield_tag"].includes(n))}function qo(e){return e==="javascript"?"Serialize JavaScript data with json and consume it as data.":e==="executable_attribute"?"Don't place merchant-controlled Liquid output in event handlers, srcdoc, or script src attributes.":"Use escape/escape_once for HTML text or attributes, or metafield_tag for supported rich content in HTML text."}function qi(e){let t=e.find(s=>s.type==="HtmlRawNode"&&s.name==="script"),n=e.find(s=>s.type.startsWith("Attr"));if(!n)return t?"javascript":"html_text";let i=Oi(n);return/^on/i.test(i)||i.toLowerCase()==="srcdoc"||i.toLowerCase()==="src"&&t?"executable_attribute":"html_attribute"}function Oi(e){return!("name"in e)||!Array.isArray(e.name)?"":e.name.map(t=>"value"in t&&typeof t.value=="string"?t.value:"").join("")}function Oo(e,t){let n=t.find(s=>s.type==="HtmlRawNode"&&s.name==="script"),i=t.find(s=>s.type.startsWith("Attr"));return!n||!i||Oi(i).toLowerCase()!=="src"?!1:/^(['"])(?:\\.|(?!\1)[^\\\n])+\1\s*\|\s*asset_url\s*$/.test(Fi(e).trim())}function Fi(e){return typeof e.markup=="string"?e.markup:e.markup.rawSource}function Mi(e){return typeof e.markup=="string"?[]:e.markup.filters.map(t=>t.name)}function Ui(e,t,n,i,s,r,o,a,c){let l=t.content.lastIndexOf(`
|
|
2362
2362
|
`,Math.max(0,n-1))+1;return{id:e,pattern_id:`liquid-${c}`,severity:o,points:a,title:i,message:s,location:{file:t.path,line:t.content.slice(0,n).split(`
|
|
2363
2363
|
`).length,column:n-l+1},snippet:t.content.slice(n,t.content.indexOf(`
|
|
2364
2364
|
`,n)===-1?void 0:t.content.indexOf(`
|
|
2365
|
-
`,n)).trim(),fix:{automated:!1,description:r}}}u();var
|
|
2366
|
-
`).length}u();var
|
|
2367
|
-
`).length}u();var
|
|
2368
|
-
`).length},fix:{automated:!1,description:"Return inert JSON, or render only trusted templates with context-appropriate escaping.",guide:"https://shopify.dev/docs/apps/online-store/app-proxies"}})}return t}function
|
|
2369
|
-
`&&!
|
|
2370
|
-
`&&(e[i]=" ")}}function
|
|
2371
|
-
`&&(e[i]=" ")}function
|
|
2365
|
+
`,n)).trim(),fix:{automated:!1,description:r}}}u();var Fo=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"]),Mo=[/\bscriptTagCreate\b|\bscriptTagUpdate\b/,/\bnew\s+[\w.]*\bScriptTag\s*\(/,/\b(?:post|put)\b[^\n]{0,120}script_tags/i];function $i(e){let t=[];for(let n of e){if(!n.content||!Fo.has(n.ext))continue;let i=Uo(n.content);for(let s of Mo){let r=s.exec(i);if(r){t.push({id:"DEPRECATED_SCRIPT_TAG_SCOPE",severity:"medium",points:-10,title:"Deprecated ScriptTag capability",message:"This code uses the deprecated ScriptTag API. Remotely hosted storefront JavaScript can be silently replaced outside extension review and versioning.",location:{file:n.path,line:$o(i,r.index)},fix:{automated:!1,description:"Migrate storefront UI to a theme app extension or analytics to a web pixel.",guide:"https://shopify.dev/docs/apps/build/online-store/theme-app-extensions"}});break}}}return t}function Uo(e){return e.replace(/\/\*[\s\S]*?\*\//g,t=>t.replace(/[^\n]/g," ")).replace(/\/\/[^\n]*/g,"")}function $o(e,t){return e.slice(0,t).split(`
|
|
2366
|
+
`).length}u();var jo=["shop/redact","customers/data_request","customers/redact"],Ho=new Map([["January","01"],["April","04"],["July","07"],["October","10"]]),ji={id:"MISSING_COMPLIANCE_WEBHOOKS",title:"Missing mandatory GDPR compliance webhooks",severity:"medium",points:-10,check(e){return e.appTomls.flatMap(t=>{let n=new Set(t.webhooks.flatMap(s=>s.topics)),i=jo.filter(s=>!n.has(s));return i.length===0?[]:[{id:this.id,severity:this.severity,points:this.points,title:this.title,message:`Missing mandatory compliance webhooks: ${i.join(", ")}.`,location:{file:w(e.appRoot,t.path).replace(/\\/g,"/")},fix:{automated:!1,description:"Add subscriptions for all three mandatory compliance topics.",guide:"https://shopify.dev/docs/apps/webhooks/configuration/mandatory-webhooks"}}]})}};function Hi(e,t,n=30){let i=/^(\d{4})-(01|04|07|10)$/.exec(e);if(!i)return!1;let s=Date.UTC(Number(i[1]),Number(i[2])-1,1),o=Date.UTC(Number(i[1])+1,Number(i[2])-1,1)+n*24*60*60*1e3;return s<=t.getTime()&&t.getTime()>=o}function Gi(e,t=new Date){let n=e.appTomls.flatMap(s=>!s.apiVersion||!Hi(s.apiVersion,t)?[]:[zi(w(e.appRoot,s.path).replace(/\\/g,"/"),s.apiVersion)]);if(e.detection.framework!=="react_router")return n;let i=e.sourceFiles.flatMap(s=>Go(s,e.reactRouterRoots,t));return[...n,...i]}function Go(e,t,n){if(!e.content||!t.some(r=>pt(r,e.path)))return[];let i=Vo(Wo(e.content));return[...i.matchAll(/\bapiVersion\s*:\s*["'](\d{4}-(?:01|04|07|10))["']/g),...i.matchAll(/\bapiVersion\s*:\s*ApiVersion\.(January|April|July|October)(\d{2}|\d{4})\b/g)].flatMap(r=>{let o=r[1]?.includes("-")?r[1]:zo(r[1],r[2]);return!o||!Hi(o,n)?[]:[zi(e.path,o,Bo(e.content,r.index??0))]})}function zo(e,t){let n=Ho.get(e);return n?`${t.length===2?`20${t}`:t}-${n}`:void 0}function zi(e,t,n){return{id:"EOL_API_VERSION",severity:"low",points:-5,title:"End-of-life API version",message:`API version ${t} is past its 12-month support window and 30-day extension grace period.`,location:{file:e,...n===void 0?{}:{line:n}},fix:{automated:!1,description:"Select a currently supported Shopify API version.",guide:"https://shopify.dev/docs/api/usage/versioning"}}}function Wo(e){return e.replace(/\/\*[\s\S]*?\*\//g,t=>t.replace(/[^\n]/g," ")).replace(/\/\/[^\n]*/g,"")}function Vo(e){return e.replace(/(["'])(?:\\.|(?!\1)[^\\\n])*\1|`(?:\\.|[^`\\])*`/g,t=>/^["']\d{4}-(?:01|04|07|10)["']$/.test(t)?t:t.replace(/[^\n]/g," "))}function Bo(e,t){return e.slice(0,t).split(`
|
|
2367
|
+
`).length}u();var Qo=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"]),Jo=/\btext\/html\b/i,Ko=/\b(?:application\/liquid|text\/liquid|application\/x-liquid)\b/i,Bi=/(?:req|request)\.(?:query|params|body)|(?:searchParams|formData)\.get\s*\(|await\s+(?:req|request)\.(?:text|json|formData)\s*\(/,kt="[A-Za-z_$][\\w$]*";function Qi(e){let t=[];for(let n of e){if(!n.content||!Qo.has(n.ext))continue;let i=ga(n.content),s=Yo(i),r=Zo(i),o=ta(i).find(a=>ra(a,s,r));o&&t.push({id:"APP_PROXY_LIQUID_INJECTION",severity:"high",points:-20,title:"App proxy Liquid injection risk",message:"Request-controlled data flows into an active Liquid or HTML app-proxy response.",location:{file:n.path,line:i.slice(0,o.index).split(`
|
|
2368
|
+
`).length},fix:{automated:!1,description:"Return inert JSON, or render only trusted templates with context-appropriate escaping.",guide:"https://shopify.dev/docs/apps/online-store/app-proxies"}})}return t}function Yo(e){let t=new Set,n=Xo(It(e));for(let i=0;i<5;i++){let s=!1;for(let r of n)t.has(r.name)||(Bi.test(r.expression)||[...t].some(o=>Xi(r.expression,o)))&&(t.add(r.name),s=!0);if(!s)break}return t}function Xo(e){let t=[],n=new RegExp(`\\b(?:const|let|var)\\s+(${kt})\\s*=\\s*`,"g"),i=n.exec(e);for(;i;){let o=Vi(e,n.lastIndex);o&&!/^(?:async\s*)?\([^)]*\)\s*=>/.test(o.text)&&!/^function\b/.test(o.text)&&t.push({name:i[1],expression:o.text}),i=n.exec(e)}let s=new RegExp(`(?:^|[;{}\\n])\\s*(${kt})\\s*=\\s*`,"g"),r=s.exec(e);for(;r;){let o=Vi(e,s.lastIndex);o&&!/^(?:async\s*)?\([^)]*\)\s*=>/.test(o.text)&&!/^function\b/.test(o.text)&&t.push({name:r[1],expression:o.text}),r=s.exec(e)}return t}function Zo(e){let t=new Set,n=/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+["']escape-html["']/g,i=n.exec(e);for(;i;){let s=i[1];ea(e,s)||t.add(s),i=n.exec(e)}return t}function ea(e,t){if(new RegExp(`\\b(?:function|const|let|var|class)\\s+${ie(t)}\\b`).test(e))return!0;let n=[/\bfunction(?:\s+[A-Za-z_$][\w$]*)?\s*\(([^)]*)\)/g,/\(([^)]*)\)\s*(?::[^=]+)?=>/g,/(?:^|[,{;]\s*)(?:async\s+)?(?:get\s+|set\s+)?[A-Za-z_$][\w$]*\s*\(([^)]*)\)\s*(?::[^={]+)?\s*\{/gm,/\bcatch\s*\(([^)]*)\)/g],i=new RegExp(`(?:^|[,\\s{])${ie(t)}(?=\\s*(?:[,}:=]|$))`);for(let s of n){let r=s.exec(e);for(;r;){if(i.test(r[1]??""))return!0;r=s.exec(e)}}return new RegExp(`\\b${ie(t)}\\s*(?::[^=,)]*)?\\s*=>`).test(e)}function ta(e){let t=[],n=It(e),i=/\bnew\s+Response\s*\(/g,s=i.exec(n);for(;s;){let a=s.index+s[0].lastIndexOf("("),c=Ce(e,a+1),l=Ji(c?.args[1]);c&&l&&c.args[0]!==void 0&&t.push({index:s.index,expression:c.args[0],responseType:l}),i.lastIndex=c?.end??i.lastIndex,s=i.exec(n)}let r=new RegExp(`\\b(${kt})\\.(?:send|end|write)\\s*\\(`,"g"),o=r.exec(n);for(;o;){let a=o.index+o[0].lastIndexOf("("),c=Ce(e,a+1),l=na(e,n,o[1],o.index);c&&l&&c.args[0]!==void 0&&t.push({index:o.index,expression:c.args[0],responseType:l}),r.lastIndex=c?.end??r.lastIndex,o=r.exec(n)}return t}function na(e,t,n,i){let s=Wi(t,i),r=new RegExp(`\\b${ie(n)}\\.(setHeader|set|header|type|contentType)\\s*\\(`,"g"),o,a=r.exec(t);for(;a&&a.index<i;){let c=a.index+a[0].lastIndexOf("("),l=Ce(e,c+1);if(l&&sa(Wi(t,a.index),s)){let d=ia(a[1],l.args);d&&(o=d)}r.lastIndex=l?.end??r.lastIndex,a=r.exec(t)}return o}function ia(e,t){let n=t[0]?.trim()??"";if((e==="set"||e==="header")&&t.length===1){let r=Ji(n);if(r)return r}let i=/^["']([^"']+)["']$/.exec(n)?.[1];if(!i)return;if(e==="type"||e==="contentType")return/^html$/i.test(i)?"html":_t(i);if(!/^content-type$/i.test(i))return;let s=/^["']([^"']+)["']$/.exec(t[1]?.trim()??"")?.[1];return s?_t(s):void 0}function Wi(e,t){let n=[];for(let i=0;i<t;i++){let s=fe(e,i);if(s!==void 0){i=s-1;continue}e[i]==="{"?n.push(i):e[i]==="}"&&n.pop()}return n}function sa(e,t){return e.length<=t.length&&e.every((n,i)=>n===t[i])}function Ji(e){if(!e)return;let t=/(?:^|[,{]\s*)["']?Content-Type["']?\s*:\s*["']([^"']+)["']/i.exec(e)?.[1]??/\[\s*["']Content-Type["']\s*,\s*["']([^"']+)["']\s*\]/i.exec(e)?.[1];return t?_t(t):void 0}function _t(e){if(Ko.test(e))return"liquid";if(Jo.test(e))return"html"}function ra(e,t,n){let i=e.responseType==="html"?oa(e.expression,n):e.expression,s=It(i);return Bi.test(s)?!0:[...t].some(r=>Xi(s,r))}function oa(e,t){if(!e.startsWith("`")||Ki(e,0)!==e.length)return e;let n=[...e],i={inTag:!1,closingTag:!1},s=1,r=!0;for(let o=1;o<e.length-1;o++){if(e[o]==="\\"){o++;continue}if(e[o]!=="$"||e[o+1]!=="{")continue;da(i,e.slice(s,o));let a=Yi(e,o+2);if(a===void 0)return e;let c=o+2,l=a-1,d=e.slice(c,l);r&&ca(i)&&aa(d,t)?se(n,c,l):r=!1,o=l,s=a}return n.join("")}function aa(e,t){let n=la(e.trim());for(let i of t){let s=new RegExp(`^${ie(i)}\\s*\\(`).exec(n);if(!s)continue;let r=Ce(n,s[0].lastIndexOf("(")+1);if(r&&r.end===n.length)return!0}return!1}function ca(e){return!e.inTag&&e.rawTag===void 0}function da(e,t){let n=t.toLowerCase();for(let i=0;i<n.length;i++){let s=n[i];if(e.rawTag){let r=`</${e.rawTag}`;if(!n.startsWith(r,i))continue;e.inTag=!0,e.closingTag=!0,e.pendingTag=e.rawTag,e.rawTag=void 0,i+=r.length-1;continue}if(!e.inTag){if(s!=="<")continue;let r=/^<\s*(\/)?\s*([a-z][\w:-]*)/.exec(n.slice(i));if(!r)continue;e.inTag=!0,e.closingTag=!!r[1],e.pendingTag=r[2],i+=r[0].length-1;continue}if(e.quote){s===e.quote&&(e.quote=void 0);continue}if(s==='"'||s==="'"){e.quote=s;continue}s===">"&&(!e.closingTag&&(e.pendingTag==="script"||e.pendingTag==="style")&&(e.rawTag=e.pendingTag),e.inTag=!1,e.closingTag=!1,e.pendingTag=void 0,e.quote=void 0)}}function la(e){let t=e;for(;t.startsWith("(")&&ua(t,0,"(",")")===t.length-1;)t=t.slice(1,-1).trim();return t}function Ce(e,t){let n=[],i=t,s=[];for(let r=t;r<e.length;r++){let o=fe(e,r);if(o!==void 0){r=o-1;continue}let a=e[r];if(a==="("||a==="["||a==="{"){s.push(a);continue}if(a===")"||a==="]"||a==="}"){if(a===")"&&s.length===0)return n.push(e.slice(i,r).trim()),{args:n,end:r+1};s.pop();continue}a===","&&s.length===0&&(n.push(e.slice(i,r).trim()),i=r+1)}}function Vi(e,t){let n=[];for(let s=t;s<e.length;s++){let r=fe(e,s);if(r!==void 0){s=r-1;continue}let o=e[s];if(o==="("||o==="["||o==="{"){n.push(o);continue}if(o===")"||o==="]"||o==="}"){n.pop();continue}if(n.length===0&&o===";")return{text:e.slice(t,s).trim(),end:s+1};if(n.length===0&&o===`
|
|
2369
|
+
`&&!pa(e,t,s))return{text:e.slice(t,s).trim(),end:s+1}}let i=e.slice(t).trim();return i?{text:i,end:e.length}:void 0}function pa(e,t,n){let i=e.slice(t,n).trimEnd().at(-1),s=e.slice(n+1).trimStart().at(0);return!!(i&&"+-*/%&|?:.,".includes(i))||!!(s&&"+-*/%&|?:.,".includes(s))}function ua(e,t,n,i){let s=[];for(let r=t;r<e.length;r++){let o=fe(e,r);if(o!==void 0){r=o-1;continue}if(e[r]===n)s.push(n);else if(e[r]===i&&(s.pop(),s.length===0))return r}}function fe(e,t){let n=e[t];if(n==='"'||n==="'")return St(e,t,n);if(n==="`")return Ki(e,t);if(n==="/"&&Tt(e,t))return Et(e,t)}function St(e,t,n){for(let i=t+1;i<e.length;i++){if(e[i]==="\\"){i++;continue}if(e[i]===n)return i+1}}function Ki(e,t){for(let n=t+1;n<e.length;n++){if(e[n]==="\\"){n++;continue}if(e[n]==="`")return n+1;if(e[n]!=="$"||e[n+1]!=="{")continue;let i=Yi(e,n+2);if(i===void 0)return;n=i-1}}function Yi(e,t){let n=1;for(let i=t;i<e.length;i++){let s=fe(e,i);if(s!==void 0){i=s-1;continue}if(e[i]==="{")n++;else if(e[i]==="}"&&(n--,n===0))return i+1}}function Tt(e,t){let n=ha(e,t);if(n===void 0||"([{:;,=!?&|+-*%^~<>".includes(n))return!0;let i=e.slice(0,t).trimEnd();return/\b(?:return|throw|case|delete|void|typeof|instanceof|in|of|yield|await)$/.test(i)}function Et(e,t){let n=!1;for(let i=t+1;i<e.length;i++){if(e[i]==="\\"){i++;continue}if(e[i]==="["){n=!0;continue}if(e[i]==="]"&&n){n=!1;continue}if(!(e[i]!=="/"||n)){for(i++;/[a-z]/i.test(e[i]??"");)i++;return i}}}function ha(e,t){for(let n=t-1;n>=0;n--)if(!/\s/.test(e[n]))return e[n]}function Xi(e,t){let n=new RegExp(`\\b${ie(t)}\\b`,"g"),i=n.exec(e);for(;i;){let s=e[i.index-1],r=e.slice(i.index+t.length);if(s!=="."&&s!=='"'&&s!=="'"&&s!=="`"&&!/^\s*:/.test(r))return!0;i=n.exec(e)}return!1}function It(e){let t=[...e];return fa(t,e,0,e.length),t.join("")}function fa(e,t,n,i){for(let s=n;s<i;s++){let r=t[s];if(r==='"'||r==="'"){let o=St(t,s,r);if(o===void 0)return;se(e,s,o),s=o-1;continue}if(r==="`"){let o=Zi(e,t,s);if(o===void 0)return;s=o-1;continue}if(r==="/"&&Tt(t,s)){let o=Et(t,s);if(o===void 0)return;se(e,s,o),s=o-1}}}function Zi(e,t,n){e[n]=" ";for(let i=n+1;i<t.length;i++){if(t[i]==="\\"){se(e,i,Math.min(i+2,t.length)),i++;continue}if(t[i]==="`")return e[i]=" ",i+1;if(t[i]==="$"&&t[i+1]==="{"){e[i]=" ",e[i+1]=" ";let s=ma(e,t,i+2);if(s===void 0)return;i=s-1;continue}t[i]!==`
|
|
2370
|
+
`&&(e[i]=" ")}}function ma(e,t,n){let i=1;for(let s=n;s<t.length;s++){let r=t[s];if(r==='"'||r==="'"){let o=St(t,s,r);if(o===void 0)return;se(e,s,o),s=o-1;continue}if(r==="`"){let o=Zi(e,t,s);if(o===void 0)return;s=o-1;continue}if(r==="/"&&Tt(t,s)){let o=Et(t,s);if(o===void 0)return;se(e,s,o),s=o-1;continue}if(r==="{")i++;else if(r==="}"&&(i--,i===0))return e[s]=" ",s+1}}function se(e,t,n){for(let i=t;i<n;i++)e[i]!==`
|
|
2371
|
+
`&&(e[i]=" ")}function ga(e){let t=[...e],n;for(let i=0;i<t.length;i++){let s=t[i];if(n){s==="\\"?i++:s===n&&(n=void 0);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="/"&&t[i+1]==="/")for(;i<t.length&&t[i]!==`
|
|
2372
2372
|
`;)t[i]=" ",i++;else if(s==="/"&&t[i+1]==="*"){for(t[i]=" ",t[i+1]=" ",i+=2;i<t.length&&!(t[i]==="*"&&t[i+1]==="/");)t[i]!==`
|
|
2373
|
-
`&&(t[i]=" "),i++;i<t.length&&(t[i]=" ",t[i+1]=" ",i++)}}return t.join("")}function ie(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}u();var
|
|
2374
|
-
`);if(/\bexpiringOfflineAccessTokens\s*:\s*false\b/.test(i)){let s=n.find(o=>/\bexpiringOfflineAccessTokens\s*:\s*false\b/.test(Pe(o.content))),r=Pe(s.content).search(/\bexpiringOfflineAccessTokens\s*:\s*false\b/);return{issues:[
|
|
2375
|
-
`).length}function
|
|
2376
|
-
`).length},fix:{automated:!1,description:"Restrict frame-ancestors to Shopify Admin and the authenticated shop origin.",guide:"https://shopify.dev/docs/apps/build/security/set-up-iframe-protection"}})}return t}function
|
|
2373
|
+
`&&(t[i]=" "),i++;i<t.length&&(t[i]=" ",t[i+1]=" ",i++)}}return t.join("")}function ie(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}u();var ya=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"]);function es(e){let t=e.sourceFiles.filter(s=>s.content!==void 0).map(s=>s.path),n=e.sourceFiles.filter(s=>s.content&&ya.has(s.ext)&&/\bshopifyApp\s*\(/.test(Pe(s.content)));if(n.length===0)return{issues:[],inspectedFiles:t,unresolvedReason:"No React Router shopifyApp(...) setup was found, so offline-token expiry could not be verified."};let i=n.map(s=>Pe(s.content)).join(`
|
|
2374
|
+
`);if(/\bexpiringOfflineAccessTokens\s*:\s*false\b/.test(i)){let s=n.find(o=>/\bexpiringOfflineAccessTokens\s*:\s*false\b/.test(Pe(o.content))),r=Pe(s.content).search(/\bexpiringOfflineAccessTokens\s*:\s*false\b/);return{issues:[wa(s.path,ba(s.content,r))],inspectedFiles:t}}return/\bexpiringOfflineAccessTokens\s*:\s*true\b/.test(i)?/\bsessionStorage\s*:/.test(i)?va(e,i)?{issues:[],inspectedFiles:t}:{issues:[],inspectedFiles:t,unresolvedReason:"Expiring offline tokens are enabled, but session storage compatibility with expires/refreshToken/refreshTokenExpires metadata could not be verified."}:{issues:[],inspectedFiles:t,unresolvedReason:"Expiring offline tokens are enabled, but the configured session storage could not be identified."}:{issues:[],inspectedFiles:t,unresolvedReason:"expiringOfflineAccessTokens: true was not found in the React Router setup. The value may be absent or computed and needs review."}}function va(e,t){return/\bnew\s+(?:Memory|Redis|MongoDB|SQLite)SessionStorage\s*\(/.test(t)?!0:/\bnew\s+PrismaSessionStorage\s*\(/.test(t)?e.sourceFiles.some(n=>{if(n.ext!==".prisma"||!n.content)return!1;let i=/\bmodel\s+Session\s*\{([^}]*)\}/.exec(n.content)?.[1];return!!(i&&/\bexpires\s+DateTime\?/.test(i)&&/\brefreshToken\s+String\?/.test(i)&&/\brefreshTokenExpires\s+DateTime\?/.test(i))}):e.sourceFiles.some(n=>n.content&&/\brefreshToken\b/.test(n.content)&&/\brefreshTokenExpires\b/.test(n.content)&&/\bexpires\b/.test(n.content)&&/\b(?:storeSession|sessionStorage|SessionStorage)\b/.test(n.content))}function Pe(e){return e.replace(/\/\*[\s\S]*?\*\//g,t=>t.replace(/[^\n]/g," ")).replace(/\/\/[^\n]*/g,"").replace(/(["'`])(?:\\.|(?!\1)[^\\\n])*\1/g,t=>t.replace(/[^\n]/g," "))}function ba(e,t){return e.slice(0,Math.max(0,t)).split(`
|
|
2375
|
+
`).length}function wa(e,t){return{id:"EXPIRING_OFFLINE_TOKEN",severity:"medium",points:-10,title:"Expiring offline access tokens explicitly disabled",message:"The React Router Shopify app setup explicitly sets expiringOfflineAccessTokens to false.",location:{file:e,line:t},fix:{automated:!1,description:"Enable expiring offline access tokens and ensure session storage persists refresh metadata.",guide:"https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens"}}}u();var xa=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"]),ka="content-security-policy",_a=/\b(?:headers|response\.headers|res)\.(?:set|append|setHeader)\s*\(\s*$/i,Sa=/^https?:\/\/\*(?::(?:\*|\d+))?(?:\/.*)?$/i,Ta=/^(?:https?:\/\/)?\*\.myshopify\.com(?::(?:\*|\d+))?(?:\/.*)?$/i,Ea=/^(?:http|https):$/i;function is(e){let t=[];for(let n of e){if(!n.content||!xa.has(n.ext))continue;let i=Da(n.content);for(let s of Ia(i))Ca(s.value)&&t.push({id:"STATIC_FRAME_ANCESTORS",severity:"high",points:-12,title:"Embedded app frame-ancestors uses a wildcard",message:"A literal frame-ancestors directive allows arbitrary or wildcard embedding origins.",location:{file:n.path,line:i.slice(0,s.index).split(`
|
|
2376
|
+
`).length},fix:{automated:!1,description:"Restrict frame-ancestors to Shopify Admin and the authenticated shop origin.",guide:"https://shopify.dev/docs/apps/build/security/set-up-iframe-protection"}})}return t}function Ia(e){let t=[];for(let n=0;n<e.length;n++){let i=ge(e,n);if(i){if(i.static&&i.value.toLowerCase()===ka){let s=Aa(e,n,i.end);s!==void 0&&t.push({index:n,value:s})}n=i.end-1}}return t}function Aa(e,t,n){let i=rs(e,n);if(e[i]===":"){let s=ts(e,i+1,new Set([",","}"]));return s?me(s.text):void 0}if(e[i]===","&&Ra(e,t)){let s=ts(e,i+1,new Set([",",")"]));return s?me(s.text):void 0}}function Ra(e,t){return _a.test(e.slice(Math.max(0,t-100),t))}function Ca(e){return e.split(";").some(t=>{let[n,...i]=t.trim().split(/\s+/);return n?.toLowerCase()==="frame-ancestors"&&i.some(Pa)})}function Pa(e){return e==="*"||Ea.test(e)||Sa.test(e)||Ta.test(e)}function me(e){let t=La(e.trim());if(!t)return;let n=ge(t,0);if(n&&n.end===t.length&&n.static)return n.value;let i=ns(t,"+");if(i.length>1){let s=i.map(r=>me(r));if(s.every(r=>r!==void 0))return s.join("")}if(t.startsWith("[")){let s=ss(t,0,"[","]");if(s!==void 0){let r=/^\.join\s*\((.*)\)$/.exec(t.slice(s+1).trim());if(r){let o=r[1].trim()?me(r[1]):",";if(o===void 0)return;let a=ns(t.slice(1,s),",").map(c=>c.trim()).filter(Boolean).map(c=>me(c));if(a.every(c=>c!==void 0))return a.join(o)}}}}function La(e){let t=e;for(;t.startsWith("(")&&ss(t,0,"(",")")===t.length-1;)t=t.slice(1,-1).trim();return t}function ts(e,t,n){let i=rs(e,t),s=0;for(let o=i;o<e.length;o++){let a=ge(e,o);if(a){o=a.end-1;continue}let c=e[o];if(c==="("||c==="["||c==="{")s++;else if(c===")"||c==="]"||c==="}"){if(s===0&&n.has(c))return{text:e.slice(i,o).trim(),end:o};s--}else if(s===0&&n.has(c))return{text:e.slice(i,o).trim(),end:o}}let r=e.slice(i).trim();return r?{text:r,end:e.length}:void 0}function ns(e,t){let n=[],i=0,s=0;for(let r=0;r<e.length;r++){let o=ge(e,r);if(o){r=o.end-1;continue}let a=e[r];a==="("||a==="["||a==="{"?s++:a===")"||a==="]"||a==="}"?s--:s===0&&a===t&&(n.push(e.slice(i,r)),i=r+1)}return n.push(e.slice(i)),n}function ss(e,t,n,i){let s=0;for(let r=t;r<e.length;r++){let o=ge(e,r);if(o){r=o.end-1;continue}if(e[r]===n)s++;else if(e[r]===i&&(s--,s===0))return r}}function ge(e,t){let n=e[t];if(n!=='"'&&n!=="'"&&n!=="`")return;let i=!0;for(let s=t+1;s<e.length;s++){let r=e[s];if(r==="\\"){s++;continue}if(n==="`"&&r==="$"&&e[s+1]==="{"&&(i=!1),r===n)return{end:s+1,value:e.slice(t+1,s),static:i}}return{end:e.length,value:"",static:!1}}function rs(e,t){let n=t;for(;/\s/.test(e[n]??"");)n++;return n}function Da(e){let t=[...e],n;for(let i=0;i<t.length;i++){let s=t[i];if(n){s==="\\"?i++:s===n&&(n=void 0);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="/"&&t[i+1]==="/")for(;i<t.length&&t[i]!==`
|
|
2377
2377
|
`;)t[i]=" ",i++;else if(s==="/"&&t[i+1]==="*"){for(t[i]=" ",t[i+1]=" ",i+=2;i<t.length&&!(t[i]==="*"&&t[i+1]==="/");)t[i]!==`
|
|
2378
|
-
`&&(t[i]=" "),i++;i<t.length&&(t[i]=" ",t[i+1]=" ",i++)}}return t.join("")}u();u();function Q(e,t){return e<t?-1:e>t?1:0}function Le(e,t){return Q(e.location.file,t.location.file)||La(e.location.line,t.location.line)}function La(e,t){return e===t?0:e===void 0?-1:t===void 0?1:e-t}var rs=e=>({file:y(e.file.replace(/\\/g,"/")),...e.line===void 0?{}:{line:e.line},...e.column===void 0?{}:{column:e.column}}),Da=e=>(e??[]).map(t=>({location:rs(t.location),...t.quote===void 0?{}:{quote:y(t.quote)}}));function At(e){return{...e,id:y(e.id),title:y(e.title),message:y(e.message),location:rs(e.location),...e.snippet===void 0?{}:{snippet:y(e.snippet)},...e.detection_evidence===void 0?{}:{detection_evidence:e.detection_evidence.map(y)},...e.evidence===void 0?{}:{evidence:Da(e.evidence)},fix:{...e.fix,description:y(e.fix.description),...e.fix.guide?{guide:y(e.fix.guide)}:{}}}}function Na(e){let t=At(e);return{location:t.location,message:t.message,evidence:t.evidence??[],...t.snippet===void 0?{}:{snippet:t.snippet},fix:t.fix}}function qa(e,t){return Le(e,t)||Q(e.message,t.message)}function Oa(e){return e==="unsupported_framework"?"unresolved":e}function Fa(e){let t=G.find(n=>n.id===e.id);if(!t)throw new Error(`Deterministic check has no catalog entry: ${e.id}`);return{title:t.title,severity:t.severity,description:t.description,...t.guide?{guide:t.guide}:{},docs_url:t.docsUrl,current_version:e.version}}function Ma(e,t){return{id:y(e.id),version:e.version,status:Oa(e.status),...e.reason?{reason:{...e.reason,message:y(e.reason.message)}}:{},analysis_mode:e.analysis_mode,snapshot:Fa(e),findings:t.map(Na).sort(qa)}}function Ua(e){let t=new Set(e.scan.checks_executed.map(i=>i.id)),n=e.issues.find(i=>!t.has(i.id));if(n)throw new Error(`Finding for a check that was not executed: ${n.id}`);return Map.groupBy(e.issues,i=>i.id)}function os(e,t){let n=Ua(e),i=e.scan.checks_executed.map(s=>Ma(s,n.get(s.id)??[])).sort((s,r)=>Q(s.id,r.id));return{schema_version:C,source:"deterministic",engine:{name:D,version:y(t.engineVersion??e.version),ruleset:y(t.ruleset??`app-security-rules@${e.version}`)},generated_at:t.generatedAt??new Date().toISOString(),detection:{...e.detection,languages:e.detection.languages.map(s=>({...s,files:s.files.map(r=>y(r))}))},coverage:{files_scanned:e.scan.files_scanned,files_skipped:(e.scan.files_skipped??[]).map(s=>({...s,path:y(s.path),...s.detail?{detail:y(s.detail)}:{}})),gaps:e.scan.coverage_gaps.map(s=>({...s,message:y(s.message),...s.file?{file:y(s.file)}:{}})),scope:xe(t.scope),scan_directories:t.scanDirectories.map(s=>({...s,directory:y(s.directory)}))},checks:i}}var J=[".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"],Rt=(e,t=1)=>({id:e.id,version:t,lifecycle:"active",analysisMode:"structured_config",target:"config",requires:e.requires,runner:n=>e.check(n)}),M=(e,t,n="source",i=1)=>({id:e,version:i,lifecycle:"active",analysisMode:"regex",target:n,extensions:J,runner:t}),$a=[Rt($i),{id:"MISSING_DEPENDENCY_SECURITY_AUTOMATION",version:2,lifecycle:"active",analysisMode:"structured_config",target:"dependency_automation",runner:e=>ii(e)},{id:"EOL_API_VERSION",version:2,lifecycle:"active",analysisMode:"regex",target:"config_and_source",extensions:J,runner:e=>Hi(e)},{...M("EXPIRING_OFFLINE_TOKEN",e=>Zi(e),"source",2),extensions:[...J,".prisma"]},{...M("UNAUTHENTICATED_ENDPOINT",e=>Ei(e.sourceFiles),"source",3),requires:"has_backend"},M("REQUEST_CONTROLLED_ADMIN_CONTEXT",e=>Ii(e.sourceFiles),"source",4),{...Rt(ht,2),target:"config_and_source",analysisMode:"regex",extensions:J,runner:e=>[...ht.check(e),...Ui(e.sourceFiles)]},Rt(Si,2),{id:"COMMITTED_SECRET",version:4,lifecycle:"active",analysisMode:"regex",target:"secrets",runner:e=>qn(e.sensitiveFiles,e.appRoot,e.gitIgnoreListing)},M("CREDENTIAL_LOG_LEAKAGE",e=>Pi(e.sourceFiles),"source",2),M("CREDENTIAL_BROWSER_LEAKAGE",e=>Li(e.sourceFiles),"source",2),{id:"LIQUID_UNSAFE_RENDER",version:1,lifecycle:"active",analysisMode:"ast",target:"theme",requires:"theme_app_extension",extensions:[".liquid",".html"],runner:e=>Ha(e,"LIQUID_UNSAFE_RENDER")},{...M("UNSAFE_INNERHTML",ja,"source_and_theme",3),analysisMode:"regex",extensions:[...J,".liquid",".html"]},{...M("APP_PROXY_LIQUID_INJECTION",e=>Bi(e.sourceFiles),"source",3),requires:"app_proxy"},{...M("STATIC_FRAME_ANCESTORS",e=>ns(e.sourceFiles),"app_source",2),requires:"embedded_app"}];function ja(e){let t=[],n=[];if(e.detection.framework==="react_router"){let i=Pt(e).filter(r=>r.content!==void 0&&J.includes(r.ext)),s=bt(i);t.push(...s),n.push({id:"react-router-js-regex",analysisMode:"regex",status:"executed",inspectedFiles:i.map(r=>r.path),findings:s.length})}if(e.capabilities.theme_app_extension){let i=re(e),s=new Set(i.map(h=>h.path));e.detection.framework!=="react_router"&&e.sourceCandidates.some(h=>!s.has(h.path))&&n.push({id:"app-source-unsupported",analysisMode:"regex",status:"unsupported_framework",inspectedFiles:[],findings:0,reason:{code:"unsupported_framework",message:"The non-theme app source framework is not supported by deterministic analysis."}});let r=i.filter(h=>h.content!==void 0&&J.includes(h.ext)),o=bt(r);t.push(...o),n.push(r.length>0?{id:"theme-js-regex",analysisMode:"regex",status:"executed",inspectedFiles:r.map(h=>h.path),findings:o.length}:{id:"theme-js-regex",analysisMode:"regex",status:"not_applicable",inspectedFiles:[],findings:0,reason:{code:"no_relevant_files",message:"The theme app extensions contain no JavaScript files."}});let a=i.filter(h=>h.content!==void 0&&(h.ext===".liquid"||h.ext===".html")),c=xt(a),l=c.issues.filter(h=>h.id==="UNSAFE_INNERHTML");t.push(...l);let d="executed",f;a.length===0?(d="not_applicable",f={code:"no_relevant_files",message:"The theme app extensions contain no Liquid files."}):c.parserFailures.length>0&&(d="unresolved",f={code:"parser_unavailable",message:`Liquid parser failed for: ${c.parserFailures.join(", ")}`}),n.push({id:"theme-liquid-ast",analysisMode:"ast",status:d,inspectedFiles:a.map(h=>h.path),findings:l.length,...f?{reason:f}:{}})}return{issues:t,inspectedFiles:[...new Set(n.flatMap(i=>i.inspectedFiles))],...n.some(i=>i.status==="unresolved")?{unresolvedReason:n.filter(i=>i.status==="unresolved").map(i=>i.reason?.message).filter(Boolean).join("; ")}:{}}}function Ha(e,t){let n=xt(re(e).filter(s=>s.content!==void 0)),i=n.parserFailures;return{issues:n.issues.filter(s=>s.id===t),...i.length>0?{unresolvedReason:`Liquid parser failed for: ${i.join(", ")}`}:{}}}var De=Ga($a);za([...De.values()]);var vp=[...De.values()].map(({id:e,version:t,requires:n})=>({id:e,version:t,requires:n}));function Ga(e){let t=new Map;for(let n of e){if(t.has(n.id))throw new Error(`Duplicate deterministic stable ID: ${n.id}`);t.set(n.id,n)}return t}function za(e){let t=new Set(G.map(n=>n.id));for(let n of e){if(!t.has(n.id))throw new Error(`Orphan deterministic runner: ${n.id}`);if(n.lifecycle==="active"&&!n.runner)throw new Error(`Active deterministic check has no runner: ${n.id}`);if(n.lifecycle!=="active"&&n.runner)throw new Error(`A non-active deterministic check can't have a runner: ${n.id}`)}}function re(e){return e.extensions.filter(t=>t.type==="theme").flatMap(t=>t.files)}function Ct(e){let t=new Set(re(e).map(n=>n.path));return e.sourceFiles.filter(n=>!t.has(n.path))}function as(e,t){return t.detection.framework!=="react_router"||wi(e,t.reactRouterRoots,t.otherAppDirectories)}function Pt(e){return Ct(e).filter(t=>as(t.path,e))}function cs(e,t){let n=t.appTomls.map(r=>w(t.appRoot,r.path).replace(/\\/g,"/"));if(e.target==="config")return n;if(e.target==="dependency_automation")return[...t.manifests.map(r=>r.path),...t.dependencyAutomation.files.filter(r=>r.content!==void 0).map(r=>r.path)];if(e.target==="secrets")return t.sensitiveFiles.filter(r=>r.content!==void 0).map(r=>r.path);let i=e.target==="app_source"?Ct(t):Pt(t);e.target==="theme"?i=re(t):e.target==="source_and_theme"&&(i=[...i,...re(t)]);let s=i.filter(r=>r.content!==void 0&&(!e.extensions||e.extensions.includes(r.ext))).map(r=>r.path);return e.target==="config_and_source"?[...n,...s]:s}function Wa(e,t){return t.sourceCandidates.find(n=>n.path===e)?.language}function Va(e,t){let n=cs(e,t),i=new Set(re(t).map(a=>a.path)),s=t.sourceCandidates.filter(a=>!i.has(a.path)),r=t.detection.framework==="react_router",o=t.capabilities.theme_app_extension;return e.target==="dependency_automation"&&!t.manifests.some(ds)?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:"No supported package manifest declares dependencies."}}:e.target==="source"&&!r?s.length>0?{status:"unsupported_framework",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} applies to app source, but deterministic analysis requires @shopify/shopify-app-react-router and its conventional app structure.`}}:{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:"No React Router app source applies to this check."}}:e.target==="source_and_theme"&&!r&&!o?s.length>0?{status:"unsupported_framework",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} applies to app source, but its framework is not supported by deterministic analysis.`}}:{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:"No supported app or theme source applies to this check."}}:e.target==="source_and_theme"&&!r&&s.length>0?n.length>0?{status:"unresolved",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} inspected theme extensions, but the non-theme app source framework is unsupported.`}}:{status:"unsupported_framework",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} applies to app source, but its framework is unsupported.`}}:e.target==="theme"&&!o?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"capability_absent",message:"No theme app extension was detected."}}:e.target==="config_and_source"&&!r&&s.length>0&&t.appTomls.length>0?{status:"unresolved",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`Configuration can be inspected, but ${e.id} source analysis requires @shopify/shopify-app-react-router.`}}:e.requires&&!t.capabilities[e.requires]?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"capability_absent",message:`Capability ${e.requires} was not detected.`}}:(e.target==="config"||e.target==="config_and_source")&&t.appTomls.length===0?{status:"unresolved",required:!0,applicable:!0,reason:{code:"parser_unavailable",message:"No readable Shopify app configuration was available."}}:["source","app_source","theme","secrets","source_and_theme"].includes(e.target)&&n.length===0?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:`No ${e.target.replaceAll("_"," ")} files apply to this check.`}}:{status:"executed",required:!0,applicable:!0}}function ds(e){return Object.keys(e.dependencies).length>0||Object.keys(e.devDependencies??{}).length>0}function Ba(e,t,n){let i=t.extensions.filter(d=>d.type==="theme").map(d=>d.path.replace(/shopify\.extension\.toml$/,"")),s=d=>i.some(f=>d.startsWith(f))||d.endsWith("shopify.extension.toml"),r=d=>!s(d)&&!!e.extensions?.some(f=>d.endsWith(f)),o=d=>r(d)&&as(d,t),a=d=>/^shopify\.app(?:\.[^/]+)?\.toml$/.test(d),c=d=>/(^|\/)package\.json$/.test(d)||t.dependencyAutomation.files.some(f=>f.path===d),l=d=>!d.detail?.includes("could not be parsed")&&(t.sourceCandidates.some(f=>f.path===d.path)||t.sensitiveFiles.some(f=>f.path===d.path)||/(^|\/)(?:\.env(?:\.[^/]+)?|secrets\.json|credentials\.json)$/.test(d.path));return n.filter(d=>e.target==="config"?a(d.path):e.target==="dependency_automation"?c(d.path):e.target==="config_and_source"?a(d.path)||o(d.path):e.target==="source"?o(d.path):e.target==="app_source"?r(d.path):e.target==="theme"?s(d.path):e.target==="source_and_theme"?o(d.path)||s(d.path):l(d))}function Qa(e,t){return{code:t.some(i=>!!i.detail?.includes("could not be parsed")||i.path.endsWith("shopify.extension.toml")||i.path.endsWith("package.json"))?"parser_unavailable":"input_rejected",message:`${e.id} could not inspect required input: ${t.map(i=>`${i.path} (${i.reason.replaceAll("_"," ")})`).join(", ")}.`}}function Ja(e,t){return e.target==="app_source"?{...t,sourceFiles:Ct(t)}:e.target==="source"||e.target==="config_and_source"?{...t,sourceFiles:Pt(t)}:t}function Ka(e){return Array.isArray(e)?{issues:e}:e}function ls({appDirectory:e,scanDirectories:t,requestedScanDirectories:n,appConfigFilePath:i},s){return oi({appDirectory:e,scanDirectories:t,requestedScanDirectories:n,selectedAppConfigFilePath:i,rules:Jn({excludePatterns:s.excludePatterns??[],noGitIgnore:s.noGitIgnore??!1})})}async function Ya(e,t={}){at({appDirectory:e.appDirectory,scanDirectories:e.scanDirectories,explicitInputs:new Set});let{paths:n,ignoredScanDirectories:i,otherAppDirectories:s}=await ls(e,t);return{paths:n,ignoredScanDirectories:i,otherAppDirectories:s}}async function ps(e,t={}){let{appDirectory:n,scanDirectories:i,appConfigFilePath:s}=e;at({appDirectory:n,scanDirectories:i,explicitInputs:new Set(s?[s]:[])});let r=s?R(w(n,s)):void 0,o=s?ri(s,n):null,a=o?[o]:[],{paths:c,ignoredScanDirectories:l,otherAppDirectories:d,listingStatus:f}=await ls(e,t),h=ai(n,c),g=ui(c),x=lt(n,c),E=hi(n,c,r),ye=mi(c),Me=gi(n,ye),Ds=Me.some(ds)?fi(n,c):{files:[]},Mt=vi(o,h,x,a),Ut=d.map(m=>R(w(n,m))),$t=bi(Me,g,Ut),oe=ki(h,g,$t),ae={appRoot:n,appToml:o,appTomls:a,extensions:h,sourceFiles:x,manifests:Me,dependencyAutomation:Ds,sensitiveFiles:E,capabilities:Mt,detection:oe,reactRouterRoots:$t,otherAppDirectories:Ut,sourceCandidates:g,gitIgnoreListing:f},$=[],Y=[],jt=new Set;for(let m of De.values()){let _=Va(m,ae),Ue=_.status==="unsupported_framework"?[]:cs(m,ae),Fs=$.length,$e=Ba(m,ae,ct()),Ms=m.target==="dependency_automation"&&$e.length>0;if((_.status==="executed"||_.status==="unresolved")&&m.runner&&!Ms){let A=Ka(await m.runner(Ja(m,ae)));(m.target!=="dependency_automation"||!A.unresolvedReason)&&$.push(...A.issues),A.inspectedFiles&&(Ue=A.inspectedFiles),A.unresolvedReason&&(_={status:"unresolved",required:!0,applicable:!0,reason:{code:A.unresolvedReasonCode??"parser_unavailable",message:A.unresolvedReason}})}_.status!=="unsupported_framework"&&$e.length>0&&(_={status:"unresolved",required:!0,applicable:!0,reason:Qa(m,$e)}),_.required&&jt.add(m.id);let Us=[...new Set(Ue.map(A=>Wa(A,ae)).filter(A=>!!A))].sort();Y.push({id:m.id,version:m.version,kind:"deterministic",status:_.status,applicable:_.applicable,languages:Us,framework:oe.framework,surface:oe.surface,inspected_files:Ue,findings:$.length-Fs,analysis_mode:m.analysisMode,..._.reason?{reason:_.reason}:{}})}let Ns=new Map([...De.values()].map(m=>[m.id,m.version]));$=$.map(m=>At({...m,rule_version:Ns.get(m.id)??1}));for(let m of Y)m.findings=$.filter(_=>_.id===m.id).length;let Ht=[...new Map(ct().map(m=>{let _={...m,path:y(m.path),...m.detail?{detail:y(m.detail)}:{}};return[`${_.path}|${_.reason}`,_]})).values()],qs=[...Ht.map(m=>({code:"skipped_file",file:m.path,message:`The file was not inspected because it was ${m.reason.replaceAll("_"," ")}.`})),...oe.languages.filter(m=>m.support==="unsupported").map(m=>({code:"unsupported_language",message:`${m.name} source is not supported by deterministic analysis.`})),...Y.flatMap(m=>!jt.has(m.id)||m.status!=="unsupported_framework"&&m.status!=="unresolved"?[]:[{code:m.status==="unsupported_framework"?"unsupported_framework":"unresolved_check",check_id:m.id,message:m.reason?.message??`${m.id} could not execute.`}])],Gt=Y.filter(m=>m.status==="executed").length,Os=_i(x.filter(m=>m.content!==void 0).length,Gt,Y.length-Gt,Ht,Y,qs);return{version:z(),timestamp:new Date().toISOString(),app:{name:y(String(o?.raw.name??"Unknown")),type:"public"},capabilities:Mt,detection:oe,scan:Os,issues:$,ignoredScanDirectories:l,otherAppDirectories:d}}function Xa(){return En}function Za({appDirectory:e,scanDirectories:t,appConfigDirectories:n=[]}){return t.map(i=>({directory:R(w(e,i))||".",origin:ec(i,e,n)}))}function ec(e,t,n){return e===t?"app_directory":n.includes(e)?"app_config_directory":"include_dir"}async function tc(e,t={}){let{ignoredScanDirectories:n,otherAppDirectories:i,...s}=await ps(e,t),r=z(),o=os(s,{engineVersion:r,scope:{include_dirs:[...t.includeDirs??[]],excludes:[...t.excludePatterns??[]],no_git_ignore:t.noGitIgnore??!1},scanDirectories:Za(e)});return{scan:s,ignoredScanDirectories:n,otherAppDirectories:i,deterministicFindings:o,agentChecks:Qn(r),engine:o.engine}}u();var us={deterministic:0,agent:1};function nc(e){return[...oc(e)].map(([t,n])=>cc(t,n)).sort((t,n)=>j[n.severity]-j[t.severity]||Q(t.id,n.id))}function Lt(e){return e.findings.filter(t=>t.disposition==="active")}function ic(e){return e.status==="executed"&&Lt(e).length===0}function sc(e){return e.applied_precedence!==e.precedence}function gs(e,t){return t==="agent"&&e.suppression!==void 0}function rc(e){let t={withFindings:0,passed:0,notApplicable:0,unresolved:0,suppressed:0,superseded:0};for(let n of e){Lt(n).length>0?t.withFindings+=1:n.status==="executed"?t.passed+=1:n.status==="not_applicable"?t.notApplicable+=1:t.unresolved+=1;for(let i of n.findings)i.disposition==="suppressed"&&(t.suppressed+=1),i.disposition==="superseded"&&(t.superseded+=1)}return t}function oc({deterministic:e,agent:t}){let n=new Map;if(e)for(let i of e.checks)n.set(i.id,{deterministic:hs(i,e),agent:void 0,describingCheck:i});if(t)for(let i of t.checks)n.set(i.id,{deterministic:n.get(i.id)?.deterministic,agent:hs(i,t),describingCheck:i});return n}function ac(e,t){return t.generated_at}function hs(e,t){return{check:e,generatedAt:ac(e,t)}}function cc(e,{deterministic:t,agent:n,describingCheck:i}){let s=n?.check.snapshot.precedence??"union",{snapshot:r}=i,o=dc(s,t,n),a=o==="prefer-agent"&&n!==void 0,c=[...(t?.check.findings??[]).map(l=>fs(l,"deterministic",a?"superseded":"active")),...(n?.check.findings??[]).map(l=>fs(l,"agent",gs(l,"agent")?"suppressed":"active"))].sort(uc);return{id:e,title:r.title,severity:r.severity,description:r.description,...r.guide?{guide:r.guide}:{},...r.docs_url?{docs_url:r.docs_url}:{},precedence:s,applied_precedence:o,status:a?n.check.status:pc([t,n].flatMap(l=>l?[l.check.status]:[])),by_source:{deterministic:t?ms(t):null,agent:n?ms(n):null},findings:c}}function dc(e,t,n){return e!=="prefer-agent"||n===void 0?"union":lc(t,n)?"prefer-agent":"union"}function lc(e,t){if(e===void 0)return!0;let n=Date.parse(t.generatedAt),i=Date.parse(e.generatedAt);return Number.isNaN(n)||Number.isNaN(i)?!1:n>=i}function pc(e){return e.includes("executed")?"executed":e.includes("unresolved")?"unresolved":"not_applicable"}function fs(e,t,n){return{...e,source:t,disposition:n}}function ms({check:e,generatedAt:t}){return{version:e.version,status:e.status,...e.reason?{reason:e.reason}:{},...e.analysis_mode?{analysis_mode:e.analysis_mode}:{},generated_at:t}}function uc(e,t){return Le(e,t)||us[e.source]-us[t.source]||Q(e.message,t.message)}u();u();u();var ys=p.enum(["high","medium","low"]),vs=p.enum(["executed","not_applicable","unresolved"]),bs=p.enum(["union","prefer-agent"]),ws=p.object({file:p.string(),line:p.number().optional(),column:p.number().optional()}),hc=p.object({automated:p.boolean(),guide:p.string().optional(),description:p.string()}),fc=p.object({title:p.string(),severity:ys,description:p.string(),guide:p.string().optional(),docs_url:p.string().optional(),current_version:p.number(),precedence:bs.optional()}),mc=p.object({location:ws,quote:p.string().optional()}),xs=p.object({location:ws,message:p.string(),evidence:p.array(mc),snippet:p.string().optional(),fix:hc.optional(),confidence:p.enum(["high","medium","low"]).optional(),reasoning:p.string().optional(),suppression:p.object({justification:p.string()}).optional()}),ks=p.object({id:p.string(),version:p.number(),status:vs,reason:p.object({code:p.string(),message:p.string()}).optional(),analysis_mode:p.enum(["regex","structured_config","ast"]).optional(),snapshot:fc,findings:p.array(xs)}),gc=p.array(ks).superRefine((e,t)=>{let n=new Set;e.forEach((i,s)=>{n.has(i.id)&&t.addIssue({code:p.ZodIssueCode.custom,path:[s,"id"],message:`duplicate check id ${i.id}`}),n.add(i.id)})}),_s=p.object({framework:p.enum(["react_router","none","unknown","mixed"]),surface:p.enum(["react_router","theme_app_extension","config_only","unknown","mixed"]),languages:p.array(p.object({name:p.string(),support:p.enum(["supported","unsupported"]),files:p.array(p.string())}))}),Ss=()=>p.object({include_dirs:p.array(p.string()),excludes:p.array(p.string()),no_git_ignore:p.boolean()}),Ts=p.object({files_scanned:p.number(),files_skipped:p.array(p.object({path:p.string(),reason:p.enum(["too_large","unreadable"]),size_bytes:p.number().optional(),detail:p.string().optional()})),gaps:p.array(p.object({code:p.enum(["skipped_file","unsupported_framework","unsupported_language","unresolved_check"]),message:p.string(),check_id:p.string().optional(),file:p.string().optional()})),scope:Ss(),scan_directories:p.array(p.object({directory:p.string(),origin:p.enum(["app_directory","include_dir","app_config_directory"])}))}),Es={schema_version:p.literal(C),generated_at:p.string(),checks:gc},Is=p.object({...Es,source:p.literal("deterministic"),engine:p.object({name:p.literal(D),version:p.string(),ruleset:p.string()}),detection:_s,coverage:Ts}),As=p.object({...Es,source:p.literal("agent"),engine:p.object({name:p.literal(D),version:p.string()}),scope:Ss()}),Rs=p.discriminatedUnion("source",[Is,As]);var yc=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),vc=e=>e===null?"null":Array.isArray(e)?"array":typeof e;function bc(e){let t=e.path.map((n,i)=>typeof n=="number"?`[${n}]`:i===0?String(n):`.${n}`).join("");return t.length===0?e.message:`${t}: ${e.message}`}function Dt(e){if(!yc(e))return{ok:!1,errors:[`expected a JSON object, received ${vc(e)}`]};if(e.schema_version!==C)return{ok:!1,errors:[`unsupported schema_version: ${String(e.schema_version)} (expected ${C})`]};let t=Rs.safeParse(e);return t.success?{ok:!0,document:t.data}:{ok:!1,errors:t.error.issues.map(bc)}}u();function wc(e){return[e.id,e.pattern_id??"",e.severity].join("|")}function xc(e){let t=new Map,n=[...e].sort((i,s)=>j[s.severity]-j[i.severity]||i.location.file.localeCompare(s.location.file)||(i.location.line??0)-(s.location.line??0)||(i.location.column??0)-(s.location.column??0)||i.id.localeCompare(s.id)||i.title.localeCompare(s.title));for(let i of n){let s=wc(i),r=t.get(s);r?(r.issues.push(i),r.files.includes(i.location.file)||r.files.push(i.location.file)):t.set(s,{severity:i.severity,issues:[i],files:[i.location.file]})}return[...t.values()]}function kc(e){return{too_large:e.filter(t=>t.reason==="too_large").length,unreadable:e.filter(t=>t.reason==="unreadable").length}}u();import{randomBytes as _c}from"node:crypto";import{lstat as K,mkdir as Sc,readdir as Tc,realpath as Ne,rename as Ec,rm as Ic,unlink as Ac,writeFile as Rc}from"node:fs/promises";var Cc=5e6,Cs=[".shopify","app-security"];function Pc(e){return b(e,...Cs)}function Fe(e,t){Lc(t);let n=b(e,...Cs,t);return{resultsDirectory:n,deterministicFindingsPath:b(n,"deterministic-findings.json"),agentChecksPath:b(n,"agent-checks.json"),agentFindingsPath:b(n,"agent-findings.json")}}function Lc(e){if(e===""||e==="."||e===".."||/[\\/]/.test(e))throw new I(`The results key "${e}" can't be used as a directory name.`,"Use a client ID without path separators.")}async function cu(e,t,{deterministicFindings:n,agentChecks:i}){let s=Fe(e,t);return await Ls(e,s.resultsDirectory),await qt(s.deterministicFindingsPath,qe(n)),await qt(s.agentChecksPath,qe(i)),{deterministicFindingsPath:s.deterministicFindingsPath,agentChecksPath:s.agentChecksPath}}async function du(e,t,n){let i=Fe(e,t);return await Ls(e,i.resultsDirectory),await qt(i.agentFindingsPath,qe(n)),i.agentFindingsPath}async function lu(e,t){let{resultsDirectory:n}=Fe(e,t);return Ft(e,n)}async function pu(e,t){let{resultsDirectory:n}=Fe(e,t);return await Ft(e,n)?(await Ps(n),[n]):[]}async function uu(e){let t=Pc(e);if(!await Ft(e,t))return[];let n=(await Tc(t)).map(r=>b(t,r)),i=await Promise.all(n.map(async r=>({path:r,stats:await K(r)})));i.filter(({stats:r})=>r.isSymbolicLink()).forEach(({path:r})=>U(r));let s=i.filter(({stats:r})=>r.isDirectory()).map(({path:r})=>r);return await Promise.all(s.map(Ps)),s}async function hu(e,t){let n=await Nc(e);if(n.status!=="ok")return n;let i=Dt(n.value);return i.ok?Dc(i.document,t)?{status:"ok",value:i.document}:{status:"invalid",errors:[`source is "${i.document.source}", but this file must hold "${t}" findings.`]}:{status:"invalid",errors:i.errors}}function Dc(e,t){return e.source===t}async function Nc(e){if(!await Yt(e))return{status:"missing"};let t;try{if(await Jt(e)>Cc)return{status:"invalid",errors:["The file is larger than 5 MB."]};t=await Bt(e)}catch(n){return{status:"invalid",errors:[`Could not read the file: ${Ot(n)}`]}}try{return{status:"ok",value:JSON.parse(t)}}catch(n){return{status:"invalid",errors:[`Could not parse JSON: ${Ot(n)}`]}}}function fu(e){return Buffer.byteLength(qe(e),"utf8")}function qe(e){return`${JSON.stringify(e,null,2)}
|
|
2379
|
-
`}async function Ft(e,t){let n=T(e),i=T(t);Oe(n,i);let s=await K(n);(s.isSymbolicLink()||!s.isDirectory())&&U(i);let r=n;for(let o of w(n,i).split("/")){r=b(r,o);let a=await
|
|
2378
|
+
`&&(t[i]=" "),i++;i<t.length&&(t[i]=" ",t[i+1]=" ",i++)}}return t.join("")}u();u();function Q(e,t){return e<t?-1:e>t?1:0}function Le(e,t){return Q(e.location.file,t.location.file)||Na(e.location.line,t.location.line)}function Na(e,t){return e===t?0:e===void 0?-1:t===void 0?1:e-t}var os=e=>({file:y(e.file.replace(/\\/g,"/")),...e.line===void 0?{}:{line:e.line},...e.column===void 0?{}:{column:e.column}}),qa=e=>(e??[]).map(t=>({location:os(t.location),...t.quote===void 0?{}:{quote:y(t.quote)}}));function At(e){return{...e,id:y(e.id),title:y(e.title),message:y(e.message),location:os(e.location),...e.snippet===void 0?{}:{snippet:y(e.snippet)},...e.detection_evidence===void 0?{}:{detection_evidence:e.detection_evidence.map(y)},...e.evidence===void 0?{}:{evidence:qa(e.evidence)},fix:{...e.fix,description:y(e.fix.description),...e.fix.guide?{guide:y(e.fix.guide)}:{}}}}function Oa(e){let t=At(e);return{location:t.location,message:t.message,evidence:t.evidence??[],...t.snippet===void 0?{}:{snippet:t.snippet},fix:t.fix}}function Fa(e,t){return Le(e,t)||Q(e.message,t.message)}function Ma(e){return e==="unsupported_framework"?"unresolved":e}function Ua(e){let t=G.find(n=>n.id===e.id);if(!t)throw new Error(`Deterministic check has no catalog entry: ${e.id}`);return{title:t.title,severity:t.severity,description:t.description,...t.guide?{guide:t.guide}:{},docs_url:t.docsUrl,current_version:e.version}}function $a(e,t){return{id:y(e.id),version:e.version,status:Ma(e.status),...e.reason?{reason:{...e.reason,message:y(e.reason.message)}}:{},analysis_mode:e.analysis_mode,snapshot:Ua(e),findings:t.map(Oa).sort(Fa)}}function ja(e){let t=new Set(e.scan.checks_executed.map(i=>i.id)),n=e.issues.find(i=>!t.has(i.id));if(n)throw new Error(`Finding for a check that was not executed: ${n.id}`);return Map.groupBy(e.issues,i=>i.id)}function as(e,t){let n=ja(e),i=e.scan.checks_executed.map(s=>$a(s,n.get(s.id)??[])).sort((s,r)=>Q(s.id,r.id));return{schema_version:C,source:"deterministic",engine:{name:D,version:y(t.engineVersion??e.version),ruleset:y(t.ruleset??`app-security-rules@${e.version}`)},generated_at:t.generatedAt??new Date().toISOString(),detection:{...e.detection,languages:e.detection.languages.map(s=>({...s,files:s.files.map(r=>y(r))}))},coverage:{files_scanned:e.scan.files_scanned,files_skipped:(e.scan.files_skipped??[]).map(s=>({...s,path:y(s.path),...s.detail?{detail:y(s.detail)}:{}})),gaps:e.scan.coverage_gaps.map(s=>({...s,message:y(s.message),...s.file?{file:y(s.file)}:{}})),scope:xe(t.scope),scan_directories:t.scanDirectories.map(s=>({...s,directory:y(s.directory)}))},checks:i}}var J=[".js",".jsx",".mjs",".cjs",".ts",".tsx",".mts",".cts"],Rt=(e,t=1)=>({id:e.id,version:t,lifecycle:"active",analysisMode:"structured_config",target:"config",requires:e.requires,runner:n=>e.check(n)}),M=(e,t,n="source",i=1)=>({id:e,version:i,lifecycle:"active",analysisMode:"regex",target:n,extensions:J,runner:t}),Ha=[Rt(ji),{id:"MISSING_DEPENDENCY_SECURITY_AUTOMATION",version:2,lifecycle:"active",analysisMode:"structured_config",target:"dependency_automation",runner:e=>si(e)},{id:"EOL_API_VERSION",version:2,lifecycle:"active",analysisMode:"regex",target:"config_and_source",extensions:J,runner:e=>Gi(e)},{...M("EXPIRING_OFFLINE_TOKEN",e=>es(e),"source",2),extensions:[...J,".prisma"]},{...M("UNAUTHENTICATED_ENDPOINT",e=>Ii(e.sourceFiles),"source",3),requires:"has_backend"},M("REQUEST_CONTROLLED_ADMIN_CONTEXT",e=>Ai(e.sourceFiles),"source",4),{...Rt(ht,2),target:"config_and_source",analysisMode:"regex",extensions:J,runner:e=>[...ht.check(e),...$i(e.sourceFiles)]},Rt(Ti,2),{id:"COMMITTED_SECRET",version:4,lifecycle:"active",analysisMode:"regex",target:"secrets",runner:e=>On(e.sensitiveFiles,e.appRoot,e.gitIgnoreListing)},M("CREDENTIAL_LOG_LEAKAGE",e=>Li(e.sourceFiles),"source",2),M("CREDENTIAL_BROWSER_LEAKAGE",e=>Di(e.sourceFiles),"source",2),{id:"LIQUID_UNSAFE_RENDER",version:1,lifecycle:"active",analysisMode:"ast",target:"theme",requires:"theme_app_extension",extensions:[".liquid",".html"],runner:e=>za(e,"LIQUID_UNSAFE_RENDER")},{...M("UNSAFE_INNERHTML",Ga,"source_and_theme",3),analysisMode:"regex",extensions:[...J,".liquid",".html"]},{...M("APP_PROXY_LIQUID_INJECTION",e=>Qi(e.sourceFiles),"source",3),requires:"app_proxy"},{...M("STATIC_FRAME_ANCESTORS",e=>is(e.sourceFiles),"app_source",2),requires:"embedded_app"}];function Ga(e){let t=[],n=[];if(e.detection.framework==="react_router"){let i=Pt(e).filter(r=>r.content!==void 0&&J.includes(r.ext)),s=bt(i);t.push(...s),n.push({id:"react-router-js-regex",analysisMode:"regex",status:"executed",inspectedFiles:i.map(r=>r.path),findings:s.length})}if(e.capabilities.theme_app_extension){let i=re(e),s=new Set(i.map(h=>h.path));e.detection.framework!=="react_router"&&e.sourceCandidates.some(h=>!s.has(h.path))&&n.push({id:"app-source-unsupported",analysisMode:"regex",status:"unsupported_framework",inspectedFiles:[],findings:0,reason:{code:"unsupported_framework",message:"The non-theme app source framework is not supported by deterministic analysis."}});let r=i.filter(h=>h.content!==void 0&&J.includes(h.ext)),o=bt(r);t.push(...o),n.push(r.length>0?{id:"theme-js-regex",analysisMode:"regex",status:"executed",inspectedFiles:r.map(h=>h.path),findings:o.length}:{id:"theme-js-regex",analysisMode:"regex",status:"not_applicable",inspectedFiles:[],findings:0,reason:{code:"no_relevant_files",message:"The theme app extensions contain no JavaScript files."}});let a=i.filter(h=>h.content!==void 0&&(h.ext===".liquid"||h.ext===".html")),c=xt(a),l=c.issues.filter(h=>h.id==="UNSAFE_INNERHTML");t.push(...l);let d="executed",f;a.length===0?(d="not_applicable",f={code:"no_relevant_files",message:"The theme app extensions contain no Liquid files."}):c.parserFailures.length>0&&(d="unresolved",f={code:"parser_unavailable",message:`Liquid parser failed for: ${c.parserFailures.join(", ")}`}),n.push({id:"theme-liquid-ast",analysisMode:"ast",status:d,inspectedFiles:a.map(h=>h.path),findings:l.length,...f?{reason:f}:{}})}return{issues:t,inspectedFiles:[...new Set(n.flatMap(i=>i.inspectedFiles))],...n.some(i=>i.status==="unresolved")?{unresolvedReason:n.filter(i=>i.status==="unresolved").map(i=>i.reason?.message).filter(Boolean).join("; ")}:{}}}function za(e,t){let n=xt(re(e).filter(s=>s.content!==void 0)),i=n.parserFailures;return{issues:n.issues.filter(s=>s.id===t),...i.length>0?{unresolvedReason:`Liquid parser failed for: ${i.join(", ")}`}:{}}}var De=Wa(Ha);Va([...De.values()]);var xp=[...De.values()].map(({id:e,version:t,requires:n})=>({id:e,version:t,requires:n}));function Wa(e){let t=new Map;for(let n of e){if(t.has(n.id))throw new Error(`Duplicate deterministic stable ID: ${n.id}`);t.set(n.id,n)}return t}function Va(e){let t=new Set(G.map(n=>n.id));for(let n of e){if(!t.has(n.id))throw new Error(`Orphan deterministic runner: ${n.id}`);if(n.lifecycle==="active"&&!n.runner)throw new Error(`Active deterministic check has no runner: ${n.id}`);if(n.lifecycle!=="active"&&n.runner)throw new Error(`A non-active deterministic check can't have a runner: ${n.id}`)}}function re(e){return e.extensions.filter(t=>t.type==="theme").flatMap(t=>t.files)}function Ct(e){let t=new Set(re(e).map(n=>n.path));return e.sourceFiles.filter(n=>!t.has(n.path))}function cs(e,t){return t.detection.framework!=="react_router"||xi(e,t.reactRouterRoots,t.otherAppDirectories)}function Pt(e){return Ct(e).filter(t=>cs(t.path,e))}function ds(e,t){let n=t.appTomls.map(r=>w(t.appRoot,r.path).replace(/\\/g,"/"));if(e.target==="config")return n;if(e.target==="dependency_automation")return[...t.manifests.map(r=>r.path),...t.dependencyAutomation.files.filter(r=>r.content!==void 0).map(r=>r.path)];if(e.target==="secrets")return t.sensitiveFiles.filter(r=>r.content!==void 0).map(r=>r.path);let i=e.target==="app_source"?Ct(t):Pt(t);e.target==="theme"?i=re(t):e.target==="source_and_theme"&&(i=[...i,...re(t)]);let s=i.filter(r=>r.content!==void 0&&(!e.extensions||e.extensions.includes(r.ext))).map(r=>r.path);return e.target==="config_and_source"?[...n,...s]:s}function Ba(e,t){return t.sourceCandidates.find(n=>n.path===e)?.language}function Qa(e,t){let n=ds(e,t),i=new Set(re(t).map(a=>a.path)),s=t.sourceCandidates.filter(a=>!i.has(a.path)),r=t.detection.framework==="react_router",o=t.capabilities.theme_app_extension;return e.target==="dependency_automation"&&!t.manifests.some(ls)?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:"No supported package manifest declares dependencies."}}:e.target==="source"&&!r?s.length>0?{status:"unsupported_framework",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} applies to app source, but deterministic analysis requires @shopify/shopify-app-react-router and its conventional app structure.`}}:{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:"No React Router app source applies to this check."}}:e.target==="source_and_theme"&&!r&&!o?s.length>0?{status:"unsupported_framework",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} applies to app source, but its framework is not supported by deterministic analysis.`}}:{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:"No supported app or theme source applies to this check."}}:e.target==="source_and_theme"&&!r&&s.length>0?n.length>0?{status:"unresolved",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} inspected theme extensions, but the non-theme app source framework is unsupported.`}}:{status:"unsupported_framework",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`${e.id} applies to app source, but its framework is unsupported.`}}:e.target==="theme"&&!o?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"capability_absent",message:"No theme app extension was detected."}}:e.target==="config_and_source"&&!r&&s.length>0&&t.appTomls.length>0?{status:"unresolved",required:!0,applicable:!0,reason:{code:"unsupported_framework",message:`Configuration can be inspected, but ${e.id} source analysis requires @shopify/shopify-app-react-router.`}}:e.requires&&!t.capabilities[e.requires]?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"capability_absent",message:`Capability ${e.requires} was not detected.`}}:(e.target==="config"||e.target==="config_and_source")&&t.appTomls.length===0?{status:"unresolved",required:!0,applicable:!0,reason:{code:"parser_unavailable",message:"No readable Shopify app configuration was available."}}:["source","app_source","theme","secrets","source_and_theme"].includes(e.target)&&n.length===0?{status:"not_applicable",required:!1,applicable:!1,reason:{code:"no_relevant_files",message:`No ${e.target.replaceAll("_"," ")} files apply to this check.`}}:{status:"executed",required:!0,applicable:!0}}function ls(e){return Object.keys(e.dependencies).length>0||Object.keys(e.devDependencies??{}).length>0}function Ja(e,t,n){let i=t.extensions.filter(d=>d.type==="theme").map(d=>d.path.replace(/shopify\.extension\.toml$/,"")),s=d=>i.some(f=>d.startsWith(f))||d.endsWith("shopify.extension.toml"),r=d=>!s(d)&&!!e.extensions?.some(f=>d.endsWith(f)),o=d=>r(d)&&cs(d,t),a=d=>/^shopify\.app(?:\.[^/]+)?\.toml$/.test(d),c=d=>/(^|\/)package\.json$/.test(d)||t.dependencyAutomation.files.some(f=>f.path===d),l=d=>!d.detail?.includes("could not be parsed")&&(t.sourceCandidates.some(f=>f.path===d.path)||t.sensitiveFiles.some(f=>f.path===d.path)||/(^|\/)(?:\.env(?:\.[^/]+)?|secrets\.json|credentials\.json)$/.test(d.path));return n.filter(d=>e.target==="config"?a(d.path):e.target==="dependency_automation"?c(d.path):e.target==="config_and_source"?a(d.path)||o(d.path):e.target==="source"?o(d.path):e.target==="app_source"?r(d.path):e.target==="theme"?s(d.path):e.target==="source_and_theme"?o(d.path)||s(d.path):l(d))}function Ka(e,t){return{code:t.some(i=>!!i.detail?.includes("could not be parsed")||i.path.endsWith("shopify.extension.toml")||i.path.endsWith("package.json"))?"parser_unavailable":"input_rejected",message:`${e.id} could not inspect required input: ${t.map(i=>`${i.path} (${i.reason.replaceAll("_"," ")})`).join(", ")}.`}}function Ya(e,t){return e.target==="app_source"?{...t,sourceFiles:Ct(t)}:e.target==="source"||e.target==="config_and_source"?{...t,sourceFiles:Pt(t)}:t}function Xa(e){return Array.isArray(e)?{issues:e}:e}function ps({appDirectory:e,scanDirectories:t,requestedScanDirectories:n,appConfigFilePath:i},s){return ai({appDirectory:e,scanDirectories:t,requestedScanDirectories:n,selectedAppConfigFilePath:i,rules:Kn({excludePatterns:s.excludePatterns??[],noGitIgnore:s.noGitIgnore??!1})})}async function Za(e,t={}){at({appDirectory:e.appDirectory,scanDirectories:e.scanDirectories,explicitInputs:new Set});let{paths:n,ignoredScanDirectories:i,otherAppDirectories:s}=await ps(e,t);return{paths:n,ignoredScanDirectories:i,otherAppDirectories:s}}async function us(e,t={}){let{appDirectory:n,scanDirectories:i,appConfigFilePath:s}=e;at({appDirectory:n,scanDirectories:i,explicitInputs:new Set(s?[s]:[])});let r=s?R(w(n,s)):void 0,o=s?oi(s,n):null,a=o?[o]:[],{paths:c,ignoredScanDirectories:l,otherAppDirectories:d,listingStatus:f}=await ps(e,t),h=ci(n,c),g=hi(c),x=lt(n,c),E=fi(n,c,r),ye=gi(c),Me=yi(n,ye),Ns=Me.some(ls)?mi(n,c):{files:[]},Mt=bi(o,h,x,a),Ut=d.map(m=>R(w(n,m))),$t=wi(Me,g,Ut),oe=_i(h,g,$t),ae={appRoot:n,appToml:o,appTomls:a,extensions:h,sourceFiles:x,manifests:Me,dependencyAutomation:Ns,sensitiveFiles:E,capabilities:Mt,detection:oe,reactRouterRoots:$t,otherAppDirectories:Ut,sourceCandidates:g,gitIgnoreListing:f},$=[],Y=[],jt=new Set;for(let m of De.values()){let _=Qa(m,ae),Ue=_.status==="unsupported_framework"?[]:ds(m,ae),Ms=$.length,$e=Ja(m,ae,ct()),Us=m.target==="dependency_automation"&&$e.length>0;if((_.status==="executed"||_.status==="unresolved")&&m.runner&&!Us){let A=Xa(await m.runner(Ya(m,ae)));(m.target!=="dependency_automation"||!A.unresolvedReason)&&$.push(...A.issues),A.inspectedFiles&&(Ue=A.inspectedFiles),A.unresolvedReason&&(_={status:"unresolved",required:!0,applicable:!0,reason:{code:A.unresolvedReasonCode??"parser_unavailable",message:A.unresolvedReason}})}_.status!=="unsupported_framework"&&$e.length>0&&(_={status:"unresolved",required:!0,applicable:!0,reason:Ka(m,$e)}),_.required&&jt.add(m.id);let $s=[...new Set(Ue.map(A=>Ba(A,ae)).filter(A=>!!A))].sort();Y.push({id:m.id,version:m.version,kind:"deterministic",status:_.status,applicable:_.applicable,languages:$s,framework:oe.framework,surface:oe.surface,inspected_files:Ue,findings:$.length-Ms,analysis_mode:m.analysisMode,..._.reason?{reason:_.reason}:{}})}let qs=new Map([...De.values()].map(m=>[m.id,m.version]));$=$.map(m=>At({...m,rule_version:qs.get(m.id)??1}));for(let m of Y)m.findings=$.filter(_=>_.id===m.id).length;let Ht=[...new Map(ct().map(m=>{let _={...m,path:y(m.path),...m.detail?{detail:y(m.detail)}:{}};return[`${_.path}|${_.reason}`,_]})).values()],Os=[...Ht.map(m=>({code:"skipped_file",file:m.path,message:`The file was not inspected because it was ${m.reason.replaceAll("_"," ")}.`})),...oe.languages.filter(m=>m.support==="unsupported").map(m=>({code:"unsupported_language",message:`${m.name} source is not supported by deterministic analysis.`})),...Y.flatMap(m=>!jt.has(m.id)||m.status!=="unsupported_framework"&&m.status!=="unresolved"?[]:[{code:m.status==="unsupported_framework"?"unsupported_framework":"unresolved_check",check_id:m.id,message:m.reason?.message??`${m.id} could not execute.`}])],Gt=Y.filter(m=>m.status==="executed").length,Fs=Si(x.filter(m=>m.content!==void 0).length,Gt,Y.length-Gt,Ht,Y,Os);return{version:z(),timestamp:new Date().toISOString(),app:{name:y(String(o?.raw.name??"Unknown")),type:"public"},capabilities:Mt,detection:oe,scan:Fs,issues:$,ignoredScanDirectories:l,otherAppDirectories:d}}function ec(){return In}function tc({appDirectory:e,scanDirectories:t,appConfigDirectories:n=[]}){return t.map(i=>({directory:R(w(e,i))||".",origin:nc(i,e,n)}))}function nc(e,t,n){return e===t?"app_directory":n.includes(e)?"app_config_directory":"include_dir"}async function ic(e,t={}){let{ignoredScanDirectories:n,otherAppDirectories:i,...s}=await us(e,t),r=z(),o=as(s,{engineVersion:r,scope:{include_dirs:[...t.includeDirs??[]],excludes:[...t.excludePatterns??[]],no_git_ignore:t.noGitIgnore??!1},scanDirectories:tc(e)});return{scan:s,ignoredScanDirectories:n,otherAppDirectories:i,deterministicFindings:o,agentChecks:Jn(r),engine:o.engine}}u();var hs={deterministic:0,agent:1};function sc(e){return[...cc(e)].map(([t,n])=>lc(t,n)).sort((t,n)=>j[n.severity]-j[t.severity]||Q(t.id,n.id))}function Lt(e){return e.findings.filter(t=>t.disposition==="active")}function rc(e){return e.status==="executed"&&Lt(e).length===0}function oc(e){return e.applied_precedence!==e.precedence}function ys(e,t){return t==="agent"&&e.suppression!==void 0}function ac(e){let t={withFindings:0,passed:0,notApplicable:0,unresolved:0,suppressed:0,superseded:0};for(let n of e){Lt(n).length>0?t.withFindings+=1:n.status==="executed"?t.passed+=1:n.status==="not_applicable"?t.notApplicable+=1:t.unresolved+=1;for(let i of n.findings)i.disposition==="suppressed"&&(t.suppressed+=1),i.disposition==="superseded"&&(t.superseded+=1)}return t}function cc({deterministic:e,agent:t}){let n=new Map;if(e)for(let i of e.checks)n.set(i.id,{deterministic:fs(i,e),agent:void 0,describingCheck:i});if(t)for(let i of t.checks)n.set(i.id,{deterministic:n.get(i.id)?.deterministic,agent:fs(i,t),describingCheck:i});return n}function dc(e,t){return t.generated_at}function fs(e,t){return{check:e,generatedAt:dc(e,t)}}function lc(e,{deterministic:t,agent:n,describingCheck:i}){let s=n?.check.snapshot.precedence??"union",{snapshot:r}=i,o=pc(s,t,n),a=o==="prefer-agent"&&n!==void 0,c=[...(t?.check.findings??[]).map(l=>ms(l,"deterministic",a?"superseded":"active")),...(n?.check.findings??[]).map(l=>ms(l,"agent",ys(l,"agent")?"suppressed":"active"))].sort(fc);return{id:e,title:r.title,severity:r.severity,description:r.description,...r.guide?{guide:r.guide}:{},...r.docs_url?{docs_url:r.docs_url}:{},precedence:s,applied_precedence:o,status:a?n.check.status:hc([t,n].flatMap(l=>l?[l.check.status]:[])),by_source:{deterministic:t?gs(t):null,agent:n?gs(n):null},findings:c}}function pc(e,t,n){return e!=="prefer-agent"||n===void 0?"union":uc(t,n)?"prefer-agent":"union"}function uc(e,t){if(e===void 0)return!0;let n=Date.parse(t.generatedAt),i=Date.parse(e.generatedAt);return Number.isNaN(n)||Number.isNaN(i)?!1:n>=i}function hc(e){return e.includes("executed")?"executed":e.includes("unresolved")?"unresolved":"not_applicable"}function ms(e,t,n){return{...e,source:t,disposition:n}}function gs({check:e,generatedAt:t}){return{version:e.version,status:e.status,...e.reason?{reason:e.reason}:{},...e.analysis_mode?{analysis_mode:e.analysis_mode}:{},generated_at:t}}function fc(e,t){return Le(e,t)||hs[e.source]-hs[t.source]||Q(e.message,t.message)}u();u();u();var vs=p.enum(["high","medium","low"]),bs=p.enum(["executed","not_applicable","unresolved"]),ws=p.enum(["union","prefer-agent"]),xs=p.object({file:p.string(),line:p.number().optional(),column:p.number().optional()}),mc=p.object({automated:p.boolean(),guide:p.string().optional(),description:p.string()}),gc=p.object({title:p.string(),severity:vs,description:p.string(),guide:p.string().optional(),docs_url:p.string().optional(),current_version:p.number(),precedence:ws.optional()}),yc=p.object({location:xs,quote:p.string().optional()}),ks=p.object({location:xs,message:p.string(),evidence:p.array(yc),snippet:p.string().optional(),fix:mc.optional(),confidence:p.enum(["high","medium","low"]).optional(),reasoning:p.string().optional(),suppression:p.object({justification:p.string()}).optional()}),_s=p.object({id:p.string(),version:p.number(),status:bs,reason:p.object({code:p.string(),message:p.string()}).optional(),analysis_mode:p.enum(["regex","structured_config","ast"]).optional(),snapshot:gc,findings:p.array(ks)}),vc=p.array(_s).superRefine((e,t)=>{let n=new Set;e.forEach((i,s)=>{n.has(i.id)&&t.addIssue({code:p.ZodIssueCode.custom,path:[s,"id"],message:`duplicate check id ${i.id}`}),n.add(i.id)})}),Ss=p.object({framework:p.enum(["react_router","none","unknown","mixed"]),surface:p.enum(["react_router","theme_app_extension","config_only","unknown","mixed"]),languages:p.array(p.object({name:p.string(),support:p.enum(["supported","unsupported"]),files:p.array(p.string())}))}),Ts=()=>p.object({include_dirs:p.array(p.string()),excludes:p.array(p.string()),no_git_ignore:p.boolean()}),Es=p.object({files_scanned:p.number(),files_skipped:p.array(p.object({path:p.string(),reason:p.enum(["too_large","unreadable"]),size_bytes:p.number().optional(),detail:p.string().optional()})),gaps:p.array(p.object({code:p.enum(["skipped_file","unsupported_framework","unsupported_language","unresolved_check"]),message:p.string(),check_id:p.string().optional(),file:p.string().optional()})),scope:Ts(),scan_directories:p.array(p.object({directory:p.string(),origin:p.enum(["app_directory","include_dir","app_config_directory"])}))}),Is={schema_version:p.literal(C),generated_at:p.string(),checks:vc},As=p.object({...Is,source:p.literal("deterministic"),engine:p.object({name:p.literal(D),version:p.string(),ruleset:p.string()}),detection:Ss,coverage:Es}),Rs=p.object({...Is,source:p.literal("agent"),engine:p.object({name:p.literal(D),version:p.string()}),scope:Ts()}),Cs=p.discriminatedUnion("source",[As,Rs]);var bc=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),wc=e=>e===null?"null":Array.isArray(e)?"array":typeof e;function xc(e){let t=e.path.map((n,i)=>typeof n=="number"?`[${n}]`:i===0?String(n):`.${n}`).join("");return t.length===0?e.message:`${t}: ${e.message}`}function Dt(e){if(!bc(e))return{ok:!1,errors:[`expected a JSON object, received ${wc(e)}`]};if(e.schema_version!==C)return{ok:!1,errors:[`unsupported schema_version: ${String(e.schema_version)} (expected ${C})`]};let t=Cs.safeParse(e);return t.success?{ok:!0,document:t.data}:{ok:!1,errors:t.error.issues.map(xc)}}u();function kc(e){return[e.id,e.pattern_id??"",e.severity].join("|")}function _c(e){let t=new Map,n=[...e].sort((i,s)=>j[s.severity]-j[i.severity]||i.location.file.localeCompare(s.location.file)||(i.location.line??0)-(s.location.line??0)||(i.location.column??0)-(s.location.column??0)||i.id.localeCompare(s.id)||i.title.localeCompare(s.title));for(let i of n){let s=kc(i),r=t.get(s);r?(r.issues.push(i),r.files.includes(i.location.file)||r.files.push(i.location.file)):t.set(s,{severity:i.severity,issues:[i],files:[i.location.file]})}return[...t.values()]}function Sc(e){return{too_large:e.filter(t=>t.reason==="too_large").length,unreadable:e.filter(t=>t.reason==="unreadable").length}}u();import{randomBytes as Tc}from"node:crypto";import{lstat as K,mkdir as Ec,readdir as Ic,realpath as Ne,rename as Ac,rm as Rc,unlink as Cc,writeFile as Pc}from"node:fs/promises";var Lc=5e6,Ps=[".shopify","app-security"];function Dc(e){return b(e,...Ps)}function Fe(e,t){Nc(t);let n=b(e,...Ps,t);return{resultsDirectory:n,deterministicFindingsPath:b(n,"deterministic-findings.json"),agentChecksPath:b(n,"agent-checks.json"),agentFindingsPath:b(n,"agent-findings.json")}}function Nc(e){if(e===""||e==="."||e===".."||/[\\/]/.test(e))throw new I(`The results key "${e}" can't be used as a directory name.`,"Use a client ID without path separators.")}async function pu(e,t,{deterministicFindings:n,agentChecks:i}){let s=Fe(e,t);return await Ds(e,s.resultsDirectory),await qt(s.deterministicFindingsPath,qe(n)),await qt(s.agentChecksPath,qe(i)),{deterministicFindingsPath:s.deterministicFindingsPath,agentChecksPath:s.agentChecksPath}}async function uu(e,t,n){let i=Fe(e,t);return await Ds(e,i.resultsDirectory),await qt(i.agentFindingsPath,qe(n)),i.agentFindingsPath}async function hu(e,t){let{resultsDirectory:n}=Fe(e,t);return Ft(e,n)}async function fu(e,t){let{resultsDirectory:n}=Fe(e,t);return await Ft(e,n)?(await Ls(n),[n]):[]}async function mu(e){let t=Dc(e);if(!await Ft(e,t))return[];let n=(await Ic(t)).map(r=>b(t,r)),i=await Promise.all(n.map(async r=>({path:r,stats:await K(r)})));i.filter(({stats:r})=>r.isSymbolicLink()).forEach(({path:r})=>U(r));let s=i.filter(({stats:r})=>r.isDirectory()).map(({path:r})=>r);return await Promise.all(s.map(Ls)),s}async function gu(e,t){let n=await Oc(e);if(n.status!=="ok")return n;let i=Dt(n.value);return i.ok?qc(i.document,t)?{status:"ok",value:i.document}:{status:"invalid",errors:[`source is "${i.document.source}", but this file must hold "${t}" findings.`]}:{status:"invalid",errors:i.errors}}function qc(e,t){return e.source===t}async function Oc(e){if(!await Yt(e))return{status:"missing"};let t;try{if(await Jt(e)>Lc)return{status:"invalid",errors:["The file is larger than 5 MB."]};t=await Bt(e)}catch(n){return{status:"invalid",errors:[`Could not read the file: ${Ot(n)}`]}}try{return{status:"ok",value:JSON.parse(t)}}catch(n){return{status:"invalid",errors:[`Could not parse JSON: ${Ot(n)}`]}}}function yu(e){return Buffer.byteLength(qe(e),"utf8")}function qe(e){return`${JSON.stringify(e,null,2)}
|
|
2379
|
+
`}async function Ft(e,t){let n=T(e),i=T(t);Oe(n,i);let s=await K(n);(s.isSymbolicLink()||!s.isDirectory())&&U(i);let r=n;for(let o of w(n,i).split("/")){r=b(r,o);let a=await Fc(r);if(!a)return!1;(a.isSymbolicLink()||!a.isDirectory())&&U(r)}return Oe(await Ne(n),await Ne(i)),!0}async function Ls(e){try{await Rc(e,{recursive:!0,force:!0})}catch(t){throw new I(`Could not remove the app security check results at ${e}.`,Ot(t))}}async function Fc(e){try{return await K(e)}catch(t){if(t.code==="ENOENT")return;throw t}}async function Ds(e,t){let n=T(e),i=T(t);Oe(n,i);let s=await K(n);(s.isSymbolicLink()||!s.isDirectory())&&U(i);let r=n;for(let c of w(n,i).split("/"))r=b(r,c),await Mc(r);let o=await Ne(n),a=await Ne(i);Oe(o,a),await rn(e)}async function Mc(e){try{let n=await K(e);(n.isSymbolicLink()||!n.isDirectory())&&U(e);return}catch(n){if(n.code!=="ENOENT")throw n}await Ec(e,{mode:448});let t=await K(e);(t.isSymbolicLink()||!t.isDirectory())&&U(e)}function Oe(e,t){let n=w(e,t);(!n||n.startsWith("..")||n.startsWith("/"))&&U(t)}function U(e){throw new I(`Refusing to write app security check artifacts through a symbolic link or outside the app: ${e}`,"Remove or replace the unsafe app security check artifact path, then run the command again.")}async function qt(e,t){await Nt(e);let n=`${e}.${Tc(8).toString("hex")}.tmp`;await Nt(n);try{await Pc(n,t,{encoding:"utf8",mode:384}),await Nt(e),await Ac(n,e)}catch(i){throw await Cc(n).catch(()=>{}),i}}async function Nt(e){try{(await K(e)).isSymbolicLink()&&U(e)}catch(t){if(t.code==="ENOENT")return;throw t}}function Ot(e){return e instanceof Error?e.message:String(e)}export{Gs as a,$c as b,mr as c,j as d,Rr as e,z as f,Za as g,ec as h,ic as i,sc as j,Lt as k,rc as l,oc as m,ys as n,ac as o,_c as p,Sc as q,Lc as r,Dc as s,Fe as t,pu as u,uu as v,hu as w,fu as x,mu as y,gu as z,yu as A,xn as B,id as C,sd as D,rd as E,od as F,ad as G,cd as H,md as I,Sn as J,sr as K,gd as L,yd as M};
|