@gfargo/doorman 3.10.2 → 3.11.0
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/bin/run.js +4 -4
- package/dist/bin/run.mjs +4 -4
- package/package.json +1 -1
package/dist/bin/run.js
CHANGED
|
@@ -122,11 +122,11 @@ Use --id for exact matching, or --interactive to select from a list.`))),process
|
|
|
122
122
|
${i.dryRun?"Would remove":"Removing"} ${n.length} rule(s):`)),ni(n),i.dryRun){r.log(U.default.dim(`
|
|
123
123
|
Dry run \u2014 no changes made.`));return}if(!await Eo(n.length,i.force||!1,i.all||!1)){r.info("Cancelled.");return}let s=new Set(n),a={...t,rules:o.filter(l=>!s.has(l))};a.rules.length===0&&r.warn(U.default.yellow("\u26A0\uFE0F Configuration will have no rules after removal.")),await q(a,i.config),r.success(U.default.green(`\u2714 Removed ${n.length} rule(s) from configuration`)),ni(n)}r.log(""),r.log(U.default.dim(`Run ${U.default.cyan("doorman sync")} to deploy these changes.`))}catch(e){Z(e,"removing rule")}};var si={};M(si,{builder:()=>Hr,command:()=>Gr,desc:()=>Kr,handler:()=>Zr});var P=S(require("chalk"));T();var Gr="setup",Kr="Show setup instructions and helpful links for Doorman",Hr={},Zr=async i=>{r.log(""),r.log(P.default.bold.cyan("\u{1F6AA} Doorman Setup Guide")),r.log(P.default.dim(`Everything you need to get started with firewall management
|
|
124
124
|
`)),r.log(P.default.bold("\u{1F4CB} Prerequisites")),r.log("\u2022 Vercel account with a deployed project"),r.log("\u2022 Project with Pro plan or higher (firewall feature requirement)"),r.log(`\u2022 Admin access to the project/team
|
|
125
|
-
`),r.log(P.default.bold("\u{1F50D} Finding Your Project Information")),r.log(""),r.log(P.default.cyan("1. Project ID:")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Select your project"),r.log(" \u2022 Navigate to Settings \u2192 General"),r.log(" \u2022 Copy the Project ID from the top of the page"),r.log(""),r.log(P.default.cyan("2. Team ID (if using team account):")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Click on your team name in the top-left"),r.log(" \u2022 Go to Team Settings \u2192 General"),r.log(" \u2022 Copy the Team ID"),r.log(" \u2022 Leave empty to use your Vercel default team (every account has one)"),r.log(" \u2022 Belong to more than one team? Set this explicitly, or doorman will use whichever is your default"),r.log(""),r.log(P.default.bold("\u{1F511} Creating API Token")),r.log(""),r.log(P.default.cyan("Steps:")),r.log(" 1. Visit https://vercel.com/account/tokens"),r.log(' 2. Click "Create Token"'),r.log(' 3. Name: "Doorman Firewall Management"'),r.log(" 4. Expiration: Set as needed (90 days recommended)"),r.log(" 5. Scope: Select your project or team"),r.log(" 6. Copy the generated token"),r.log(""),r.log(P.default.cyan("Required Permissions:")),r.log(" \u2022 Read access to project settings"),r.log(" \u2022 Write access to firewall configuration"),r.log(""),r.log(P.default.bold("\u{1F30D} Environment Setup")),r.log(""),r.log(P.default.cyan("Set your API token as environment variable:")),r.log(""),r.log(P.default.green("# For current session:")),r.log('export VERCEL_TOKEN="your-token-here"'),r.log(""),r.log(P.default.green("# For permanent setup (add to ~/.bashrc or ~/.zshrc):")),r.log(`echo 'export VERCEL_TOKEN="your-token-here"' >> ~/.bashrc`),r.log(""),r.log(P.default.green("# Or create a .env file in your project:")),r.log('echo "VERCEL_TOKEN=your-token-here" > .env'),r.log(""),r.log(P.default.bold("\u{1F680} Quick Start Commands")),r.log(""),r.log(P.default.cyan("1. Initialize configuration:")),r.log(" doorman init --interactive"),r.log(""),r.log(P.default.cyan("2. Validate setup:")),r.log(" doorman validate"),r.log(""),r.log(P.default.cyan("3. Check current status:")),r.log(" doorman status"),r.log(""),r.log(P.default.cyan("4. View existing rules:")),r.log(" doorman list"),r.log(""),r.log(P.default.cyan("5. Sync your configuration:")),r.log(" doorman sync"),r.log(""),r.log(P.default.bold("\u{1F527} Common Issues")),r.log(""),r.log(P.default.yellow('\u274C "Project not found" error:')),r.log(" \u2022 Double-check your Project ID"),r.log(" \u2022 Ensure your token has access to the project"),r.log(" \u2022 Verify the project has Pro plan or higher"),r.log(""),r.log(P.default.yellow('\u274C "Unauthorized" error:')),r.log(" \u2022 Check your VERCEL_TOKEN is set correctly"),r.log(" \u2022 Ensure token hasn't expired"),r.log(" \u2022 Verify token has firewall permissions"),r.log(""),r.log(P.default.yellow('\u274C "Firewall not available" error:')),r.log(" \u2022 Upgrade to Pro plan or higher"),r.log(" \u2022 Contact Vercel support if issue persists"),r.log(""),r.log(P.default.bold("\u{1F4DA} Additional Resources")),r.log(""),r.log(P.default.cyan("Documentation:")),r.log(" \u2022 Doorman Docs: https://doorman.griffen.codes"),r.log(" \u2022 Vercel Firewall: https://vercel.com/docs/security/vercel-firewall"),r.log(" \u2022 API Reference: https://vercel.com/docs/rest-api/endpoints/firewall"),r.log(""),r.log(P.default.cyan("Support:")),r.log(" \u2022 GitHub Issues: https://github.com/gfargo/doorman/issues"),r.log(" \u2022 Vercel Support: https://vercel.com/help"),r.log(""),r.log(P.default.bold("\u{1F512} Security Best Practices")),r.log(""),r.log("\u2022 Store API tokens securely (use environment variables)"),r.log("\u2022 Set token expiration dates"),r.log("\u2022 Use principle of least privilege for token scopes"),r.log("\u2022 Regularly rotate API tokens"),r.log("\u2022 Test rules in staging before production"),r.log("\u2022 Keep backups of working configurations"),r.log(""),r.log(P.default.bold.green("\u2705 Ready to get started?")),r.log(P.default.dim("Run: ")+P.default.cyan("doorman init --interactive")),r.log("")};var ai={};M(ai,{builder:()=>Qr,command:()=>Yr,desc:()=>Jr,handler:()=>Xr});var L=S(require("chalk"));T();var Yr="status",Jr="Show sync status between local and remote firewall configuration",Qr={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:Y,projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},workspaceId:{type:"string",description:"Fastly Next-Gen WAF Workspace ID (defaults to FASTLY_WORKSPACE_ID env var)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Xr=async i=>{await J({config:i.config,provider:i.provider,projectId:i.projectId,teamId:i.teamId,token:i.token,apiToken:i.apiToken,zoneId:i.zoneId,accountId:i.accountId,workspaceId:i.workspaceId,debug:i.debug,ci:i.ci,errorContext:"checking status"},async({config:e,provider:t})=>{await es(t,le(e))})};async function es(i,e){r.start("Checking sync status...");let
|
|
125
|
+
`),r.log(P.default.bold("\u{1F50D} Finding Your Project Information")),r.log(""),r.log(P.default.cyan("1. Project ID:")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Select your project"),r.log(" \u2022 Navigate to Settings \u2192 General"),r.log(" \u2022 Copy the Project ID from the top of the page"),r.log(""),r.log(P.default.cyan("2. Team ID (if using team account):")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Click on your team name in the top-left"),r.log(" \u2022 Go to Team Settings \u2192 General"),r.log(" \u2022 Copy the Team ID"),r.log(" \u2022 Leave empty to use your Vercel default team (every account has one)"),r.log(" \u2022 Belong to more than one team? Set this explicitly, or doorman will use whichever is your default"),r.log(""),r.log(P.default.bold("\u{1F511} Creating API Token")),r.log(""),r.log(P.default.cyan("Steps:")),r.log(" 1. Visit https://vercel.com/account/tokens"),r.log(' 2. Click "Create Token"'),r.log(' 3. Name: "Doorman Firewall Management"'),r.log(" 4. Expiration: Set as needed (90 days recommended)"),r.log(" 5. Scope: Select your project or team"),r.log(" 6. Copy the generated token"),r.log(""),r.log(P.default.cyan("Required Permissions:")),r.log(" \u2022 Read access to project settings"),r.log(" \u2022 Write access to firewall configuration"),r.log(""),r.log(P.default.bold("\u{1F30D} Environment Setup")),r.log(""),r.log(P.default.cyan("Set your API token as environment variable:")),r.log(""),r.log(P.default.green("# For current session:")),r.log('export VERCEL_TOKEN="your-token-here"'),r.log(""),r.log(P.default.green("# For permanent setup (add to ~/.bashrc or ~/.zshrc):")),r.log(`echo 'export VERCEL_TOKEN="your-token-here"' >> ~/.bashrc`),r.log(""),r.log(P.default.green("# Or create a .env file in your project:")),r.log('echo "VERCEL_TOKEN=your-token-here" > .env'),r.log(""),r.log(P.default.bold("\u{1F680} Quick Start Commands")),r.log(""),r.log(P.default.cyan("1. Initialize configuration:")),r.log(" doorman init --interactive"),r.log(""),r.log(P.default.cyan("2. Validate setup:")),r.log(" doorman validate"),r.log(""),r.log(P.default.cyan("3. Check current status:")),r.log(" doorman status"),r.log(""),r.log(P.default.cyan("4. View existing rules:")),r.log(" doorman list"),r.log(""),r.log(P.default.cyan("5. Sync your configuration:")),r.log(" doorman sync"),r.log(""),r.log(P.default.bold("\u{1F527} Common Issues")),r.log(""),r.log(P.default.yellow('\u274C "Project not found" error:')),r.log(" \u2022 Double-check your Project ID"),r.log(" \u2022 Ensure your token has access to the project"),r.log(" \u2022 Verify the project has Pro plan or higher"),r.log(""),r.log(P.default.yellow('\u274C "Unauthorized" error:')),r.log(" \u2022 Check your VERCEL_TOKEN is set correctly"),r.log(" \u2022 Ensure token hasn't expired"),r.log(" \u2022 Verify token has firewall permissions"),r.log(""),r.log(P.default.yellow('\u274C "Firewall not available" error:')),r.log(" \u2022 Upgrade to Pro plan or higher"),r.log(" \u2022 Contact Vercel support if issue persists"),r.log(""),r.log(P.default.bold("\u{1F4DA} Additional Resources")),r.log(""),r.log(P.default.cyan("Documentation:")),r.log(" \u2022 Doorman Docs: https://doorman.griffen.codes"),r.log(" \u2022 Vercel Firewall: https://vercel.com/docs/security/vercel-firewall"),r.log(" \u2022 API Reference: https://vercel.com/docs/rest-api/endpoints/firewall"),r.log(""),r.log(P.default.cyan("Support:")),r.log(" \u2022 GitHub Issues: https://github.com/gfargo/doorman/issues"),r.log(" \u2022 Vercel Support: https://vercel.com/help"),r.log(""),r.log(P.default.bold("\u{1F512} Security Best Practices")),r.log(""),r.log("\u2022 Store API tokens securely (use environment variables)"),r.log("\u2022 Set token expiration dates"),r.log("\u2022 Use principle of least privilege for token scopes"),r.log("\u2022 Regularly rotate API tokens"),r.log("\u2022 Test rules in staging before production"),r.log("\u2022 Keep backups of working configurations"),r.log(""),r.log(P.default.bold.green("\u2705 Ready to get started?")),r.log(P.default.dim("Run: ")+P.default.cyan("doorman init --interactive")),r.log("")};var ai={};M(ai,{builder:()=>Qr,command:()=>Yr,desc:()=>Jr,handler:()=>Xr});var L=S(require("chalk"));T();var Yr="status",Jr="Show sync status between local and remote firewall configuration",Qr={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:Y,projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},workspaceId:{type:"string",description:"Fastly Next-Gen WAF Workspace ID (defaults to FASTLY_WORKSPACE_ID env var)"},format:{alias:"f",type:"string",choices:["table","json"],description:"Output format",default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Xr=async i=>{await J({config:i.config,provider:i.provider,projectId:i.projectId,teamId:i.teamId,token:i.token,apiToken:i.apiToken,zoneId:i.zoneId,accountId:i.accountId,workspaceId:i.workspaceId,debug:i.debug,ci:i.ci,errorContext:"checking status"},async({config:e,provider:t})=>{await es(t,le(e),i)})};async function es(i,e,t){let o=t.format==="json";o||r.start("Checking sync status...");let n=await i.getChanges(e),s=i.getHealthScore(e),a=e.metadata?.version,l=n.version!==void 0&&a!==void 0&&a!==n.version,c=!n.hasChanges&&!l;if(o){r.log(JSON.stringify({provider:i.name,inSync:c,version:{local:a??null,remote:n.version??null,matches:n.version!==void 0?!l:null},rules:{toAdd:n.rulesToAdd.length,toUpdate:n.rulesToUpdate.length,toDelete:n.rulesToDelete.length},ips:{toAdd:n.ipsToAdd?.length??0,toUpdate:n.ipsToUpdate?.length??0,toDelete:n.ipsToDelete?.length??0},health:s},null,2));return}r.log(L.default.bold(`
|
|
126
126
|
\u{1F4CA} ${i.name} Sync Status Summary
|
|
127
|
-
`)),
|
|
128
|
-
`+L.default.bold("\u{1F3E5} Configuration Health Check"))
|
|
129
|
-
Recommendations:`)),s.recommendations.forEach(
|
|
127
|
+
`)),n.version!==void 0&&(r.log(`${L.default.dim("Local Version:")} ${L.default.yellow(a??"unknown")}`),r.log(`${L.default.dim("Remote Version:")} ${L.default.yellow(n.version)}`),r.log(`${L.default.dim("Version Status:")} ${l?L.default.red("Out of sync"):L.default.green("In sync")}`),r.log("")),r.log(`${L.default.dim("Rules:")}`),r.log(` ${L.default.green("+")} ${n.rulesToAdd.length} to add`),r.log(` ${L.default.cyan("~")} ${n.rulesToUpdate.length} to update`),r.log(` ${L.default.red("-")} ${n.rulesToDelete.length} to delete`),r.log(`${L.default.dim("IPs:")}`),r.log(` ${L.default.green("+")} ${n.ipsToAdd?.length??0} to add`),r.log(` ${L.default.cyan("~")} ${n.ipsToUpdate?.length??0} to update`),r.log(` ${L.default.red("-")} ${n.ipsToDelete?.length??0} to delete`),r.log(""),c?r.success(L.default.green("\u2705 Everything is in sync!")):r.warn(L.default.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")),r.log(`
|
|
128
|
+
`+L.default.bold("\u{1F3E5} Configuration Health Check")),r.log(`${L.default.dim("Score:")} ${s.score}/100 (${s.grade})`),s.issues.forEach(d=>{let u=d.severity==="error"?L.default.red("\u2717"):d.severity==="warning"?L.default.yellow("\u26A0"):L.default.dim("\u2139");r.log(` ${u} [${d.category}] ${d.message}`),d.suggestion&&r.log(` ${L.default.dim(d.suggestion)}`)}),s.recommendations.length>0&&(r.log(L.default.dim(`
|
|
129
|
+
Recommendations:`)),s.recommendations.forEach(d=>r.log(` - ${d}`)))}var di={};M(di,{builder:()=>os,command:()=>ts,desc:()=>is,handler:()=>ns});var z=S(require("chalk"));T();var ts="sync",is="Sync Vercel Firewall rules with config file",os={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:Y,projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},workspaceId:{type:"string",description:"Fastly Next-Gen WAF Workspace ID (defaults to FASTLY_WORKSPACE_ID env var)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1},allowDeletions:{type:"boolean",description:"Required together with --ci to let a non-interactive sync delete existing rules/IPs. Without it, a --ci sync that would delete anything is refused rather than applied silently.",default:!1}},ns=async i=>{await J({config:i.config,provider:i.provider,projectId:i.projectId,teamId:i.teamId,token:i.token,apiToken:i.apiToken,zoneId:i.zoneId,accountId:i.accountId,workspaceId:i.workspaceId,debug:i.debug,ci:i.ci,errorContext:"syncing firewall rules"},async e=>{await rs(e.provider,e.config,i)})};async function rs(i,e,t){let o=le(e);r.start(z.default.magenta(`Calculating ${i.name} firewall configuration changes...`));let n=await i.getChanges(o),s=o.metadata?.version,a=n.version!==void 0&&s!==n.version;if(!n.hasChanges&&!a){r.success(z.default.green("No changes detected. Firewall rules are in sync."));return}r.log(z.default.bold(`
|
|
130
130
|
Proposed Changes:
|
|
131
131
|
`)),li("Rules to add",n.rulesToAdd,z.default.green("+")),li("Rules to update",n.rulesToUpdate,z.default.cyan("~")),li("Rules to delete",n.rulesToDelete,z.default.red("-"));let l=n.ipsToAdd??[],c=n.ipsToUpdate??[],d=n.ipsToDelete??[];(l.length||c.length||d.length)&&(ci("IPs to add",l,z.default.green("+")),ci("IPs to update",c,z.default.cyan("~")),ci("IPs to delete",d,z.default.red("-"))),a&&r.log(` Remote config version: ${z.default.red(s??"unknown")} ${z.default.dim("->")} ${z.default.green(n.version)}`),r.start("Starting firewall rules sync...");let u=await i.syncRules(o,{force:t.ci,allowDeletions:t.allowDeletions});if(!u.success)throw new Error(`Sync failed: ${u.errors?.join(", ")||"unknown error"}`);r.success(z.default.green("Firewall rules sync completed successfully")),r.log(` Rules: ${u.rulesAdded} added, ${u.rulesUpdated} updated, ${u.rulesDeleted} deleted`),(u.ipsAdded||u.ipsUpdated||u.ipsDeleted)&&r.log(` IPs: ${u.ipsAdded??0} added, ${u.ipsUpdated??0} updated, ${u.ipsDeleted??0} deleted`),u.warnings?.forEach(g=>r.warn(g)),u.idRemappings?.length&&(r.info(z.default.yellow("Rule IDs were reassigned by the provider and written back to the local config:")),u.idRemappings.forEach(g=>{r.log(` - ${g.name}: ${z.default.red(g.oldId||"none")} ${z.default.dim("->")} ${z.default.green(g.newId)}`)}));let p=Pt(e,u);p.changed&&(await q(p.config,t.config),r.success(z.default.green("Updated local config with new version/metadata")))}function li(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(o=>r.log(` - ${o.name}`)))}function ci(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(o=>r.log(` - ${o.ip}${o.hostname?` (${o.hostname})`:""}`)))}var pi={};M(pi,{builder:()=>ls,command:()=>ss,desc:()=>as,handler:()=>us});var $e=S(require("chalk")),Uo=require("consola");T();var ko={metadata:{title:"Block AI Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-ai-bots-firewall-rule"},config:{rules:[{id:"rule_detect_ai_bots",name:"Detect AI Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"AI2Bot|Ai2Bot-Dolma|Amazonbot|Applebot|Applebot-Extended|Bytespider|CCBot|ChatGPT-User|Claude-Web|ClaudeBot|Diffbot|FacebookBot|FriendlyCrawler|GPTBot|Google-Extended|GoogleOther|GoogleOther-Image|GoogleOther-Video|ICC-Crawler|ImagesiftBot|Meta-ExternalAgent|Meta-ExternalFetcher|OAI-SearchBot|PerplexityBot|PetalBot|Scrapy|Timpibot|VelenPublicWebCrawler|Webzio-Extended|YouBot|anthropic-ai|cohere-ai|facebookexternalhit|img2dataset|omgili|omgilibot"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var $o={metadata:{title:"Block Bad Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-bad-bots-firewall-rule"},config:{rules:[{id:"rule_detect_bad_bots",name:"Detect Bad Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"01h4x.com|360Spider|404checker|404enemy|80legs|ADmantX|AIBOT|ALittle Client|ASPSeek|Abonti|Aboundex|Aboundexbot|Acunetix|AdsTxtCrawlerTP|AfD-Verbotsverfahren|AhrefsBot|AiHitBot|Aipbot|Alexibot|AllSubmitter|Alligator|AlphaBot|Anarchie|Anarchy|Anarchy99|Ankit|Anthill|Apexoo|Aspiegel|Asterias|Atomseobot|Attach|AwarioBot|AwarioRssBot|AwarioSmartBot|BBBike|BDCbot|BDFetch|BLEXBot|BackDoorBot|BackStreet|BackWeb|Backlink-Ceck|BacklinkCrawler|BacklinksExtendedBot|Badass|Bandit|Barkrowler|BatchFTP|Battleztar Bazinga|BetaBot|Bigfoot|Bitacle|BlackWidow|Black Hole|Blackboard|Blow|BlowFish|Boardreader|Bolt|BotALot|Brandprotect|Brandwatch|Buck|Buddy|BuiltBotTough|BuiltWith|Bullseye|BunnySlippers|BuzzSumo|Bytespider|CATExplorador|CCBot|CODE87|CSHttp|Calculon|CazoodleBot|Cegbfeieh|CensysInspect|ChatGPT-User|CheTeam|CheeseBot|CherryPicker|ChinaClaw|Chlooe|Citoid|Claritybot|ClaudeBot|Cliqzbot|Cloud mapping|Cocolyzebot|Cogentbot|Collector|Copier|CopyRightCheck|Copyscape|Cosmos|Craftbot|Crawling at Home Project|CrazyWebCrawler|Crescent|CrunchBot|Curious|Custo|CyotekWebCopy|DBLBot|DIIbot|DSearch|DTS Agent|DataCha0s|DatabaseDriverMysqli|Demon|Deusu|Devil|Digincore|DigitalPebble|Dirbuster|Disco|Discobot|Discoverybot|Dispatch|DittoSpyder|DnBCrawler-Analytics|DnyzBot|DomCopBot|DomainAppender|DomainCrawler|DomainSigmaCrawler|DomainStatsBot|Domains Project|Dotbot|Download Wonder|Dragonfly|Drip|ECCP/1.0|EMail Siphon|EMail Wolf|EasyDL|Ebingbong|Ecxi|EirGrabber|EroCrawler|Evil|Exabot|Express WebPictures|ExtLinksBot|Extractor|ExtractorPro|Extreme Picture Finder|EyeNetIE|Ezooms|FDM|FHscan|FacebookBot|FemtosearchBot|Fimap|Firefox/7.0|FlashGet|Flunky|Foobot|Freeuploader|FrontPage|Fuzz|FyberSpider|Fyrebot|G-i-g-a-b-o-t|GPTBot|GT::WWW|GalaxyBot|Genieo|GermCrawler|GetRight|GetWeb|Getintent|Gigabot|Go!Zilla|Go-Ahead-Got-It|GoZilla|Gotit|GrabNet|Grabber|Grafula|GrapeFX|GrapeshotCrawler|GridBot|HEADMasterSEO|HMView|HTMLparser|HTTP::Lite|HTTrack|Haansoft|HaosouSpider|Harvest|Havij|Heritrix|Hloader|HonoluluBot|Humanlinks|HybridBot|IDBTE4M|IDBot|IRLbot|Iblog|Id-search|IlseBot|Image Fetch|Image Sucker|ImagesiftBot|IndeedBot|Indy Library|InfoNaviRobot|InfoTekies|Information Security Team InfraSec Scanner|InfraSec Scanner|Intelliseek|InterGET|InternetMeasurement|InternetSeer|Internet Ninja|Iria|Iskanie|IstellaBot|JOC Web Spider|JamesBOT|Jbrofuzz|JennyBot|JetCar|Jetty|JikeSpider|Joomla|Jorgee|JustView|Jyxobot|Kenjin Spider|Keybot Translation-Search-Machine|Keyword Density|Kinza|Kozmosbot|LNSpiderguy|LWP::Simple|Lanshanbot|Larbin|Leap|LeechFTP|LeechGet|LexiBot|Lftp|LibWeb|Libwhisker|LieBaoFast|Lightspeedsystems|Likse|LinkScan|LinkWalker|Linkbot|LinkextractorPro|LinkpadBot|LinksManager|LinqiaMetadataDownloaderBot|LinqiaRSSBot|LinqiaScrapeBot|Lipperhey|Lipperhey Spider|Litemage_walker|Lmspider|Ltx71|MFC_Tear_Sample|MIDown tool|MIIxpc|MJ12bot|MQQBrowser|MSFrontPage|MSIECrawler|MTRobot|Mag-Net|Magnet|Mail.RU_Bot|Majestic-SEO|Majestic12|Majestic SEO|MarkMonitor|MarkWatch|Mass Downloader|Masscan|Mata Hari|MauiBot|Mb2345Browser|MeanPath Bot|Meanpathbot|Mediatoolkitbot|MegaIndex.ru|Metauri|MicroMessenger|Microsoft Data Access|Microsoft URL Control|Minefield|Mister PiX|Moblie Safari|Mojeek|Mojolicious|MolokaiBot|Morfeus Fucking Scanner|Mozlila|Mr.4x3|Msrabot|Musobot|NICErsPRO|NPbot|Name Intelligence|Nameprotect|Navroad|NearSite|Needle|Nessus|NetAnts|NetLyzer|NetMechanic|NetSpider|NetZIP|Net Vampire|Netcraft|Nettrack|Netvibes|NextGenSearchBot|Nibbler|Niki-bot|Nikto|NimbleCrawler|Nimbostratus|Ninja|Nmap|Nuclei|Nutch|Octopus|Offline Explorer|Offline Navigator|OnCrawl|OpenLinkProfiler|OpenVAS|Openfind|Openvas|OrangeBot|OrangeSpider|OutclicksBot|OutfoxBot|PECL::HTTP|PHPCrawl|POE-Component-Client-HTTP|PageAnalyzer|PageGrabber|PageScorer|PageThing.com|Page Analyzer|Pandalytics|Panscient|Papa Foto|Pavuk|PeoplePal|Petalbot|Pi-Monster|Picscout|Picsearch|PictureFinder|Piepmatz|Pimonster|Pixray|PleaseCrawl|Pockey|ProPowerBot|ProWebWalker|Probethenet|Proximic|Psbot|Pu_iN|Pump|PxBroker|PyCurl|QueryN Metasearch|Quick-Crawler|RSSingBot|Rainbot|RankActive|RankActiveLinkBot|RankFlex|RankingBot|RankingBot2|Rankivabot|RankurBot|Re-re|ReGet|RealDownload|Reaper|RebelMouse|Recorder|RedesScrapy|RepoMonkey|Ripper|RocketCrawler|Rogerbot|SBIder|SEOkicks|SEOkicks-Robot|SEOlyt|SEOlyticsCrawler|SEOprofiler|SEOstats|SISTRIX|SMTBot|SalesIntelligent|ScanAlert|Scanbot|ScoutJet|Scrapy|Screaming|ScreenerBot|ScrepyBot|Searchestate|SearchmetricsBot|Seekport|SeekportBot|SemanticJuice|Semrush|SemrushBot|SentiBot|SenutoBot|SeoSiteCheckup|SeobilityBot|Seomoz|Shodan|Siphon|SiteCheckerBotCrawler|SiteExplorer|SiteLockSpider|SiteSnagger|SiteSucker|Site Sucker|Sitebeam|Siteimprove|Sitevigil|SlySearch|SmartDownload|Snake|Snapbot|Snoopy|SocialRankIOBot|Sociscraper|Sogou web spider|Sosospider|Sottopop|SpaceBison|Spammen|SpankBot|Spanner|Spbot|Spider_Bot|Spider_Bot/3.0|Spinn3r|SputnikBot|Sqlmap|Sqlworm|Sqworm|Steeler|Stripper|Sucker|Sucuri|SuperBot|SuperHTTP|Surfbot|SurveyBot|Suzuran|Swiftbot|Szukacz|T0PHackTeam|T8Abot|Teleport|TeleportPro|Telesoft|Telesphoreo|Telesphorep|TheNomad|The Intraformant|Thumbor|TightTwatBot|TinyTestBot|Titan|Toata|Toweyabot|Tracemyfile|Trendiction|Trendictionbot|True_Robot|Turingos|Turnitin|TurnitinBot|TwengaBot|Twice|Typhoeus|URLy.Warning|URLy Warning|UnisterBot|Upflow|V-BOT|VB Project|VCI|Vacuum|Vagabondo|VelenPublicWebCrawler|VeriCiteCrawler|VidibleScraper|Virusdie|VoidEYE|Voil|Voltron|WASALive-Bot|WBSearchBot|WEBDAV|WISENutbot|WPScan|WWW-Collector-E|WWW-Mechanize|WWW::Mechanize|WWWOFFLE|Wallpapers|Wallpapers/3.0|WallpapersHD|WeSEE|WebAuto|WebBandit|WebCollage|WebCopier|WebEnhancer|WebFetch|WebFuck|WebGo IS|WebImageCollector|WebLeacher|WebPix|WebReaper|WebSauger|WebStripper|WebSucker|WebWhacker|WebZIP|Web Auto|Web Collage|Web Enhancer|Web Fetch|Web Fuck|Web Pix|Web Sauger|Web Sucker|Webalta|WebmasterWorldForumBot|Webshag|WebsiteExtractor|WebsiteQuester|Website Quester|Webster|Whack|Whacker|Whatweb|Who.is Bot|Widow|WinHTTrack|WiseGuys Robot|Wonderbot|Woobot|Wotbox|Wprecon|Xaldon WebSpider|Xaldon_WebSpider|Xenu|YaK|YoudaoBot|Zade|Zauba|Zermelo|Zeus|Zitebot|ZmEu|ZoomBot|ZoominfoBot|ZumBot|ZyBorg|adscanner|anthropic-ai|archive.org_bot|arquivo-web-crawler|arquivo.pt|autoemailspider|awario.com|backlink-check|cah.io.community|check1.exe|clark-crawler|coccocbot|cognitiveseo|cohere-ai|com.plumanalytics|crawl.sogou.com|crawler.feedback|crawler4j|dataforseo.com|dataforseobot|demandbase-bot|domainsproject.org|eCatch|evc-batch|facebookscraper|gopher|heritrix|imagesift.com|instabid|internetVista monitor|ips-agent|isitwp.com|iubenda-radar|linkdexbot|linkfluence|lwp-request|lwp-trivial|magpie-crawler|meanpathbot|mediawords|muhstik-scan|netEstate NE Crawler|oBot|omgili|openai|openai.com|page scorer|pcBrowser|plumanalytics|polaris version|probe-image-size|ripz|s1z.ru|satoristudio.net|scalaj-http|scan.lol|seobility|seocompany.store|seoscanners|seostar|serpstatbot|sexsearcher|sitechecker.pro|siteripz|sogouspider|sp_auditbot|spyfu|sysscan|tAkeOut|trendiction.com|trendiction.de|ubermetrics-technologies.com|voyagerx.com|webgains-bot|webmeup-crawler|webpros.com|webprosbot|x09Mozilla|x22Mozilla|xpymep1.exe|zauba.io|zgrab"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var _o={metadata:{title:"Block OFAC-Sanctioned Countries",reference:"https://vercel.com/templates/other/block-ofac-sanctioned-countries-firewall-rule"},config:{rules:[{id:"rule_block_traffic_from_ofac_sanctioned_countries",name:"Block traffic from OFAC-sanctioned countries",description:"Blocks traffic from OFAC-sanctioned countries and enforces a one-hour persistent block after the first violation.",conditionGroup:[{conditions:[{type:"geo_country",op:"inc",value:["SY","IR","RU","CU","KP"]}]}],action:{mitigate:{action:"deny",actionDuration:"1h"}},active:!0}]}};var So={metadata:{title:"Deny Common WordPress URLs Firewall Rule",reference:"https://vercel.com/templates/other/block-wordpress-urls-firewall-rule"},config:{rules:[{name:"Deny WordPress URLs",description:"",conditionGroup:[{conditions:[{type:"path",op:"re",value:"/(wp-admin|wp-login\\.php|xmlrpc\\.php|wp-content|wp-includes|wp-signup\\.php|wp-activate\\.php|register\\.php|wp-register\\.php)"}]}],action:{mitigate:{action:"deny"}},active:!0}]}};var ui={"ai-bots":ko,"bad-bots":$o,"block-ofac-sanctioned-countries":_o,wordpress:So};var Lo=i=>ui[i]?.config;pe();var ss="template [name]",as="Add a firewall rule template to your configuration",ls={name:{type:"string",description:"Name of the template to add"},config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},dryRun:{alias:"d",type:"boolean",description:"Preview changes without applying them",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}};function cs(i,e){let t=new Set(i.map(n=>n.name.toLowerCase())),o=e.filter(n=>t.has(n.name.toLowerCase())).map(n=>n.name);return[...new Set(o)]}var ds=()=>Object.keys(ui),us=async i=>{try{i.debug&&(r.level=Uo.LogLevels.debug),r.debug("Template command arguments:",i);let e=i.name;if(!e){let c=ds();e=await w("Select a template to add:",{type:"select",options:c,initial:c[0]})}let t=Lo(e);t||(r.error(`Template not found: ${e}`),process.exit(1)),r.debug("Template content:",t),r.start("Loading current configuration...");let o=await j(i.config,"raw"),n=o.rules||[],s=cs(n,t.rules);if(i.dryRun){r.info($e.default.cyan(`
|
|
132
132
|
Dry run - The following rules would be added:`)),r.log(JSON.stringify(t.rules,null,2)),s.length>0&&r.warn($e.default.yellow(`\u26A0\uFE0F Rule name(s) already exist in the configuration: ${s.join(", ")}`));return}if(s.length>0&&(r.warn($e.default.yellow(`\u26A0\uFE0F Rule name(s) already exist in the configuration: ${s.join(", ")}`)),!await w("Proceed anyway?",{type:"confirm",initial:!1}))){r.info("Cancelled.");return}let a={...o,rules:[...n,...t.rules]},l=Te.safeParse(a);l.success||(r.error($e.default.red("The resulting configuration would be invalid:")),l.error.errors.forEach(c=>{let d=c.path.join(".");r.error($e.default.red(` - ${d}: ${c.message}`))}),r.info($e.default.dim("Template was not applied. Fix the issues above and try again.")),process.exit(1)),r.start("Saving updated configuration..."),await q(a,i.config,{validate:!1}),r.success($e.default.green(`Successfully added template '${e}' to configuration`))}catch(e){Z(e,"adding template")}};var fi={};M(fi,{builder:()=>gs,command:()=>ps,desc:()=>fs,handler:()=>ms});var ee=S(require("chalk"));T();var ps="validate",fs="Validate firewall configuration file",gs={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},verbose:{alias:"v",type:"boolean",description:"Show detailed validation results",default:!1}},ms=async i=>{try{let e=await j(i.config,"raw"),t=Re.getInstance(),o=we(e);i.verbose&&r.start(`Validating configuration file...
|
package/dist/bin/run.mjs
CHANGED
|
@@ -122,11 +122,11 @@ Use --id for exact matching, or --interactive to select from a list.`))),process
|
|
|
122
122
|
${i.dryRun?"Would remove":"Removing"} ${n.length} rule(s):`)),Qt(n),i.dryRun){r.log(D.dim(`
|
|
123
123
|
Dry run \u2014 no changes made.`));return}if(!await oo(n.length,i.force||!1,i.all||!1)){r.info("Cancelled.");return}let s=new Set(n),a={...t,rules:o.filter(l=>!s.has(l))};a.rules.length===0&&r.warn(D.yellow("\u26A0\uFE0F Configuration will have no rules after removal.")),await j(a,i.config),r.success(D.green(`\u2714 Removed ${n.length} rule(s) from configuration`)),Qt(n)}r.log(""),r.log(D.dim(`Run ${D.cyan("doorman sync")} to deploy these changes.`))}catch(e){H(e,"removing rule")}};var ei={};z(ei,{builder:()=>Jr,command:()=>Zr,desc:()=>Yr,handler:()=>Qr});E();import k from"chalk";var Zr="setup",Yr="Show setup instructions and helpful links for Doorman",Jr={},Qr=async i=>{r.log(""),r.log(k.bold.cyan("\u{1F6AA} Doorman Setup Guide")),r.log(k.dim(`Everything you need to get started with firewall management
|
|
124
124
|
`)),r.log(k.bold("\u{1F4CB} Prerequisites")),r.log("\u2022 Vercel account with a deployed project"),r.log("\u2022 Project with Pro plan or higher (firewall feature requirement)"),r.log(`\u2022 Admin access to the project/team
|
|
125
|
-
`),r.log(k.bold("\u{1F50D} Finding Your Project Information")),r.log(""),r.log(k.cyan("1. Project ID:")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Select your project"),r.log(" \u2022 Navigate to Settings \u2192 General"),r.log(" \u2022 Copy the Project ID from the top of the page"),r.log(""),r.log(k.cyan("2. Team ID (if using team account):")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Click on your team name in the top-left"),r.log(" \u2022 Go to Team Settings \u2192 General"),r.log(" \u2022 Copy the Team ID"),r.log(" \u2022 Leave empty to use your Vercel default team (every account has one)"),r.log(" \u2022 Belong to more than one team? Set this explicitly, or doorman will use whichever is your default"),r.log(""),r.log(k.bold("\u{1F511} Creating API Token")),r.log(""),r.log(k.cyan("Steps:")),r.log(" 1. Visit https://vercel.com/account/tokens"),r.log(' 2. Click "Create Token"'),r.log(' 3. Name: "Doorman Firewall Management"'),r.log(" 4. Expiration: Set as needed (90 days recommended)"),r.log(" 5. Scope: Select your project or team"),r.log(" 6. Copy the generated token"),r.log(""),r.log(k.cyan("Required Permissions:")),r.log(" \u2022 Read access to project settings"),r.log(" \u2022 Write access to firewall configuration"),r.log(""),r.log(k.bold("\u{1F30D} Environment Setup")),r.log(""),r.log(k.cyan("Set your API token as environment variable:")),r.log(""),r.log(k.green("# For current session:")),r.log('export VERCEL_TOKEN="your-token-here"'),r.log(""),r.log(k.green("# For permanent setup (add to ~/.bashrc or ~/.zshrc):")),r.log(`echo 'export VERCEL_TOKEN="your-token-here"' >> ~/.bashrc`),r.log(""),r.log(k.green("# Or create a .env file in your project:")),r.log('echo "VERCEL_TOKEN=your-token-here" > .env'),r.log(""),r.log(k.bold("\u{1F680} Quick Start Commands")),r.log(""),r.log(k.cyan("1. Initialize configuration:")),r.log(" doorman init --interactive"),r.log(""),r.log(k.cyan("2. Validate setup:")),r.log(" doorman validate"),r.log(""),r.log(k.cyan("3. Check current status:")),r.log(" doorman status"),r.log(""),r.log(k.cyan("4. View existing rules:")),r.log(" doorman list"),r.log(""),r.log(k.cyan("5. Sync your configuration:")),r.log(" doorman sync"),r.log(""),r.log(k.bold("\u{1F527} Common Issues")),r.log(""),r.log(k.yellow('\u274C "Project not found" error:')),r.log(" \u2022 Double-check your Project ID"),r.log(" \u2022 Ensure your token has access to the project"),r.log(" \u2022 Verify the project has Pro plan or higher"),r.log(""),r.log(k.yellow('\u274C "Unauthorized" error:')),r.log(" \u2022 Check your VERCEL_TOKEN is set correctly"),r.log(" \u2022 Ensure token hasn't expired"),r.log(" \u2022 Verify token has firewall permissions"),r.log(""),r.log(k.yellow('\u274C "Firewall not available" error:')),r.log(" \u2022 Upgrade to Pro plan or higher"),r.log(" \u2022 Contact Vercel support if issue persists"),r.log(""),r.log(k.bold("\u{1F4DA} Additional Resources")),r.log(""),r.log(k.cyan("Documentation:")),r.log(" \u2022 Doorman Docs: https://doorman.griffen.codes"),r.log(" \u2022 Vercel Firewall: https://vercel.com/docs/security/vercel-firewall"),r.log(" \u2022 API Reference: https://vercel.com/docs/rest-api/endpoints/firewall"),r.log(""),r.log(k.cyan("Support:")),r.log(" \u2022 GitHub Issues: https://github.com/gfargo/doorman/issues"),r.log(" \u2022 Vercel Support: https://vercel.com/help"),r.log(""),r.log(k.bold("\u{1F512} Security Best Practices")),r.log(""),r.log("\u2022 Store API tokens securely (use environment variables)"),r.log("\u2022 Set token expiration dates"),r.log("\u2022 Use principle of least privilege for token scopes"),r.log("\u2022 Regularly rotate API tokens"),r.log("\u2022 Test rules in staging before production"),r.log("\u2022 Keep backups of working configurations"),r.log(""),r.log(k.bold.green("\u2705 Ready to get started?")),r.log(k.dim("Run: ")+k.cyan("doorman init --interactive")),r.log("")};var ti={};z(ti,{builder:()=>ts,command:()=>Xr,desc:()=>es,handler:()=>is});E();import S from"chalk";var Xr="status",es="Show sync status between local and remote firewall configuration",ts={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:Z,projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},workspaceId:{type:"string",description:"Fastly Next-Gen WAF Workspace ID (defaults to FASTLY_WORKSPACE_ID env var)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},is=async i=>{await Y({config:i.config,provider:i.provider,projectId:i.projectId,teamId:i.teamId,token:i.token,apiToken:i.apiToken,zoneId:i.zoneId,accountId:i.accountId,workspaceId:i.workspaceId,debug:i.debug,ci:i.ci,errorContext:"checking status"},async({config:e,provider:t})=>{await os(t,se(e))})};async function os(i,e){r.start("Checking sync status...");let
|
|
125
|
+
`),r.log(k.bold("\u{1F50D} Finding Your Project Information")),r.log(""),r.log(k.cyan("1. Project ID:")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Select your project"),r.log(" \u2022 Navigate to Settings \u2192 General"),r.log(" \u2022 Copy the Project ID from the top of the page"),r.log(""),r.log(k.cyan("2. Team ID (if using team account):")),r.log(" \u2022 Go to https://vercel.com/dashboard"),r.log(" \u2022 Click on your team name in the top-left"),r.log(" \u2022 Go to Team Settings \u2192 General"),r.log(" \u2022 Copy the Team ID"),r.log(" \u2022 Leave empty to use your Vercel default team (every account has one)"),r.log(" \u2022 Belong to more than one team? Set this explicitly, or doorman will use whichever is your default"),r.log(""),r.log(k.bold("\u{1F511} Creating API Token")),r.log(""),r.log(k.cyan("Steps:")),r.log(" 1. Visit https://vercel.com/account/tokens"),r.log(' 2. Click "Create Token"'),r.log(' 3. Name: "Doorman Firewall Management"'),r.log(" 4. Expiration: Set as needed (90 days recommended)"),r.log(" 5. Scope: Select your project or team"),r.log(" 6. Copy the generated token"),r.log(""),r.log(k.cyan("Required Permissions:")),r.log(" \u2022 Read access to project settings"),r.log(" \u2022 Write access to firewall configuration"),r.log(""),r.log(k.bold("\u{1F30D} Environment Setup")),r.log(""),r.log(k.cyan("Set your API token as environment variable:")),r.log(""),r.log(k.green("# For current session:")),r.log('export VERCEL_TOKEN="your-token-here"'),r.log(""),r.log(k.green("# For permanent setup (add to ~/.bashrc or ~/.zshrc):")),r.log(`echo 'export VERCEL_TOKEN="your-token-here"' >> ~/.bashrc`),r.log(""),r.log(k.green("# Or create a .env file in your project:")),r.log('echo "VERCEL_TOKEN=your-token-here" > .env'),r.log(""),r.log(k.bold("\u{1F680} Quick Start Commands")),r.log(""),r.log(k.cyan("1. Initialize configuration:")),r.log(" doorman init --interactive"),r.log(""),r.log(k.cyan("2. Validate setup:")),r.log(" doorman validate"),r.log(""),r.log(k.cyan("3. Check current status:")),r.log(" doorman status"),r.log(""),r.log(k.cyan("4. View existing rules:")),r.log(" doorman list"),r.log(""),r.log(k.cyan("5. Sync your configuration:")),r.log(" doorman sync"),r.log(""),r.log(k.bold("\u{1F527} Common Issues")),r.log(""),r.log(k.yellow('\u274C "Project not found" error:')),r.log(" \u2022 Double-check your Project ID"),r.log(" \u2022 Ensure your token has access to the project"),r.log(" \u2022 Verify the project has Pro plan or higher"),r.log(""),r.log(k.yellow('\u274C "Unauthorized" error:')),r.log(" \u2022 Check your VERCEL_TOKEN is set correctly"),r.log(" \u2022 Ensure token hasn't expired"),r.log(" \u2022 Verify token has firewall permissions"),r.log(""),r.log(k.yellow('\u274C "Firewall not available" error:')),r.log(" \u2022 Upgrade to Pro plan or higher"),r.log(" \u2022 Contact Vercel support if issue persists"),r.log(""),r.log(k.bold("\u{1F4DA} Additional Resources")),r.log(""),r.log(k.cyan("Documentation:")),r.log(" \u2022 Doorman Docs: https://doorman.griffen.codes"),r.log(" \u2022 Vercel Firewall: https://vercel.com/docs/security/vercel-firewall"),r.log(" \u2022 API Reference: https://vercel.com/docs/rest-api/endpoints/firewall"),r.log(""),r.log(k.cyan("Support:")),r.log(" \u2022 GitHub Issues: https://github.com/gfargo/doorman/issues"),r.log(" \u2022 Vercel Support: https://vercel.com/help"),r.log(""),r.log(k.bold("\u{1F512} Security Best Practices")),r.log(""),r.log("\u2022 Store API tokens securely (use environment variables)"),r.log("\u2022 Set token expiration dates"),r.log("\u2022 Use principle of least privilege for token scopes"),r.log("\u2022 Regularly rotate API tokens"),r.log("\u2022 Test rules in staging before production"),r.log("\u2022 Keep backups of working configurations"),r.log(""),r.log(k.bold.green("\u2705 Ready to get started?")),r.log(k.dim("Run: ")+k.cyan("doorman init --interactive")),r.log("")};var ti={};z(ti,{builder:()=>ts,command:()=>Xr,desc:()=>es,handler:()=>is});E();import S from"chalk";var Xr="status",es="Show sync status between local and remote firewall configuration",ts={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:Z,projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},workspaceId:{type:"string",description:"Fastly Next-Gen WAF Workspace ID (defaults to FASTLY_WORKSPACE_ID env var)"},format:{alias:"f",type:"string",choices:["table","json"],description:"Output format",default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},is=async i=>{await Y({config:i.config,provider:i.provider,projectId:i.projectId,teamId:i.teamId,token:i.token,apiToken:i.apiToken,zoneId:i.zoneId,accountId:i.accountId,workspaceId:i.workspaceId,debug:i.debug,ci:i.ci,errorContext:"checking status"},async({config:e,provider:t})=>{await os(t,se(e),i)})};async function os(i,e,t){let o=t.format==="json";o||r.start("Checking sync status...");let n=await i.getChanges(e),s=i.getHealthScore(e),a=e.metadata?.version,l=n.version!==void 0&&a!==void 0&&a!==n.version,c=!n.hasChanges&&!l;if(o){r.log(JSON.stringify({provider:i.name,inSync:c,version:{local:a??null,remote:n.version??null,matches:n.version!==void 0?!l:null},rules:{toAdd:n.rulesToAdd.length,toUpdate:n.rulesToUpdate.length,toDelete:n.rulesToDelete.length},ips:{toAdd:n.ipsToAdd?.length??0,toUpdate:n.ipsToUpdate?.length??0,toDelete:n.ipsToDelete?.length??0},health:s},null,2));return}r.log(S.bold(`
|
|
126
126
|
\u{1F4CA} ${i.name} Sync Status Summary
|
|
127
|
-
`)),
|
|
128
|
-
`+S.bold("\u{1F3E5} Configuration Health Check"))
|
|
129
|
-
Recommendations:`)),s.recommendations.forEach(
|
|
127
|
+
`)),n.version!==void 0&&(r.log(`${S.dim("Local Version:")} ${S.yellow(a??"unknown")}`),r.log(`${S.dim("Remote Version:")} ${S.yellow(n.version)}`),r.log(`${S.dim("Version Status:")} ${l?S.red("Out of sync"):S.green("In sync")}`),r.log("")),r.log(`${S.dim("Rules:")}`),r.log(` ${S.green("+")} ${n.rulesToAdd.length} to add`),r.log(` ${S.cyan("~")} ${n.rulesToUpdate.length} to update`),r.log(` ${S.red("-")} ${n.rulesToDelete.length} to delete`),r.log(`${S.dim("IPs:")}`),r.log(` ${S.green("+")} ${n.ipsToAdd?.length??0} to add`),r.log(` ${S.cyan("~")} ${n.ipsToUpdate?.length??0} to update`),r.log(` ${S.red("-")} ${n.ipsToDelete?.length??0} to delete`),r.log(""),c?r.success(S.green("\u2705 Everything is in sync!")):r.warn(S.yellow("\u26A0\uFE0F Changes detected. Run `sync` to apply changes.")),r.log(`
|
|
128
|
+
`+S.bold("\u{1F3E5} Configuration Health Check")),r.log(`${S.dim("Score:")} ${s.score}/100 (${s.grade})`),s.issues.forEach(d=>{let u=d.severity==="error"?S.red("\u2717"):d.severity==="warning"?S.yellow("\u26A0"):S.dim("\u2139");r.log(` ${u} [${d.category}] ${d.message}`),d.suggestion&&r.log(` ${S.dim(d.suggestion)}`)}),s.recommendations.length>0&&(r.log(S.dim(`
|
|
129
|
+
Recommendations:`)),s.recommendations.forEach(d=>r.log(` - ${d}`)))}var ni={};z(ni,{builder:()=>ss,command:()=>ns,desc:()=>rs,handler:()=>as});E();import q from"chalk";var ns="sync",rs="Sync Vercel Firewall rules with config file",ss={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:Z,projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},workspaceId:{type:"string",description:"Fastly Next-Gen WAF Workspace ID (defaults to FASTLY_WORKSPACE_ID env var)"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1},allowDeletions:{type:"boolean",description:"Required together with --ci to let a non-interactive sync delete existing rules/IPs. Without it, a --ci sync that would delete anything is refused rather than applied silently.",default:!1}},as=async i=>{await Y({config:i.config,provider:i.provider,projectId:i.projectId,teamId:i.teamId,token:i.token,apiToken:i.apiToken,zoneId:i.zoneId,accountId:i.accountId,workspaceId:i.workspaceId,debug:i.debug,ci:i.ci,errorContext:"syncing firewall rules"},async e=>{await ls(e.provider,e.config,i)})};async function ls(i,e,t){let o=se(e);r.start(q.magenta(`Calculating ${i.name} firewall configuration changes...`));let n=await i.getChanges(o),s=o.metadata?.version,a=n.version!==void 0&&s!==n.version;if(!n.hasChanges&&!a){r.success(q.green("No changes detected. Firewall rules are in sync."));return}r.log(q.bold(`
|
|
130
130
|
Proposed Changes:
|
|
131
131
|
`)),ii("Rules to add",n.rulesToAdd,q.green("+")),ii("Rules to update",n.rulesToUpdate,q.cyan("~")),ii("Rules to delete",n.rulesToDelete,q.red("-"));let l=n.ipsToAdd??[],c=n.ipsToUpdate??[],d=n.ipsToDelete??[];(l.length||c.length||d.length)&&(oi("IPs to add",l,q.green("+")),oi("IPs to update",c,q.cyan("~")),oi("IPs to delete",d,q.red("-"))),a&&r.log(` Remote config version: ${q.red(s??"unknown")} ${q.dim("->")} ${q.green(n.version)}`),r.start("Starting firewall rules sync...");let u=await i.syncRules(o,{force:t.ci,allowDeletions:t.allowDeletions});if(!u.success)throw new Error(`Sync failed: ${u.errors?.join(", ")||"unknown error"}`);r.success(q.green("Firewall rules sync completed successfully")),r.log(` Rules: ${u.rulesAdded} added, ${u.rulesUpdated} updated, ${u.rulesDeleted} deleted`),(u.ipsAdded||u.ipsUpdated||u.ipsDeleted)&&r.log(` IPs: ${u.ipsAdded??0} added, ${u.ipsUpdated??0} updated, ${u.ipsDeleted??0} deleted`),u.warnings?.forEach(g=>r.warn(g)),u.idRemappings?.length&&(r.info(q.yellow("Rule IDs were reassigned by the provider and written back to the local config:")),u.idRemappings.forEach(g=>{r.log(` - ${g.name}: ${q.red(g.oldId||"none")} ${q.dim("->")} ${q.green(g.newId)}`)}));let p=Rt(e,u);p.changed&&(await j(p.config,t.config),r.success(q.green("Updated local config with new version/metadata")))}function ii(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(o=>r.log(` - ${o.name}`)))}function oi(i,e,t){e.length!==0&&(r.log(` ${t} ${i} (${e.length}):`),e.forEach(o=>r.log(` - ${o.ip}${o.hostname?` (${o.hostname})`:""}`)))}var si={};z(si,{builder:()=>ps,command:()=>ds,desc:()=>us,handler:()=>ms});E();import De from"chalk";import{LogLevels as cs}from"consola";var so={metadata:{title:"Block AI Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-ai-bots-firewall-rule"},config:{rules:[{id:"rule_detect_ai_bots",name:"Detect AI Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"AI2Bot|Ai2Bot-Dolma|Amazonbot|Applebot|Applebot-Extended|Bytespider|CCBot|ChatGPT-User|Claude-Web|ClaudeBot|Diffbot|FacebookBot|FriendlyCrawler|GPTBot|Google-Extended|GoogleOther|GoogleOther-Image|GoogleOther-Video|ICC-Crawler|ImagesiftBot|Meta-ExternalAgent|Meta-ExternalFetcher|OAI-SearchBot|PerplexityBot|PetalBot|Scrapy|Timpibot|VelenPublicWebCrawler|Webzio-Extended|YouBot|anthropic-ai|cohere-ai|facebookexternalhit|img2dataset|omgili|omgilibot"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var ao={metadata:{title:"Block Bad Bots Firewall Rule",reference:"https://vercel.com/templates/other/block-bad-bots-firewall-rule"},config:{rules:[{id:"rule_detect_bad_bots",name:"Detect Bad Bots",description:"",conditionGroup:[{conditions:[{type:"user_agent",op:"re",value:"01h4x.com|360Spider|404checker|404enemy|80legs|ADmantX|AIBOT|ALittle Client|ASPSeek|Abonti|Aboundex|Aboundexbot|Acunetix|AdsTxtCrawlerTP|AfD-Verbotsverfahren|AhrefsBot|AiHitBot|Aipbot|Alexibot|AllSubmitter|Alligator|AlphaBot|Anarchie|Anarchy|Anarchy99|Ankit|Anthill|Apexoo|Aspiegel|Asterias|Atomseobot|Attach|AwarioBot|AwarioRssBot|AwarioSmartBot|BBBike|BDCbot|BDFetch|BLEXBot|BackDoorBot|BackStreet|BackWeb|Backlink-Ceck|BacklinkCrawler|BacklinksExtendedBot|Badass|Bandit|Barkrowler|BatchFTP|Battleztar Bazinga|BetaBot|Bigfoot|Bitacle|BlackWidow|Black Hole|Blackboard|Blow|BlowFish|Boardreader|Bolt|BotALot|Brandprotect|Brandwatch|Buck|Buddy|BuiltBotTough|BuiltWith|Bullseye|BunnySlippers|BuzzSumo|Bytespider|CATExplorador|CCBot|CODE87|CSHttp|Calculon|CazoodleBot|Cegbfeieh|CensysInspect|ChatGPT-User|CheTeam|CheeseBot|CherryPicker|ChinaClaw|Chlooe|Citoid|Claritybot|ClaudeBot|Cliqzbot|Cloud mapping|Cocolyzebot|Cogentbot|Collector|Copier|CopyRightCheck|Copyscape|Cosmos|Craftbot|Crawling at Home Project|CrazyWebCrawler|Crescent|CrunchBot|Curious|Custo|CyotekWebCopy|DBLBot|DIIbot|DSearch|DTS Agent|DataCha0s|DatabaseDriverMysqli|Demon|Deusu|Devil|Digincore|DigitalPebble|Dirbuster|Disco|Discobot|Discoverybot|Dispatch|DittoSpyder|DnBCrawler-Analytics|DnyzBot|DomCopBot|DomainAppender|DomainCrawler|DomainSigmaCrawler|DomainStatsBot|Domains Project|Dotbot|Download Wonder|Dragonfly|Drip|ECCP/1.0|EMail Siphon|EMail Wolf|EasyDL|Ebingbong|Ecxi|EirGrabber|EroCrawler|Evil|Exabot|Express WebPictures|ExtLinksBot|Extractor|ExtractorPro|Extreme Picture Finder|EyeNetIE|Ezooms|FDM|FHscan|FacebookBot|FemtosearchBot|Fimap|Firefox/7.0|FlashGet|Flunky|Foobot|Freeuploader|FrontPage|Fuzz|FyberSpider|Fyrebot|G-i-g-a-b-o-t|GPTBot|GT::WWW|GalaxyBot|Genieo|GermCrawler|GetRight|GetWeb|Getintent|Gigabot|Go!Zilla|Go-Ahead-Got-It|GoZilla|Gotit|GrabNet|Grabber|Grafula|GrapeFX|GrapeshotCrawler|GridBot|HEADMasterSEO|HMView|HTMLparser|HTTP::Lite|HTTrack|Haansoft|HaosouSpider|Harvest|Havij|Heritrix|Hloader|HonoluluBot|Humanlinks|HybridBot|IDBTE4M|IDBot|IRLbot|Iblog|Id-search|IlseBot|Image Fetch|Image Sucker|ImagesiftBot|IndeedBot|Indy Library|InfoNaviRobot|InfoTekies|Information Security Team InfraSec Scanner|InfraSec Scanner|Intelliseek|InterGET|InternetMeasurement|InternetSeer|Internet Ninja|Iria|Iskanie|IstellaBot|JOC Web Spider|JamesBOT|Jbrofuzz|JennyBot|JetCar|Jetty|JikeSpider|Joomla|Jorgee|JustView|Jyxobot|Kenjin Spider|Keybot Translation-Search-Machine|Keyword Density|Kinza|Kozmosbot|LNSpiderguy|LWP::Simple|Lanshanbot|Larbin|Leap|LeechFTP|LeechGet|LexiBot|Lftp|LibWeb|Libwhisker|LieBaoFast|Lightspeedsystems|Likse|LinkScan|LinkWalker|Linkbot|LinkextractorPro|LinkpadBot|LinksManager|LinqiaMetadataDownloaderBot|LinqiaRSSBot|LinqiaScrapeBot|Lipperhey|Lipperhey Spider|Litemage_walker|Lmspider|Ltx71|MFC_Tear_Sample|MIDown tool|MIIxpc|MJ12bot|MQQBrowser|MSFrontPage|MSIECrawler|MTRobot|Mag-Net|Magnet|Mail.RU_Bot|Majestic-SEO|Majestic12|Majestic SEO|MarkMonitor|MarkWatch|Mass Downloader|Masscan|Mata Hari|MauiBot|Mb2345Browser|MeanPath Bot|Meanpathbot|Mediatoolkitbot|MegaIndex.ru|Metauri|MicroMessenger|Microsoft Data Access|Microsoft URL Control|Minefield|Mister PiX|Moblie Safari|Mojeek|Mojolicious|MolokaiBot|Morfeus Fucking Scanner|Mozlila|Mr.4x3|Msrabot|Musobot|NICErsPRO|NPbot|Name Intelligence|Nameprotect|Navroad|NearSite|Needle|Nessus|NetAnts|NetLyzer|NetMechanic|NetSpider|NetZIP|Net Vampire|Netcraft|Nettrack|Netvibes|NextGenSearchBot|Nibbler|Niki-bot|Nikto|NimbleCrawler|Nimbostratus|Ninja|Nmap|Nuclei|Nutch|Octopus|Offline Explorer|Offline Navigator|OnCrawl|OpenLinkProfiler|OpenVAS|Openfind|Openvas|OrangeBot|OrangeSpider|OutclicksBot|OutfoxBot|PECL::HTTP|PHPCrawl|POE-Component-Client-HTTP|PageAnalyzer|PageGrabber|PageScorer|PageThing.com|Page Analyzer|Pandalytics|Panscient|Papa Foto|Pavuk|PeoplePal|Petalbot|Pi-Monster|Picscout|Picsearch|PictureFinder|Piepmatz|Pimonster|Pixray|PleaseCrawl|Pockey|ProPowerBot|ProWebWalker|Probethenet|Proximic|Psbot|Pu_iN|Pump|PxBroker|PyCurl|QueryN Metasearch|Quick-Crawler|RSSingBot|Rainbot|RankActive|RankActiveLinkBot|RankFlex|RankingBot|RankingBot2|Rankivabot|RankurBot|Re-re|ReGet|RealDownload|Reaper|RebelMouse|Recorder|RedesScrapy|RepoMonkey|Ripper|RocketCrawler|Rogerbot|SBIder|SEOkicks|SEOkicks-Robot|SEOlyt|SEOlyticsCrawler|SEOprofiler|SEOstats|SISTRIX|SMTBot|SalesIntelligent|ScanAlert|Scanbot|ScoutJet|Scrapy|Screaming|ScreenerBot|ScrepyBot|Searchestate|SearchmetricsBot|Seekport|SeekportBot|SemanticJuice|Semrush|SemrushBot|SentiBot|SenutoBot|SeoSiteCheckup|SeobilityBot|Seomoz|Shodan|Siphon|SiteCheckerBotCrawler|SiteExplorer|SiteLockSpider|SiteSnagger|SiteSucker|Site Sucker|Sitebeam|Siteimprove|Sitevigil|SlySearch|SmartDownload|Snake|Snapbot|Snoopy|SocialRankIOBot|Sociscraper|Sogou web spider|Sosospider|Sottopop|SpaceBison|Spammen|SpankBot|Spanner|Spbot|Spider_Bot|Spider_Bot/3.0|Spinn3r|SputnikBot|Sqlmap|Sqlworm|Sqworm|Steeler|Stripper|Sucker|Sucuri|SuperBot|SuperHTTP|Surfbot|SurveyBot|Suzuran|Swiftbot|Szukacz|T0PHackTeam|T8Abot|Teleport|TeleportPro|Telesoft|Telesphoreo|Telesphorep|TheNomad|The Intraformant|Thumbor|TightTwatBot|TinyTestBot|Titan|Toata|Toweyabot|Tracemyfile|Trendiction|Trendictionbot|True_Robot|Turingos|Turnitin|TurnitinBot|TwengaBot|Twice|Typhoeus|URLy.Warning|URLy Warning|UnisterBot|Upflow|V-BOT|VB Project|VCI|Vacuum|Vagabondo|VelenPublicWebCrawler|VeriCiteCrawler|VidibleScraper|Virusdie|VoidEYE|Voil|Voltron|WASALive-Bot|WBSearchBot|WEBDAV|WISENutbot|WPScan|WWW-Collector-E|WWW-Mechanize|WWW::Mechanize|WWWOFFLE|Wallpapers|Wallpapers/3.0|WallpapersHD|WeSEE|WebAuto|WebBandit|WebCollage|WebCopier|WebEnhancer|WebFetch|WebFuck|WebGo IS|WebImageCollector|WebLeacher|WebPix|WebReaper|WebSauger|WebStripper|WebSucker|WebWhacker|WebZIP|Web Auto|Web Collage|Web Enhancer|Web Fetch|Web Fuck|Web Pix|Web Sauger|Web Sucker|Webalta|WebmasterWorldForumBot|Webshag|WebsiteExtractor|WebsiteQuester|Website Quester|Webster|Whack|Whacker|Whatweb|Who.is Bot|Widow|WinHTTrack|WiseGuys Robot|Wonderbot|Woobot|Wotbox|Wprecon|Xaldon WebSpider|Xaldon_WebSpider|Xenu|YaK|YoudaoBot|Zade|Zauba|Zermelo|Zeus|Zitebot|ZmEu|ZoomBot|ZoominfoBot|ZumBot|ZyBorg|adscanner|anthropic-ai|archive.org_bot|arquivo-web-crawler|arquivo.pt|autoemailspider|awario.com|backlink-check|cah.io.community|check1.exe|clark-crawler|coccocbot|cognitiveseo|cohere-ai|com.plumanalytics|crawl.sogou.com|crawler.feedback|crawler4j|dataforseo.com|dataforseobot|demandbase-bot|domainsproject.org|eCatch|evc-batch|facebookscraper|gopher|heritrix|imagesift.com|instabid|internetVista monitor|ips-agent|isitwp.com|iubenda-radar|linkdexbot|linkfluence|lwp-request|lwp-trivial|magpie-crawler|meanpathbot|mediawords|muhstik-scan|netEstate NE Crawler|oBot|omgili|openai|openai.com|page scorer|pcBrowser|plumanalytics|polaris version|probe-image-size|ripz|s1z.ru|satoristudio.net|scalaj-http|scan.lol|seobility|seocompany.store|seoscanners|seostar|serpstatbot|sexsearcher|sitechecker.pro|siteripz|sogouspider|sp_auditbot|spyfu|sysscan|tAkeOut|trendiction.com|trendiction.de|ubermetrics-technologies.com|voyagerx.com|webgains-bot|webmeup-crawler|webpros.com|webprosbot|x09Mozilla|x22Mozilla|xpymep1.exe|zauba.io|zgrab"}]}],action:{mitigate:{action:"log"}},active:!0}]}};var lo={metadata:{title:"Block OFAC-Sanctioned Countries",reference:"https://vercel.com/templates/other/block-ofac-sanctioned-countries-firewall-rule"},config:{rules:[{id:"rule_block_traffic_from_ofac_sanctioned_countries",name:"Block traffic from OFAC-sanctioned countries",description:"Blocks traffic from OFAC-sanctioned countries and enforces a one-hour persistent block after the first violation.",conditionGroup:[{conditions:[{type:"geo_country",op:"inc",value:["SY","IR","RU","CU","KP"]}]}],action:{mitigate:{action:"deny",actionDuration:"1h"}},active:!0}]}};var co={metadata:{title:"Deny Common WordPress URLs Firewall Rule",reference:"https://vercel.com/templates/other/block-wordpress-urls-firewall-rule"},config:{rules:[{name:"Deny WordPress URLs",description:"",conditionGroup:[{conditions:[{type:"path",op:"re",value:"/(wp-admin|wp-login\\.php|xmlrpc\\.php|wp-content|wp-includes|wp-signup\\.php|wp-activate\\.php|register\\.php|wp-register\\.php)"}]}],action:{mitigate:{action:"deny"}},active:!0}]}};var ri={"ai-bots":so,"bad-bots":ao,"block-ofac-sanctioned-countries":lo,wordpress:co};var uo=i=>ri[i]?.config;le();var ds="template [name]",us="Add a firewall rule template to your configuration",ps={name:{type:"string",description:"Name of the template to add"},config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},dryRun:{alias:"d",type:"boolean",description:"Preview changes without applying them",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1}};function fs(i,e){let t=new Set(i.map(n=>n.name.toLowerCase())),o=e.filter(n=>t.has(n.name.toLowerCase())).map(n=>n.name);return[...new Set(o)]}var gs=()=>Object.keys(ri),ms=async i=>{try{i.debug&&(r.level=cs.debug),r.debug("Template command arguments:",i);let e=i.name;if(!e){let c=gs();e=await w("Select a template to add:",{type:"select",options:c,initial:c[0]})}let t=uo(e);t||(r.error(`Template not found: ${e}`),process.exit(1)),r.debug("Template content:",t),r.start("Loading current configuration...");let o=await W(i.config,"raw"),n=o.rules||[],s=fs(n,t.rules);if(i.dryRun){r.info(De.cyan(`
|
|
132
132
|
Dry run - The following rules would be added:`)),r.log(JSON.stringify(t.rules,null,2)),s.length>0&&r.warn(De.yellow(`\u26A0\uFE0F Rule name(s) already exist in the configuration: ${s.join(", ")}`));return}if(s.length>0&&(r.warn(De.yellow(`\u26A0\uFE0F Rule name(s) already exist in the configuration: ${s.join(", ")}`)),!await w("Proceed anyway?",{type:"confirm",initial:!1}))){r.info("Cancelled.");return}let a={...o,rules:[...n,...t.rules]},l=ve.safeParse(a);l.success||(r.error(De.red("The resulting configuration would be invalid:")),l.error.errors.forEach(c=>{let d=c.path.join(".");r.error(De.red(` - ${d}: ${c.message}`))}),r.info(De.dim("Template was not applied. Fix the issues above and try again.")),process.exit(1)),r.start("Saving updated configuration..."),await j(a,i.config,{validate:!1}),r.success(De.green(`Successfully added template '${e}' to configuration`))}catch(e){H(e,"adding template")}};var ai={};z(ai,{builder:()=>ws,command:()=>hs,desc:()=>ys,handler:()=>Is});E();import X from"chalk";var hs="validate",ys="Validate firewall configuration file",ws={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},verbose:{alias:"v",type:"boolean",description:"Show detailed validation results",default:!1}},Is=async i=>{try{let e=await W(i.config,"raw"),t=ye.getInstance(),o=me(e);i.verbose&&r.start(`Validating configuration file...
|