Merge branch 'master' into master

main
Elbert Alias 3 years ago committed by GitHub
commit 38e1a4bd3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,18 @@
name: Close inactive issues
on:
schedule:
- cron: "30 0 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
days-before-stale: 90
days-before-close: 14
stale-issue-label: "stale"
stale-pr-message: "This PR is stale because it has been open for 90 days with no activity."
close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale."
stale-issue-message: "This issue is stale because it has been open for 90 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."

@ -1,4 +1,4 @@
[![Travis](https://travis-ci.org/aliasio/wappalyzer.svg?branch=master)](https://travis-ci.org/aliasio/wappalyzer/)
[![Validate](https://github.com/AliasIO/wappalyzer/actions/workflows/validate.yml/badge.svg)](https://github.com/AliasIO/wappalyzer/actions/workflows/validate.yml)
[![wappalyzer NPM](https://img.shields.io/badge/npm-wappalyzer-blue)](https://www.npmjs.com/package/wappalyzer)
[![wappalyzer-core NPM](https://img.shields.io/badge/npm-wappalyzer--core-blue)](https://www.npmjs.com/package/wappalyzer-core)
[![Github Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/AliasIO)](https://github.com/sponsors/AliasIO)

@ -19,7 +19,6 @@ const { AWS_LAMBDA_FUNCTION_NAME, CHROMIUM_BIN, CHROMIUM_DATA_DIR } =
let puppeteer
let chromiumArgs = [
'--no-sandbox',
'--single-process',
'--no-zygote',
'--disable-gpu',
'--ignore-certificate-errors',
@ -460,7 +459,8 @@ class Site {
promiseTimeout(
promise,
fallback,
errorMessage = 'Operation took too long to respond'
errorMessage = 'Operation took too long to respond',
maxWait = this.options.maxWait
) {
let timeout = null
@ -478,7 +478,7 @@ class Site {
error.code = 'PROMISE_TIMEOUT_ERROR'
fallback !== undefined ? resolve(fallback) : reject(error)
}, this.options.maxWait)
}, maxWait)
}),
promise.then((value) => {
clearTimeout(timeout)
@ -615,11 +615,16 @@ class Site {
await page.setUserAgent(this.options.userAgent)
try {
await this.promiseTimeout(
page.goto(url.href),
undefined,
'Timeout (navigation)'
)
try {
await this.promiseTimeout(page.goto(url.href))
} catch (error) {
if (
error.constructor.name !== 'TimeoutError' &&
error.code !== 'PROMISE_TIMEOUT_ERROR'
) {
throw error
}
}
if (!this.options.noScripts) {
await sleep(1000)
@ -779,11 +784,7 @@ class Site {
this.analyzedUrls[url.href] &&
!this.analyzedUrls[url.href].status
) {
await page.close()
this.log(`Page closed (${url})`)
throw new Error(`No response from server`)
throw new Error('No response from server')
}
this.cache[url.href] = {
@ -843,10 +844,18 @@ class Site {
await page.close()
this.log('Page closed')
this.log(`Page closed (${url})`)
return reducedLinks
} catch (error) {
try {
await page.close()
this.log(`Page closed (${url})`)
} catch (error) {
this.log(error)
}
let hostname = url
try {
@ -890,17 +899,26 @@ class Site {
await sleep(this.options.delay * index)
}
const links = await this.goto(url)
if (links && this.options.recursive && depth < this.options.maxDepth) {
await this.batch(links.slice(0, this.options.maxUrls), depth + 1)
}
await Promise.all([
(async () => {
const links = await this.goto(url)
if (this.options.probe && !this.probed) {
this.probed = true
if (
links &&
this.options.recursive &&
depth < this.options.maxDepth
) {
await this.batch(links.slice(0, this.options.maxUrls), depth + 1)
}
})(),
(async () => {
if (this.options.probe && !this.probed) {
this.probed = true
await this.probe(url)
}
await this.probe(url)
}
})(),
])
} catch (error) {
this.analyzedUrls[url.href] = {
status: 0,
@ -950,31 +968,9 @@ class Site {
magento: '/magento_version',
}
for (const file of Object.keys(files)) {
const path = files[file]
try {
await sleep(this.options.delay)
const body = await get(new URL(path, url.href), {
userAgent: this.options.userAgent,
timeout: Math.min(this.options.maxWait, 3000),
})
this.log(`get ${path}: ok`)
await this.onDetect(
url,
await analyze({ [file]: body.slice(0, 100000) })
)
} catch (error) {
this.error(`get ${path}: ${error.message || error}`)
}
}
// DNS
const records = {}
const resolve = (func, hostname) => {
const resolveDns = (func, hostname) => {
return this.promiseTimeout(
func(hostname).catch((error) => {
if (error.code !== 'ENODATA') {
@ -984,39 +980,74 @@ class Site {
return []
}),
[],
'Timeout (dns)'
'Timeout (dns)',
Math.min(this.options.maxWait, 15000)
)
}
const domain = url.hostname.replace(/^www\./, '')
;[records.cname, records.ns, records.mx, records.txt, records.soa] =
await Promise.all([
resolve(dns.resolveCname, url.hostname),
resolve(dns.resolveNs, domain),
resolve(dns.resolveMx, domain),
resolve(dns.resolveTxt, domain),
resolve(dns.resolveSoa, domain),
])
await Promise.all([
// Static files
...Object.keys(files).map(async (file, index) => {
const path = files[file]
const dnsRecords = Object.keys(records).reduce((dns, type) => {
dns[type] = dns[type] || []
try {
await sleep(this.options.delay * index)
Array.prototype.push.apply(
dns[type],
Array.isArray(records[type])
? records[type].map((value) => {
return typeof value === 'object'
? Object.values(value).join(' ')
: value
})
: [Object.values(records[type]).join(' ')]
)
const body = await get(new URL(path, url.href), {
userAgent: this.options.userAgent,
timeout: Math.min(this.options.maxWait, 3000),
})
this.log(`Probe ok (${path})`)
await this.onDetect(
url,
await analyze({ [file]: body.slice(0, 100000) })
)
} catch (error) {
this.error(`Probe failed (${path}): ${error.message || error}`)
}
}),
// DNS
// eslint-disable-next-line no-async-promise-executor
new Promise(async (resolve, reject) => {
;[records.cname, records.ns, records.mx, records.txt, records.soa] =
await Promise.all([
resolveDns(dns.resolveCname, url.hostname),
resolveDns(dns.resolveNs, domain),
resolveDns(dns.resolveMx, domain),
resolveDns(dns.resolveTxt, domain),
resolveDns(dns.resolveSoa, domain),
])
const dnsRecords = Object.keys(records).reduce((dns, type) => {
dns[type] = dns[type] || []
return dns
}, {})
Array.prototype.push.apply(
dns[type],
Array.isArray(records[type])
? records[type].map((value) => {
return typeof value === 'object'
? Object.values(value).join(' ')
: value
})
: [Object.values(records[type]).join(' ')]
)
await this.onDetect(url, await analyze({ dns: dnsRecords }))
return dns
}, {})
this.log(
`Probe DNS ok: (${Object.values(dnsRecords).flat().length} records)`
)
await this.onDetect(url, await analyze({ dns: dnsRecords }))
resolve()
}),
])
}
async batch(links, depth, batch = 0) {
@ -1108,8 +1139,6 @@ class Site {
if (page) {
try {
await page.close()
this.log('Page closed')
} catch (error) {
// Continue
}

@ -13,7 +13,7 @@
"software"
],
"homepage": "https://www.wappalyzer.com/",
"version": "6.8.5",
"version": "6.8.9",
"author": "Wappalyzer",
"license": "MIT",
"repository": {
@ -43,4 +43,4 @@
"dependencies": {
"puppeteer": "^5.3.0"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 0H4C2 0 0 2 0 4V28C0 30.2092 1.79086 32 4 32H28C30.2092 32 32 30.2092 32 28V4C32 1.79086 30.2092 0 28 0ZM23.4334 12.5195L22.302 11.3882L20.0392 13.6509L21.1706 14.7823L23.4334 12.5195ZM19.332 9.83258C19.7225 9.44206 20.3556 9.44206 20.7462 9.83258L22.3018 11.3882L10.9881 22.702L8.72538 20.4392L19.332 9.83258ZM14.8066 5.3072C15.1971 4.91668 15.8303 4.91668 16.2208 5.3072L17.7765 6.86284L17.7764 6.86292L18.9076 7.99414L16.6449 10.2569L15.5136 9.12566L8.7255 15.9138L9.85682 17.0451L7.59408 19.3079L6.46276 18.1765L6.46274 18.1766L4.90712 16.6209C4.51658 16.2304 4.51658 15.5972 4.90712 15.2067L14.8066 5.3072ZM26.8274 15.9137L27.9588 17.0451L25.696 19.3079L24.5646 18.1765L26.8274 15.9137ZM13.2508 24.9646L13.2509 24.9644L12.1197 23.8334L14.3825 21.5706L15.5136 22.7018L23.8574 14.358C24.2478 13.9674 24.881 13.9674 25.2716 14.358L26.8272 15.9136L16.2206 26.5202C15.8301 26.9108 15.1969 26.9108 14.8064 26.5202L13.2508 24.9646Z" fill="#2B2C30"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -4,7 +4,7 @@
"author": "Wappalyzer",
"homepage_url": "https://www.wappalyzer.com/",
"description": "Identify web technologies",
"version": "6.8.5",
"version": "6.8.9",
"default_locale": "en",
"manifest_version": 2,
"icons": {

@ -13,7 +13,7 @@
"software"
],
"homepage": "https://www.wappalyzer.com/",
"version": "6.8.5",
"version": "6.8.9",
"author": "Wappalyzer",
"license": "MIT",
"repository": {

@ -346,8 +346,8 @@
"description": "Blackbaud Luminate Online provides online fundraising and marketing automation for nonprofits.",
"icon": "Blackbaud-Luminate-Online.png",
"js": {
"don_premium_map": "",
"BLACKBAUD": ""
"BLACKBAUD": "",
"don_premium_map": ""
},
"scripts": "js/convio/modules\\.js",
"url": "/site/Donation2?.*df_id=",
@ -415,9 +415,12 @@
],
"description": "Blitz provides intelligent static page caching for creating lightning-fast sites with Craft CMS.",
"html": "<!-- Cached by Blitz on",
"implies": "Craft CMS",
"pricing": ["onetime", "low"],
"icon": "Blitz.svg",
"implies": "Craft CMS",
"pricing": [
"onetime",
"low"
],
"website": "https://putyourlightson.com/plugins/blitz"
},
"Blogger": {
@ -1261,4 +1264,4 @@
"scripts": "basket.*\\.js\\;confidence:10",
"website": "https://addyosmani.github.io/basket.js/"
}
}
}

@ -258,7 +258,7 @@
},
"scripts": [
"googlecommerce\\.com/trustedstores/api/js"
],
],
"website": "https://www.wappalyzer.com/technologies/ecommerce/cart-functionality"
},
"CartStack": {
@ -619,20 +619,20 @@
21
],
"description": "Classeh is a LMS that allows user to participate in webinars and also use LMS options like messanger,finances,homework,quiz and some extra options like sending messages and more.",
"dom": "a[href*='apps.classeh.ir'][target='_blank']",
"icon": "Classeh.svg",
"implies": [
"PHP",
"React",
"Python"
],
"dom": "a[href*='apps.classeh.ir'][target='_blank']",
"meta": {
"author": "^fanavar\\.org$"
},
"saas": true,
"pricing": [
"recurring"
],
"saas": true,
"website": "https://fanavar.org"
},
"Classy": {
@ -1524,7 +1524,7 @@
"icon": "cookieyes.svg",
"js": {
"cookieYes": ""
},
},
"scripts": [
"app\\.cookieyes\\.com/client_data/",
"cdn-cookieyes\\.com/client_data/"
@ -1637,9 +1637,9 @@
},
"scripts": [
"static\\.cloud\\.coveo\\.com"
],
],
"website": "https://www.coveo.com/"
},
},
"CoverManager": {
"cats": [
5,
@ -1848,6 +1848,10 @@
],
"description": "Customer.io is an automated messaging platform for marketers.",
"icon": "Customer.io.png",
"pricing": [
"recurring",
"mid"
],
"saas": true,
"pricing": ["recurring", "mid"],
"scripts": "assets\\.customer\\.io",
@ -1961,4 +1965,4 @@
"oss": true,
"website": "https://github.com/zloirock/core-js"
}
}
}

@ -535,9 +535,9 @@
"description": "Flow is an ecommerce platform that enables brands and retailers to sell their merchandise to customers internationally by creating localized shopping experiences.",
"icon": "Flow.png",
"js": {
"flow.cart": "",
"flow.countryPicker": "",
"flow_cart_localize": "",
"flow.cart": ""
"flow_cart_localize": ""
},
"pricing": [
"poa"
@ -631,7 +631,6 @@
17
],
"description": "Fork Awesome is now a community effort based on Font Awesome by Dave Gandy.",
"icon": "Fork Awesome.png",
"dom": {
"link[href*='fork-awesome.min.css']": {
"attributes": {
@ -644,6 +643,7 @@
}
}
},
"icon": "Fork Awesome.png",
"oss": true,
"website": "https://forkaweso.me"
},
@ -843,7 +843,7 @@
"icon": "Freshchat.svg",
"js": {
"Freshbots": ""
},
},
"pricing": [
"low",
"recurring"

@ -1,4 +1,18 @@
{
"GEOvendas": {
"cats": [
6
],
"description": "GEOvendas is an ecommerce platform with analytics, sales force, B2B and B2C products.",
"dom": "a[href*='geovendas.com.br'][target='_blank']",
"icon": "GEOvendas.svg",
"pricing": [
"mid",
"recurring"
],
"saas": true,
"website": "https://www.geovendas.com"
},
"GOV.UK Elements": {
"cats": [
66
@ -263,20 +277,6 @@
"icon": "Gentoo.png",
"website": "http://www.gentoo.org"
},
"GEOvendas": {
"cats": [
6
],
"description": "GEOvendas is an ecommerce platform with analytics, sales force, B2B and B2C products.",
"icon": "GEOvendas.svg",
"dom": "a[href*='geovendas.com.br'][target='_blank']",
"saas": true,
"pricing": [
"mid",
"recurring"
],
"website": "https://www.geovendas.com"
},
"Gerrit": {
"cats": [
47
@ -702,7 +702,7 @@
"implies": [
"Google Analytics",
"Cart Functionality"
],
],
"js": {
"gaplugins.EC": ""
},
@ -1028,11 +1028,11 @@
"description": "Grafana is a multi-platform open source analytics and interactive visualisation web application.",
"icon": "Grafana.svg",
"js": {
"grafanaBootData.settings.buildInfo['version']": "([\\d.]+)\\;version:\\1",
"__grafana_public_path__": ""
"__grafana_public_path__": "",
"grafanaBootData.settings.buildInfo['version']": "([\\d.]+)\\;version:\\1"
},
"scripts": "grafana\\..+\\.com/public/build/",
"oss": true,
"scripts": "grafana\\..+\\.com/public/build/",
"website": "https://grafana.com"
},
"Graffiti CMS": {

@ -287,10 +287,10 @@
],
"js": {
"HeroWebPluginSettings": ""
},
},
"scripts": "cdn\\.usehero\\.com",
"website": "https://www.usehero.com/"
},
},
"Heroku": {
"cats": [
62
@ -633,4 +633,4 @@
"scripts": "//hantana\\.org/widget",
"website": "https://hantana.org/"
}
}
}

@ -384,7 +384,7 @@
"oss": true,
"scripts": "instant\\.page",
"website": "https://instant.page/"
},
},
"InstantCMS": {
"cats": [
1

@ -496,15 +496,18 @@
"icon": "Justuno.png",
"js": {
"JustunoApp": ""
},
},
"pricing": [
"recurring",
"low"
],
"saas": true,
"pricing": ["recurring", "low"],
"scripts": [
"my\\.jst\\.ai",
"cdn\\.justuno\\.com"
],
"website": "https://www.justuno.com/"
},
},
"jComponent": {
"cats": [
12,
@ -656,4 +659,4 @@
"scripts": "//cdn\\.jsdelivr\\.net/",
"website": "https://www.jsdelivr.com/"
}
}
}

@ -667,4 +667,4 @@
],
"website": "https://www.kustomer.com/"
}
}
}

@ -803,6 +803,26 @@
"scripts": "loja2\\.com\\.br",
"website": "https://www.loja2.com.br"
},
"Loop54": {
"cats": [
29,
76
],
"description": "Loop54 is a ecommerce search and navigation SaaS product.",
"icon": "Loop54.png",
"cookies": {
"Loop54User": ""
},
"js": {
"Loop54.config.libVersion": "([\\d\\.]+)\\;version:\\1"
},
"saas": true,
"pricing": [
"mid",
"recurring"
],
"website": "https://www.loop54.com"
},
"Lootly": {
"cats": [
84,

@ -236,17 +236,17 @@
35
],
"description": "Mapplic is a plugin for creating interactive maps based on simple image (jpg, png) or vector (svg) files.",
"icon": "Mapplic.svg",
"dom": "div.mapplic-layer > div.mapplic-map-image",
"scripts": [
"wp-content/plugins/mapplic/",
"/include/mapplic/mapplic\\.js"
],
"saas": true,
"icon": "Mapplic.svg",
"pricing": [
"low",
"onetime"
],
"saas": true,
"scripts": [
"wp-content/plugins/mapplic/",
"/include/mapplic/mapplic\\.js"
],
"website": "https://mapplic.com"
},
"MariaDB": {
@ -735,6 +735,22 @@
],
"website": "https://metomic.io"
},
"microCMS": {
"cats": [
1
],
"description": "microCMS is a Japan-based headless CMS that enables editors and developers to build delicate sites and apps.",
"icon": "microCMS.svg",
"dom": "img[src*='.microcms-assets.io/']",
"saas": true,
"pricing": [
"freemium",
"low",
"recurring",
"payg"
],
"website": "https://microcms.io"
},
"Microsoft 365": {
"cats": [
30,
@ -1007,11 +1023,11 @@
"meta": {
"mixin_hash_id": ""
},
"saas": true,
"pricing": [
"low",
"payg"
],
"saas": true,
"website": "https://mixin.ir"
},
"Mixpanel": {
@ -1729,4 +1745,4 @@
],
"website": "https://code.google.com/p/modwsgi"
}
}
}

@ -514,19 +514,19 @@
"Periodic": {
"cats": [
72
],
],
"description": "Periodic is a white-label scheduling system.",
"icon": "Periodic.svg",
"js": {
"PeriodicSyncManager": "",
"PeriodicWave": ""
},
"scripts": "/integrations/embed/periodic-embed-resize\\.js",
"saas": true,
"pricing": [
"low",
"recurring"
],
"saas": true,
"scripts": "/integrations/embed/periodic-embed-resize\\.js",
"website": "https://periodic.is"
},
"Perl": {
@ -1346,10 +1346,10 @@
"icon": "Printful.png",
"implies": [
"Cart Functionality"
],
],
"scripts": "static\\.cdn\\.printful\\.com",
"website": "https://www.printful.com/"
},
},
"Prism": {
"cats": [
19
@ -1367,8 +1367,8 @@
1
],
"description": "Prismic is a headless CMS for Jamstack.",
"icon": "Prismic.svg",
"dom": "img[src*='images.prismic.io'",
"icon": "Prismic.svg",
"pricing": [
"low",
"freemium",
@ -1864,4 +1864,4 @@
},
"website": "http://punbb.informer.com"
}
}
}

@ -369,11 +369,15 @@
],
"js": {
"rebuyConfig": ""
},
"pricing": ["recurring", "payg", "low"],
},
"pricing": [
"recurring",
"payg",
"low"
],
"scripts": "rebuyengine\\.com",
"website": "https://rebuyengine.com/"
},
},
"Recart": {
"cats": [
32
@ -397,12 +401,18 @@
"Cart Functionality"
],
"saas": true,
"pricing": ["recurring", "payg", "mid"],
],
"pricing": [
"recurring",
"payg",
"mid"
],
"saas": true,
"scripts": [
"rechargeassets-bootstrapheroes-rechargeapps\\.netdna-ssl\\.com"
],
"website": "https://rechargepayments.com/"
},
},
"Recite Me": {
"cats": [
68
@ -489,11 +499,11 @@
"cats": [
36
],
"description": "Reddit Ads is an online advertising offering from Reddit.",
"description": "Reddit Ads is an online advertising offering from Reddit.",
"icon": "Reddit.png",
"scripts": "www\\.redditstatic\\.com",
"website": "https://advertising.reddithelp.com/"
},
},
"Redis": {
"cats": [
34
@ -1173,4 +1183,4 @@
],
"website": "https://www.google.com/recaptcha/"
}
}
}

@ -2628,11 +2628,13 @@
"icon": "Squadded.png",
"implies": [
"Cart Functionality"
],
"pricing": ["poa"],
],
"pricing": [
"poa"
],
"scripts": "static\\.squadded\\.co",
"website": "https://www.squadded.co/"
},
},
"Square": {
"cats": [
41
@ -3532,4 +3534,4 @@
},
"website": "https://styled-components.com"
}
}
}

@ -679,11 +679,11 @@
"js": {
"LS.store.url": "^.+\\.mitiendanube\\.com$"
},
"saas": true,
"pricing": [
"high",
"recurring"
],
"saas": true,
"website": "https://www.tiendanube.com"
},
"TikTok Pixel": {

@ -162,29 +162,6 @@
"icon": "Varnish.svg",
"website": "http://www.varnish-cache.org"
},
"vcita": {
"cats": [
53,
72
],
"description": "vcita is an all-in-one customer service and business management software designed for service providers.",
"icon": "vcita.svg",
"dom": "iframe[src*='www.vcita.com/widgets/']",
"js": {
"Vcita": "",
"LiveSite.btCheckout": ""
},
"scripts": [
"www\\.vcita\\.com/widgets/",
"widgets\\.vcdnita\\.com/"
],
"saas": true,
"pricing": [
"low",
"recurring"
],
"website": "https://www.vcita.com"
},
"Venmo": {
"cats": [
41
@ -578,6 +555,29 @@
},
"website": "https://www.vbulletin.com"
},
"vcita": {
"cats": [
53,
72
],
"description": "vcita is an all-in-one customer service and business management software designed for service providers.",
"dom": "iframe[src*='www.vcita.com/widgets/']",
"icon": "vcita.svg",
"js": {
"LiveSite.btCheckout": "",
"Vcita": ""
},
"pricing": [
"low",
"recurring"
],
"saas": true,
"scripts": [
"www\\.vcita\\.com/widgets/",
"widgets\\.vcdnita\\.com/"
],
"website": "https://www.vcita.com"
},
"vibecommerce": {
"cats": [
6

@ -897,14 +897,14 @@
73
],
"description": "Wufoo is an online form builder that creates forms including contact forms, online payments, online surveys and event registrations.",
"icon": "Wufoo.svg",
"dom": "a[href*='.wufoo.com/forms/'][target='_blank']",
"saas": true,
"icon": "Wufoo.svg",
"pricing": [
"freemium",
"low",
"recurring"
],
"saas": true,
"website": "https://www.wufoo.com"
},
"Wunderkind": {
@ -994,4 +994,4 @@
"url": "^https?://[^/]+\\.wpcache\\.co",
"website": "https://wpcache.co"
}
}
}

@ -163,11 +163,11 @@
"zingchart": ""
},
"oss": true,
"saas": true,
"pricing": [
"freemium",
"mid"
],
"saas": true,
"website": "https://www.zingchart.com"
},
"Zinnia": {
@ -349,15 +349,15 @@
"description": "Zyro is a website builder service by the Hostinger group.",
"icon": "Zyro.svg",
"implies": "Vue.js",
"meta":{
"meta": {
"generator": "^Zyro\\.com Website Builder$"
},
"scripts": "userapp\\.zyrosite\\.com/",
"saas": true,
"pricing": [
"low",
"recurring"
],
"saas": true,
"scripts": "userapp\\.zyrosite\\.com/",
"website": "https://zyro.com"
}
}
Loading…
Cancel
Save