Compare commits

..

2 Commits

Author SHA1 Message Date
Kelly
f25bebf6ee feat: Add wildcard support for trusted domains
Add *.cannaiq.co and *.cannabrands.app to trusted domains list.
Updated isTrustedDomain() to recognize *.domain.com as wildcard
that matches the base domain and any subdomain.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 15:29:23 -07:00
Kelly
22dad6d0fc feat: Add wildcard trusted origins for cannaiq.co and cannabrands.app
Add *.cannaiq.co and *.cannabrands.app patterns to both:
- auth/middleware.ts (admin routes)
- public-api.ts (consumer /api/v1/* routes)

This allows any subdomain of these domains to access the API without
requiring an API key.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 15:25:04 -07:00
5 changed files with 60 additions and 23 deletions

View File

@@ -89,11 +89,7 @@ steps:
from_secret: registry_password
platforms: linux/amd64
provenance: false
build_args:
- APP_BUILD_VERSION=${CI_COMMIT_SHA:0:8}
- APP_GIT_SHA=${CI_COMMIT_SHA}
- APP_BUILD_TIME=${CI_PIPELINE_CREATED}
- CONTAINER_IMAGE_TAG=${CI_COMMIT_SHA:0:8}
build_args: APP_BUILD_VERSION=${CI_COMMIT_SHA:0:8},APP_GIT_SHA=${CI_COMMIT_SHA},APP_BUILD_TIME=${CI_PIPELINE_CREATED},CONTAINER_IMAGE_TAG=${CI_COMMIT_SHA:0:8}
depends_on: []
when:
branch: master

View File

@@ -32,6 +32,7 @@ const TRUSTED_ORIGINS = [
// Pattern-based trusted origins (wildcards)
const TRUSTED_ORIGIN_PATTERNS = [
/^https:\/\/.*\.cannabrands\.app$/, // *.cannabrands.app
/^https:\/\/.*\.cannaiq\.co$/, // *.cannaiq.co
];
// Trusted IPs for internal pod-to-pod communication

View File

@@ -5,8 +5,8 @@ import { Request, Response, NextFunction } from 'express';
* These are our own frontends that should have unrestricted access.
*/
const TRUSTED_DOMAINS = [
'cannaiq.co',
'www.cannaiq.co',
'*.cannaiq.co',
'*.cannabrands.app',
'findagram.co',
'www.findagram.co',
'findadispo.com',
@@ -32,6 +32,24 @@ function extractDomain(header: string): string | null {
}
}
/**
* Checks if a domain matches any trusted domain (supports *.domain.com wildcards)
*/
function isTrustedDomain(domain: string): boolean {
for (const trusted of TRUSTED_DOMAINS) {
if (trusted.startsWith('*.')) {
// Wildcard: *.example.com matches example.com and any subdomain
const baseDomain = trusted.slice(2);
if (domain === baseDomain || domain.endsWith('.' + baseDomain)) {
return true;
}
} else if (domain === trusted) {
return true;
}
}
return false;
}
/**
* Checks if the request comes from a trusted domain
*/
@@ -42,7 +60,7 @@ function isRequestFromTrustedDomain(req: Request): boolean {
// Check Origin header first (preferred for CORS requests)
if (origin) {
const domain = extractDomain(origin);
if (domain && TRUSTED_DOMAINS.includes(domain)) {
if (domain && isTrustedDomain(domain)) {
return true;
}
}
@@ -50,7 +68,7 @@ function isRequestFromTrustedDomain(req: Request): boolean {
// Fallback to Referer header
if (referer) {
const domain = extractDomain(referer);
if (domain && TRUSTED_DOMAINS.includes(domain)) {
if (domain && isTrustedDomain(domain)) {
return true;
}
}

View File

@@ -130,6 +130,12 @@ const CONSUMER_TRUSTED_ORIGINS = [
'http://localhost:3002',
];
// Wildcard trusted origin patterns (*.domain.com)
const CONSUMER_TRUSTED_PATTERNS = [
/^https:\/\/([a-z0-9-]+\.)?cannaiq\.co$/,
/^https:\/\/([a-z0-9-]+\.)?cannabrands\.app$/,
];
// Trusted IPs for local development (bypass API key auth)
const TRUSTED_IPS = ['127.0.0.1', '::1', '::ffff:127.0.0.1'];
@@ -150,8 +156,17 @@ function isConsumerTrustedRequest(req: Request): boolean {
return true;
}
const origin = req.headers.origin;
if (origin && CONSUMER_TRUSTED_ORIGINS.includes(origin)) {
return true;
if (origin) {
// Check exact matches
if (CONSUMER_TRUSTED_ORIGINS.includes(origin)) {
return true;
}
// Check wildcard patterns
for (const pattern of CONSUMER_TRUSTED_PATTERNS) {
if (pattern.test(origin)) {
return true;
}
}
}
const referer = req.headers.referer;
if (referer) {
@@ -160,6 +175,18 @@ function isConsumerTrustedRequest(req: Request): boolean {
return true;
}
}
// Check wildcard patterns against referer origin
try {
const refererUrl = new URL(referer);
const refererOrigin = refererUrl.origin;
for (const pattern of CONSUMER_TRUSTED_PATTERNS) {
if (pattern.test(refererOrigin)) {
return true;
}
}
} catch {
// Invalid referer URL, ignore
}
}
return false;
}

View File

@@ -46,17 +46,14 @@ class CannaIQ_Menus_Plugin {
// Initialize plugin
load_plugin_textdomain('cannaiq-menus', false, dirname(plugin_basename(__FILE__)) . '/languages');
// Register shortcodes - primary CannaIQ shortcodes
// Register shortcodes
add_shortcode('cannaiq_products', [$this, 'products_shortcode']);
add_shortcode('cannaiq_product', [$this, 'single_product_shortcode']);
// DEPRECATED: Legacy shortcode aliases for backward compatibility only
// These allow sites that used the old plugin names to continue working
// New implementations should use [cannaiq_products] and [cannaiq_product]
add_shortcode('crawlsy_products', [$this, 'products_shortcode']); // deprecated
add_shortcode('crawlsy_product', [$this, 'single_product_shortcode']); // deprecated
add_shortcode('dutchie_products', [$this, 'products_shortcode']); // deprecated
add_shortcode('dutchie_product', [$this, 'single_product_shortcode']); // deprecated
// Legacy shortcode support (backward compatibility)
add_shortcode('crawlsy_products', [$this, 'products_shortcode']);
add_shortcode('crawlsy_product', [$this, 'single_product_shortcode']);
add_shortcode('dutchie_products', [$this, 'products_shortcode']);
add_shortcode('dutchie_product', [$this, 'single_product_shortcode']);
}
/**
@@ -117,9 +114,7 @@ class CannaIQ_Menus_Plugin {
public function register_settings() {
register_setting('cannaiq_menus_settings', 'cannaiq_api_token');
// MIGRATION: Auto-migrate API tokens from old plugin versions
// This runs once - if user had crawlsy or dutchie plugin, their token is preserved
// Can be removed in a future major version once all users have migrated
// Migrate old settings if they exist
$old_crawlsy_token = get_option('crawlsy_api_token');
$old_dutchie_token = get_option('dutchie_api_token');