DIYgod/RSSHub · warning · ConfigNotFoundError

This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN

Error message

This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.

What it means

18comic routes let the caller pick the source domain via a path parameter. Because the site rotates through many mirrors (some untrusted), the route only accepts a hard-coded allow-list of domains unless the operator explicitly opts in via config.feature.allow_user_supply_unsafe_domain. ConfigNotFoundError is thrown when an off-list domain is supplied and the flag is false.

Source

Thrown at lib/routes/18comic/utils.ts:22

import { config } from '@/config';
import ConfigNotFoundError from '@/errors/types/config-not-found';
import type { DataItem } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import md5 from '@/utils/md5';
import { parseDate } from '@/utils/parse-date';

import { renderDescription } from './templates/description';

const defaultDomain = 'jmcomic1.me';
// list of address: https://jmcomic2.bet
const allowDomain = new Set(['18comic.vip', '18comic.org', 'jmcomic.me', 'jmcomic1.me', 'jm-comic3.art', 'jm-comic.club', 'jm-comic2.ark']);

const apiDomain = 'www.cdnhth.cc';

const getRootUrl = (domain) => {
    if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(domain)) {
        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
    }

    return `https://${domain}`;
};

const apiMapCategory = (category) => {
    switch (category) {
        case 'another':
            return '其他漫畫';
        case 'doujin':
            return '同人';
        case 'hanman':
            return '韓漫';
        case 'meiman':
            return '美漫';
        case 'short':
            return '短篇';
        case 'single':

View on GitHub (pinned to bed535e087)

Solutions

  1. Use one of the allow-listed domains verbatim (see allowDomain in lib/routes/18comic/utils.ts).
  2. If you run your own instance and accept the SSRF/trust risk, set environment variable ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true.
  3. Open an issue / PR to add the new canonical domain to allowDomain so others benefit.

Example fix

# before (env)
# nothing set, calling /18comic/<untrusted-mirror>/...
# after
ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true
# OR keep default and call:
/18comic/jmcomic1.me/...
Defensive patterns

Strategy: validation

Validate before calling

import { config } from '@/config';
const ALLOW_DOMAIN_18COMIC = new Set(['18comic.vip','18comic.org','jmcomic.me','jmcomic1.me','jm-comic3.art','jm-comic.club','jm-comic2.ark']);
function safe18comicDomain(domain) {
  return ALLOW_DOMAIN_18COMIC.has(domain) || config.feature.allow_user_supply_unsafe_domain === true;
}

Type guard

function isAllowed18comicDomain(d, allowAny): boolean {
  return allowAny === true || ALLOW_DOMAIN_18COMIC.has(d);
}

Prevention

When it happens

Trigger: Calling any /18comic/... route with a `domain` parameter not in {18comic.vip, 18comic.org, jmcomic.me, jmcomic1.me, jm-comic3.art, jm-comic.club, jm-comic2.ark} while ALLOW_USER_SUPPLY_UNSAFE_DOMAIN is unset/false.

Common situations: Hardcoding a mirror found on a forum that isn't yet in the allow-list; the site rotated domains and the allow-list is stale; running a self-hosted RSSHub with the safe default and pasting the newest mirror.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/5aef06e365d16648. Report an issue: GitHub.