huawei-cloud-waf-policy-manage

v2026.09.24

Import WAF protection policies from JSON files exported by huawei-cloud-waf-policy-query, supporting creation of new policies or overwriting existing ones. Triggers include: import WAF policy, create policy from JSON, overwrite WAF policy, JSON import protection policy

GitHub
安装命令
npx skhub add huaweicloud/huawei-cloud-waf-policy-manage
Markdown
SKILL.md

WAF Protection Policy JSON Import

Overview

This Skill is specifically designed to import WAF protection policies from JSON files exported by huawei-cloud-waf-policy-query, supporting:

  • Create New Policy — Create an entirely new protection policy and all its rules based on a JSON file
  • Overwrite Existing Policy — Update an existing policy's configuration and rebuild all rules
  • Automatic Mapping — Automatically map JSON fields to corresponding CLI parameters
  • Batch Rule Creation — Automatically create 11 types of protection rules

Suitable for scenarios such as policy migration, batch deployment, and environment synchronization.

Parameter Conflict Resolution (--cli-jsonInput)

[!IMPORTANT] When API parameter names conflict with KooCLI system parameters (e.g., --mode), use --cli-jsonInput to pass a JSON file to bypass the conflict.

Affected Commands

CommandConflicting ParameterAPI MeaningKooCLI Meaning
CreateCcRule--modeProtection mode 0=Standard/1=AdvancedOutput format json/table
CreateIgnoreRule--modeFixed value 1=v2 false positive suppressionOutput format

Solution: Using --cli-jsonInput

Step 1: Create a JSON file, categorized by path / query / body locations:

Important: path must contain both project_id and policy_id, otherwise the error "Missing required parameter: project_id" will occur. project_id can be obtained from the response of a previous CreatePolicy or ShowPolicy call.

{
  "path": {
    "project_id": "<project_id>",
    "policy_id": "<policy_id>"
  },
  "query": {},
  "body": {
    "name": "cc_rule_name",
    "mode": 1,
    "conditions": [
      {
        "category": "ip",
        "logic_operation": "equal",
        "contents": ["1.1.1.1"]
      }
    ],
    "action": {
      "category": "block"
    },
    "tag_type": "ip",
    "limit_num": 10,
    "limit_period": 60
  }
}

Step 2: Execute the command, using --cli-jsonInput to pass the JSON file path:

hcloud WAF CreateCcRule --cli-jsonInput=<json_file_path>

JSON File Format Requirements

LocationDescriptionParameters Included
pathPath parametersproject_id, policy_id
queryQuery parametersenterprise_project_id, etc. (optional)
bodyRequest body parametersname, mode, conditions, action, tag_type, limit_num, limit_period, etc.

Advantages

  • Completely bypasses KooCLI system parameter conflicts
  • Supports all nested object and array parameters
  • Suitable for non-interactive environments (scripts, Agent tools)

Workflow

Initial Interaction (Fixed Procedure)

[!CAUTION] After loading this Skill, the following fixed procedure must be strictly followed. Do not change the order or format.

Step 0.1: Prerequisite Check

Execute the following commands in parallel to verify the environment:

hcloud version
hcloud configure list

[!IMPORTANT] Version Requirement: hcloud CLI version must be >= 7.2.12. If the version is lower, prompt the user to update with hcloud update or download the latest version.

Step 0.2: Display Check Results and Collect Parameters

After the prerequisite check is complete, you must output in the following fixed format, then wait for the user's response:


WAF Protection Policy JSON Import

Prerequisite Check:

  • hcloud CLI Version: <version> (required >= 7.2.12) ✓/✗
  • Configuration File: <config_name>, Region <region> ✓/✗

Please provide the following information:

  1. JSON File Path — The policy file exported by huawei-cloud-waf-policy-query
  2. Import Mode — Create New Policy or Overwrite Existing Policy

[!IMPORTANT] This initial interaction format must remain fixed and must not vary each time. After the user responds, proceed to the "JSON Import Workflow" below.

JSON Import Workflow

[!IMPORTANT] Mandatory Requirement: Before performing any import operation, the JSON file source and import mode must be confirmed first.

  1. Confirm JSON Source → Verify the file was exported by huawei-cloud-waf-policy-query
  2. Select Import Mode → Create New Policy or Overwrite Existing Policy
  3. Collect Required Parameters → Create New Policy mode: ask the user for a new policy name (name can only contain digits, letters, underscores, length <= 64); Overwrite mode: ask the user for the target policy ID
  4. Parse JSON Structure → Extract metadata, basic_info, module_status, rule_details
  5. Validate Parameter Mapping → Present the commands to be executed to the user and wait for confirmation
  6. Resolve Prerequisite Resources → Scan rule_details for reference tables (value_list) and IP address groups (ip_group), query whether they already exist using ListValueList / ListIpGroup by ID or name, create if they don't exist, and record old ID → new ID mappings
  7. Create/Update Policy → Execute CreatePolicy or UpdatePolicy
  8. Batch Create Rules → Create rules module by module based on rule_details, using the reference table IDs and IP address group IDs resolved in Step 6
  9. Verify Import Results → Query policy details to confirm all rules were created correctly

[!IMPORTANT] Create New Policy mode must ask the user for a new policy name before executing any write operation. Do not use the source policy name from the JSON directly.

Execution Discipline (MANDATORY)

[!CAUTION] The following rules are strictly enforced. Violations will result in duplicate creation, errors, or resource leaks.

E1. Sequential Execution for Policy Operations

Policy creation/update operations MUST be executed sequentially, NEVER in parallel:

OperationExecution RuleReason
CreatePolicy (Mode 1)Execute ALONE, wait for responseMust extract policy_id and project_id before any other operation
UpdatePolicyExecute after CreatePolicy completesDepends on policy_id from creation response
UpdatePolicy (Mode 2)Execute ALONE, wait for responseMust complete before deleting old rules

E2. Each Command Executed Exactly Once Across All Batches

When organizing parallel tool calls, it is strictly prohibited to place the same command in multiple parallel batches. Each Create*Rule / UpdatePolicy / BatchDeleteRules / Write command may appear only once throughout the entire import workflow.

Before constructing any parallel batch, you must:

  1. List ALL commands to be executed in that batch
  2. Verify each command appears exactly once in the list
  3. Cross-check against previous batches to ensure no command has been executed before

E3. Batch Execution + Sequential Batch Verification

Rule creation is divided into two batches, which must be executed in order and cannot be merged:

BatchContentReason
Batch ATemporary JSON file writes (Write tool) + Rule creation not dependent on JSON files (Bash tool)File writes and rule creation can be parallelized
Batch BRule creation dependent on --cli-jsonInput (CC rules, Global Whitelist)Must wait for Batch A's JSON file writes to complete before execution

Critical Rule: Each batch must be executed as a single parallel call. Do NOT execute the same batch multiple times.

After each batch is executed, the returned results must be checked to confirm there are no unexpected errors before proceeding to the next batch.

E4. Idempotent Error Handling

The following error codes indicate that a rule already exists and should be treated as successful — do not retry:

Error CodeMeaningHandling
WAF.00021022Duplicated name (rule name already exists)Skip, do not retry
WAF.00022012Same condition rule already existsSkip, do not retry

E5. Prohibit Duplicate Batches and Duplicate Commands Within Batches

When constructing parallel calls, you must:

  1. List the command inventory to be executed in the current batch
  2. Verify each command appears exactly once in the inventory
  3. Cross-check against all previous batches to ensure no command has been executed before
  4. Never submit the same batch twice — if a batch has been executed, mark it as "✓ Completed" and never execute it again

Common Mistake: Accidentally placing the same Write or Bash command in multiple parallel calls within the same batch. This causes duplicate file writes or duplicate rule creation attempts.

E6. Pre-Submission Count Verification (MANDATORY)

[!CAUTION] This is the last line of defense against duplicate execution. It MUST be performed before every parallel batch submission.

Before submitting any parallel batch of tool calls, you MUST perform the following count verification:

  1. Count your inventory: Sum up the expected number of tool calls from the command inventory (e.g., 9 Bash + 2 Write = 11 total)
  2. Count your actual calls: Count the number of tool call blocks you are about to submit in this message
  3. Compare: If actual count ≠ expected count → DUPLICATES EXIST → stop immediately, identify and remove the extra calls
  4. Only submit when counts match exactly

Known Anti-Pattern (MUST NOT happen): Generating the entire command list twice in a single message, resulting in double the expected number of tool calls. For example: inventory says 11 commands, but 22 tool calls are submitted → the entire batch was duplicated.

Verification Template (must be completed before each batch):

Expected: X Bash + Y Write = Z total tool calls
Actual:   [count the tool call blocks being submitted] = N
Match?    Z == N → [ ] YES → proceed | [ ] NO → STOP, find duplicates

E7. Unique Temporary File Naming

When creating temporary JSON files for --cli-jsonInput, you must ensure the file name does not conflict with existing files:

  1. Check Before Creating: Before writing a temporary JSON file, check if a file with the same name already exists
  2. Use Unique Names: If a file with the intended name exists, generate a unique name by adding a suffix (e.g., timestamp, random string, or incrementing number)
  3. Recommended Naming Pattern: Use <rule_type>-<policy_id>-<timestamp>.json to ensure uniqueness across different imports

Example:

# First attempt: waf-cc-rule.json (if exists)
# Second attempt: waf-cc-rule-1.json
# Third attempt: waf-cc-rule-2.json
# OR use timestamp: waf-cc-rule-1f88204a66654e6aba2d094d7e0a2629-1789094114.json

Common Mistake: Overwriting an existing temporary JSON file that is still being used by a previous command, causing incorrect parameters to be passed.

Import Mode Description

Mode 1: Create New Policy

Create an entirely new protection policy from JSON, suitable for:

  • Migrating policies to a new region
  • Copying policies to a test environment
  • Creating variants based on existing policies

Execution Flow:

Read JSON → Ask for new policy name → CreatePolicy (new policy) → Obtain policy_id → Batch create rules

Mode 2: Overwrite Existing Policy

Update an existing policy's configuration and rebuild all rules, suitable for:

  • Policy configuration rollback
  • Batch policy updates
  • Policy configuration synchronization

Execution Flow:

Read JSON → UpdatePolicy (update configuration) → Delete old rules → Batch create new rules

[!WARNING] Overwrite mode will delete all existing rules of the target policy. Please confirm before proceeding.

Core Commands

Step 1: Read JSON File, Validate Format, and Collect Parameters

# Read JSON file
$json = Get-Content -Path "<json_file_path>" -Raw | ConvertFrom-Json

# Validate required fields
if (-not $json.metadata -or -not $json.basic_info -or -not $json.rule_details) {
    Write-Error "Invalid JSON format, missing required fields"
    exit 1
}

# Display policy basic information
Write-Host "Source Policy Name: $($json.basic_info.policy_name)"
Write-Host "Protection Level: $($json.basic_info.level)"
Write-Host "Default Action: $($json.basic_info.default_action)"
Write-Host "Region: $($json.metadata.region)"

# Parse module_status and build --options.* parameters for UpdatePolicy
# Exclude ip_reputation because it does not exist in UpdatePolicy's --options parameter list
$excludedModules = @("ip_reputation")
$optionsParams = @()
foreach ($module in $json.module_status) {
    if ($excludedModules -contains $module.module) { continue }
    $enabledStr = $module.enabled.ToString().ToLower()
    $optionsParams += "--options.$($module.module)=$enabledStr"
}
Write-Host "Options Parameters: $($optionsParams -join ' ')"

Collect Required Parameters (complete after parsing JSON and before executing any write operations):

Import ModeRequired ParameterDescription
Create New PolicyNew Policy NameDisplay the source policy name and ask the user for the new name to use. Name can only contain digits, letters, underscores, length <= 64
Overwrite Existing PolicyTarget Policy IDAsk the user for the target policy ID to overwrite (can be obtained via ListPolicy)

[!IMPORTANT] Create New Policy mode must ask the user for a new policy name before executing any write operation. Do not use the source policy name (basic_info.policy_name) from the JSON directly. The source policy name is displayed to the user for reference only.

Step 2: Create or Update Policy

Mode 1: Create New Policy

[!CAUTION] CreatePolicy MUST be executed as a SINGLE standalone call. Do NOT execute it in parallel with any other commands.

Correct: Execute CreatePolicy alone → wait for response → extract policy_id and project_id → proceed to next step

Incorrect: Execute CreatePolicy in parallel with UpdatePolicy or other commands → causes duplicate creation or missing policy_id

# Step 1: CreatePolicy - creates policy with default configuration
# THIS COMMAND MUST BE EXECUTED ALONE - DO NOT INCLUDE IN PARALLEL BATCH
hcloud WAF CreatePolicy \
  --name=<user-specified new policy name>

# Step 2: Extract policy_id and project_id from CreatePolicy response
# Also extract the "options" object from the response - this contains the current module states

[!IMPORTANT] CreatePolicy does not accept --level and --action.category parameters. After creation, defaults are level=2, action=log. If modifications are needed, you must create the policy first, then call UpdatePolicy.

Step 3: Compare and Build Differential UpdatePolicy Command

After CreatePolicy returns, compare the JSON's module_status with the options object from the CreatePolicy response:

  1. For each module in module_status (excluding ip_reputation):

    • Check if the module exists in the CreatePolicy response's options object
    • If it exists AND the value differs from the JSON's enabled field → include --options.<module>=<json_value> in the command
    • If it does NOT exist in the response → skip this module (likely not supported by current WAF specification)
    • If the values match → skip this module (no change needed)
  2. Similarly compare basic_info.level and basic_info.default_action:

    • If level differs from response → include --level=<basic_info.level>
    • If default_action differs from response action.category → include --action.category=<basic_info.default_action>
  3. If NO differences found at all → skip UpdatePolicy entirely and proceed to rule creation

Example comparison:

// JSON module_status says: webshell.enabled = true
// CreatePolicy response options says: webshell = false
// → Include: --options.webshell=true

// JSON module_status says: bot_enable.enabled = true  
// CreatePolicy response options says: bot_enable = true
// → Skip (values match)

// JSON module_status says: some_new_feature.enabled = true
// CreatePolicy response options: field does not exist
// → Skip (not supported by current specification)

Execute UpdatePolicy only with the differing parameters:

# Only include parameters that differ from CreatePolicy response
# Example: if only webshell and followed_action differ
hcloud WAF UpdatePolicy \
  --policy_id=<newly created policy_id> \
  --level=2 \
  --action.category=log \
  --options.webshell=true \
  --options.followed_action=true

[!WARNING] Why differential update? Some WAF specifications do not support all modules. Passing unsupported module options will cause error WAF.00013002: The feature is not supported in the current specification. By comparing with CreatePolicy response first, we only update modules that are both supported AND need changes.

JSON Field Mapping:

JSON FieldCLI ParameterDescription
User-specified--nameNew policy name (asked from user in Step 1, not using the source policy name from JSON)
module_status[*].enabled--options.<module>Module switch status (true/false), ONLY if differs from CreatePolicy response

[!NOTE] Module Status Processing: Compare each module in module_status array against the options object returned by CreatePolicy. Only generate --options.<module> parameters for modules where the values differ. Modules not present in the CreatePolicy response should be skipped entirely. Note: The ip_reputation module is always excluded because it cannot be modified via UpdatePolicy.

Policy Name Conflict Handling: Since the user is asked for a new policy name in Step 1, name conflicts should not normally occur. If WAF.00011016: Duplicate name is still returned, ask the user for a new name and retry.

After successful creation (and optional UpdatePolicy), extract id from the CreatePolicy response as the new policy_id. Also extract project_id from the response, which will be needed for subsequent --cli-jsonInput commands.

Mode 2: Overwrite Existing Policy

# Step 1: Query the current state of target policy
hcloud WAF ShowPolicy --policy_id=<target_policy_id>

# Step 2: Extract the "options" object, "level", and "action.category" from ShowPolicy response
# These represent the CURRENT state of the target policy

# Step 3: Compare and Build Differential UpdatePolicy Command
# Compare JSON's module_status with ShowPolicy response's options:
# - If a module value DIFFERS → include in UpdatePolicy command
# - If a module value MATCHES → skip (no change needed)
# - If a module is NOT in ShowPolicy response → skip (not supported by specification)
# - Also compare basic_info.level and basic_info.default_action

# Execute UpdatePolicy only with differing parameters
# Example: if level, webshell, and followed_action differ from current state
hcloud WAF UpdatePolicy \
  --policy_id=<target_policy_id> \
  --level=2 \
  --action.category=log \
  --options.webshell=true \
  --options.followed_action=true

[!IMPORTANT] Do not modify the policy name in overwrite mode. If you attempt to rename the target policy to the source policy name (--name=<basic_info.policy_name>) and that name already exists, the error WAF.00011016: Duplicate name will occur. Keep the target policy's original name.

Step-by-Step Comparison Logic:

  1. For each module in module_status (excluding ip_reputation):

    • Check if the module exists in ShowPolicy response's options object
    • If it exists AND the value differs from JSON's enabled field → include --options.<module>=<json_value>
    • If it does NOT exist in response → skip (likely not supported by current WAF specification)
    • If values match → skip (no change needed)
  2. Compare basic_info.level with ShowPolicy response's level:

    • If different → include --level=<basic_info.level>
  3. Compare basic_info.default_action with ShowPolicy response's action.category:

    • If different → include --action.category=<basic_info.default_action>
  4. If NO differences found at all → skip UpdatePolicy entirely and proceed to delete old rules

Example comparison:

// JSON says: webshell.enabled = true
// ShowPolicy says: webshell = false
// → Include: --options.webshell=true

// JSON says: bot_enable.enabled = true
// ShowPolicy says: bot_enable = true
// → Skip (values match)

// JSON says: some_new_feature.enabled = true
// ShowPolicy response: field does not exist
// → Skip (not supported by current specification)

[!WARNING] Why differential update? Same reason as Mode 1: Some WAF specifications do not support all modules. Passing unsupported module options will cause error WAF.00013002: The feature is not supported in the current specification. By comparing with ShowPolicy response first, we only update modules that are both supported AND need changes.

Module Status Processing: Compare each module in module_status array against the options object returned by ShowPolicy. Only generate --options.<module> parameters for modules where the values differ. Modules not present in the ShowPolicy response should be skipped entirely. Note: The ip_reputation module is always excluded because it cannot be modified via UpdatePolicy.

Delete Old Rules (module by module):

[!IMPORTANT] BatchDeleteRules Parameter Format: Use --policy_rule_ids.N.policy_id and --policy_rule_ids.N.rule_ids.M, not --policy_id and --rule_ids.

# Delete precise protection rules
hcloud WAF BatchDeleteRules --rule_type=custom --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1> --policy_rule_ids.1.rule_ids.2=<id2>

# Delete CC attack protection rules
hcloud WAF BatchDeleteRules --rule_type=cc --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete blacklist/whitelist rules
hcloud WAF BatchDeleteRules --rule_type=whiteblackip --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete geo-access control rules
hcloud WAF BatchDeleteRules --rule_type=geoip --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete global whitelist rules
hcloud WAF BatchDeleteRules --rule_type=ignore --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete anti-crawler rules (use independent API, not BatchDeleteRules)
# First get rule IDs via: hcloud WAF ListAnticrawlerRules --policy_id=<policy_id>
hcloud WAF DeleteAnticrawlerRule --policy_id=<policy_id> --rule_id=<id1>

# Delete privacy masking rules
hcloud WAF BatchDeleteRules --rule_type=privacy --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete web anti-tampering rules
hcloud WAF BatchDeleteRules --rule_type=antitamper --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete sensitive data leakage prevention rules
hcloud WAF BatchDeleteRules --rule_type=antileakage --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete threat intelligence access control rules
hcloud WAF BatchDeleteRules --rule_type=ip-reputation --policy_rule_ids.1.policy_id=<policy_id> --policy_rule_ids.1.rule_ids.1=<id1>

# Delete attack punishment rules (use independent API, not BatchDeleteRules)
# First get rule IDs via: hcloud WAF ListPunishmentRules --policy_id=<policy_id>
hcloud WAF DeletePunishmentRule --policy_id=<policy_id> --rule_id=<id1>

[!NOTE] If a module has no rules, the corresponding delete command will return empty results and will not affect subsequent operations.

Step 3: Batch Create Rules

[!CAUTION] Pre-execution Checklist (Must Be Completed):

  1. Confirm Step 2 (policy creation/update) is complete
  2. Confirm prerequisite resource resolution (reference tables, IP address groups) is complete
  3. Confirm a complete command inventory has been established (see "Batch Execution Rules" below)
  4. Confirm no batch has been executed yet (first execution of Step 3)

Create rules in the following order based on the modules in rule_details.

[!CAUTION] Batch Execution Rules (see "Execution Discipline" section):

Before execution, scan rule_details first to establish a complete command inventory, marking each command's batch assignment:

BatchIncluded CommandsDependenciesExecution Status
Batch AWrite temporary JSON files (CC rules, Global Whitelist) + All rule creation commands not dependent on --cli-jsonInput (Precise Protection, Blacklist/Whitelist, Geo-Access Control, Privacy Masking, Web Anti-Tampering, Threat Intelligence, Attack Punishment)No prerequisite dependencies; file writes and rule creation can be parallelized□ Pending
Batch BRule creation commands dependent on --cli-jsonInput (CC rules CreateCcRule, Global Whitelist CreateIgnoreRule)Must wait for Batch A's JSON file writes to succeed□ Pending

Execution Requirements:

  1. Batch A and Batch B must be executed in order and cannot be merged into the same parallel call
  2. Each command may appear only once throughout Step 3 and must not be repeatedly submitted across multiple batches
  3. Batch Execution Tracking: Before executing each batch, you must explicitly list all commands it contains; after execution, mark each as completed. Batches marked as "✓ Completed" must not be executed again
  4. After each batch execution, check the returned results. If WAF.00021022 (Duplicated name) or WAF.00022012 (Same condition rule already exists) appears, treat it as the rule already existing — skip without retry
  5. After all batches are complete, proceed to Step 4 for verification
  6. Temporary File Naming: Before writing temporary JSON files (for CC rules and Global Whitelist), check if files with the intended names already exist. If they do, use unique names (e.g., add timestamp or incrementing suffix like -1, -2). See E6. Unique Temporary File Naming in the Execution Discipline section for details.

Command Inventory Template (must be filled out before executing Batch A):

Batch A Command Inventory:
- Write: <temp_json_path_cc> (CC rule JSON)
- Write: <temp_json_path_ignore> (Global whitelist JSON)
- Bash: CreateCustomRule (rule 1) - if custom rules exist
- Bash: CreateCustomRule (rule 2) - if custom rules exist
- Bash: CreateWhiteblackipRule - if whiteblackip rules exist
- Bash: CreateGeoipRule - if geoip rules exist
- Bash: CreatePrivacyRule - if privacy rules exist
- Bash: CreateAntiTamperRule - if antitamper rules exist
- Bash: CreateAntileakageRule - if antileakage rules exist
- Bash: CreateIpReputationRule - if ip_reputation rules exist
- Bash: CreatePunishmentRule - if followed_action rules exist

Verification: Each command appears exactly once? [ ] YES [ ] NO
If NO, remove duplicates before proceeding.

Common Mistakes to Avoid:

  • ❌ Executing the same CreateCustomRule command multiple times in Batch A
  • ❌ Including Write and Bash commands for the same rule in both Batch A and Batch B
  • ❌ Executing Batch A, then executing Batch A again (duplicate batch execution)
  • ❌ Executing CreateCcRule in Batch A instead of Batch B (CC rules require --cli-jsonInput)

3.1 Precise Protection Rules

[!IMPORTANT] Reference Table (ValueList) Pre-processing: If a rule's logic_operation ends with _any or _all (e.g., contain_any, equal_any), then --conditions.N.value_list_id must be used, and --conditions.N.contents.M cannot be used.

Prerequisite Step: Check Reference Table Dependencies

Iterate through each rule's conditions and check whether logic_operation ends with _any or _all:

  1. Extract conditions[N].value_list_id from JSON
  2. Three-level fallback lookup for reference table:
    • Level 1 (Exact query by ID):
      hcloud WAF ShowValueList --valuelistid=<value_list_id>
      
      If successful (no error code), directly reuse this value_list_id
    • Level 2 (Fuzzy query by name): If Level 1 returns an error (e.g., WAF.00011004: Illegal id), extract value_list_detail.name from JSON, then:
      hcloud WAF ListValueList --name=<value_list_detail.name>
      
      If results are returned and total > 0, find the matching record by name from the returned list and reuse its id
    • Level 3 (Create new reference table): If neither of the above levels found it, create a new reference table:
      hcloud WAF CreateValueList \
             --name=<value_list_detail.name>_copy \
        --type=<value_list_detail.type> \
        --values.1=<value1> \
        --values.2=<value2> \
        --values.3=<value3>
      
      Extract the new id from the response and record the old ID → new ID mapping

Case A: Normal Conditions (logic_operation does NOT end with _any/_all)

hcloud WAF CreateCustomRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --priority=<rule.priority> \
  --action.category=<rule.action.category> \
  --conditions.1.category=<rule.conditions[0].category> \
  --conditions.1.logic_operation=<rule.conditions[0].logic_operation> \
  --conditions.1.contents.1=<rule.conditions[0].contents[0]> \
  --time=false

Case B: Reference Table Conditions (logic_operation ends with _any/_all)

hcloud WAF CreateCustomRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --priority=<rule.priority> \
  --action.category=<rule.action.category> \
  --conditions.1.category=<rule.conditions[0].category> \
  --conditions.1.logic_operation=<rule.conditions[0].logic_operation> \
  --conditions.1.value_list_id=<value_list_id> \
  --time=false

[!WARNING] In Case B, do NOT pass --conditions.N.contents.M, otherwise the error WAF.00021017: Missing field or illegal field value will occur. value_list_id and contents are mutually exclusive.

JSON Field Mapping:

JSON FieldCLI Parameter
rule.name--name
rule.priority--priority
rule.action.category--action.category
rule.conditions[N].category--conditions.N.category
rule.conditions[N].logic_operation--conditions.N.logic_operation
rule.conditions[N].contents[M]--conditions.N.contents.M (normal conditions only)
rule.conditions[N].value_list_id--conditions.N.value_list_id (reference table conditions only)
rule.time--time (false = effective immediately)

3.2 CC Attack Protection Rules

[!IMPORTANT] The --mode parameter conflicts with KooCLI system parameters. You must use --cli-jsonInput to create these rules. See the "Parameter Conflict Resolution (--cli-jsonInput)" section above.

Step 1: Based on the CC rule data in JSON, create a temporary JSON file:

Note: path must contain both project_id and policy_id.

[!IMPORTANT] Temporary File Naming: Before creating the file, check if a file with your intended name already exists. If it does, use a unique name by adding a suffix (e.g., -1, -2) or timestamp. See E6. Unique Temporary File Naming in the Execution Discipline section.

[!IMPORTANT] CC Rule mode Determination: The source JSON may not contain a mode field. Determine mode based on the condition categories:

  • mode: 0 (Standard): Only supports url category conditions. If ALL conditions have category: "url", use mode 0.
  • mode: 1 (Advanced): Supports ip, cookie, header, params, url and other categories. If ANY condition has a category other than url, must use mode 1.
  • Using mode 0 with non-url conditions (e.g., category: "ip") will cause WAF.00021017: Illegal path error.
  • When in doubt, default to mode: 1 (Advanced mode is a superset of Standard mode).
{
  "path": {
    "project_id": "<project_id>",
    "policy_id": "<policy_id>"
  },
  "query": {},
  "body": {
    "name": "<rule.name>",
    "mode": <determined_mode_value>,
    "conditions": [
      {
        "category": "<rule.conditions[0].category>",
        "logic_operation": "<rule.conditions[0].logic_operation>",
        "contents": ["<rule.conditions[0].contents[0]>"]
      }
    ],
    "action": {
      "category": "<rule.action.category>"
    },
    "tag_type": "<rule.tag_type>",
    "limit_num": <rule.limit_num>,
    "limit_period": <rule.limit_period>
  }
}

Step 2: Execute the command:

hcloud WAF CreateCcRule --cli-jsonInput=<temp_json_path>

JSON Field Mapping:

JSON FieldCLI Parameter (in JSON body)
rule.namebody.name
Determined from conditionsbody.mode (0=Standard: url only; 1=Advanced: all categories)
rule.limit_numbody.limit_num
rule.limit_periodbody.limit_period
rule.tag_typebody.tag_type
rule.action.categorybody.action.category

3.3 Blacklist/Whitelist Rules

[!IMPORTANT] IP Address Group Pre-processing: --addr only accepts a single IP/CIDR (e.g., 42.123.120.66 or 42.123.120.0/16). If the JSON rule uses ip_group (multiple IPs), then --ip_group_id must be used, and --addr cannot be used.

Prerequisite Step: Check IP Address Group Dependencies

Check whether the JSON rule uses addr (single IP) or ip_group (IP address group):

  1. If the JSON contains rule.ip_group field (instead of rule.addr), then IP address group handling is required
  2. Extract ip_group.id from JSON
  3. Three-level fallback lookup for IP address group:
    • Level 1 (Exact query by ID):
      hcloud WAF ShowIpGroup --id=<ip_group.id>
      
      If successful (no error code), directly use this ip_group_id
    • Level 2 (Fuzzy query by name): If Level 1 returns an error (e.g., WAF.00014001: Resource not found), extract ip_group.name from JSON, then:
      hcloud WAF ListIpGroup --name=<ip_group.name>
      
      If results are returned and total > 0, find the matching record by name from the returned list and use its id
    • Level 3 (Create new IP address group): If neither of the above levels found it, create a new IP address group:
      hcloud WAF CreateIpGroup \
             --name=<ip_group.name>_copy \
        --ips=<ip_group.detail.ips>
      
      Extract the new id from the response and record the old ID → new ID mapping
Case A: Single IP (JSON contains rule.addr)
hcloud WAF CreateWhiteblackipRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --white=<rule.white> \
  --addr=<rule.addr>
Case B: IP Address Group (JSON contains rule.ip_group)
hcloud WAF CreateWhiteblackipRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --white=<rule.white> \
  --ip_group_id=<ip_group_id>

[!WARNING] In Case B, do NOT pass --addr. addr and ip_group_id are mutually exclusive; when both are present, addr takes precedence (ip_group_id will be ignored).

JSON Field Mapping:

JSON FieldCLI Parameter
rule.name--name
rule.white--white (0=block, 1=allow, 2=log only)
rule.addr--addr (single IP scenarios only)
rule.ip_group.id--ip_group_id (IP address group scenarios only)

3.4 Geo-Access Control Rules

[!WARNING] CreateGeoipRule Known False Failure: This command may return an error response even though the rule was successfully created on the server side. After executing CreateGeoipRule, if any error is returned:

  1. Run hcloud WAF ListGeoipRule --policy_id=<policy_id> immediately
  2. Check if a rule matching the expected name exists in the returned list
  3. If found → treat as successful creation, proceed to next rule. Do NOT retry CreateGeoipRule
  4. If not found → genuine failure, report to user

Do NOT enter a check-and-retry loop. One verification is enough. If the rule exists, move on.

Important: geoTagList in JSON is an array (e.g., ["BJ", "Afghanistan"]), but the CLI's --geoip parameter requires multiple region codes joined with | separator. The JSON array must be converted to a |-delimited string.

# geoTagList in JSON: ["BJ", "Afghanistan"]
# Convert to CLI parameter: --geoip="BJ|Afghanistan"

hcloud WAF CreateGeoipRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --geoip="<region1>|<region2>|..." \
  --white=<rule.white>

Conversion Rule:

# Convert from JSON array to |-delimited string
$geoipString = ($rule.geoTagList -join "|")
# Or for cases containing Chinese/special characters
$geoipString = "BJ|Afghanistan"

JSON Field Mapping:

JSON FieldCLI ParameterConversion Description
rule.name--nameDirect use
rule.geoTagList--geoipArray joined with `
rule.white--whiteDirect use

3.5 Global Whitelist Rules

[!IMPORTANT] The --mode parameter conflicts with KooCLI system parameters. You must use --cli-jsonInput to create these rules. See the "Parameter Conflict Resolution (--cli-jsonInput)" section above.

Step 1: Based on the global whitelist rule data in JSON, create a temporary JSON file:

Note: path must contain both project_id and policy_id.

[!IMPORTANT] Temporary File Naming: Before creating the file, check if a file with your intended name already exists. If it does, use a unique name by adding a suffix (e.g., -1, -2) or timestamp. See E6. Unique Temporary File Naming in the Execution Discipline section.

[!NOTE] Global Whitelist API does NOT require name: The CreateIgnoreRule API parameters are conditions, domain, mode, rule, and optional advanced/description. There is no name field. Do not inject a name into the temp JSON body.

{
  "path": {
    "project_id": "<project_id>",
    "policy_id": "<policy_id>"
  },
  "query": {},
  "body": {
    "mode": 1,
    "conditions": [
      {
        "category": "<rule.conditions[0].category>",
        "logic_operation": "<rule.conditions[0].logic_operation>",
        "contents": ["<rule.conditions[0].contents[0]>"]
      }
    ],
    "rule": "<rule.rule>",
    "domain": ["<rule.domain[0]>", "<rule.domain[1]>"]
  }
}

[!WARNING] The domain field must contain the actual domain list from the JSON rule data (e.g., ["sada.com"]), NOT an empty array []. Using [] will cause the rule to apply to all domains, which is usually not the intended behavior.

Step 2: Execute the command:

hcloud WAF CreateIgnoreRule --cli-jsonInput=<temp_json_path>

JSON Field Mapping:

JSON FieldCLI Parameter (in JSON body)
rule.rulebody.rule
rule.conditions[N].*body.conditions[N].*
Fixed valuebody.mode = 1

3.6 Anti-Crawler Rules

hcloud WAF CreateAnticrawlerRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --type=<rule.type> \
  --priority=<rule.priority> \
  --conditions.1.category=<rule.conditions[0].category> \
  --conditions.1.logic_operation=<rule.conditions[0].logic_operation> \
  --conditions.1.contents.1=<rule.conditions[0].contents[0]>

3.7 Privacy Masking Rules

hcloud WAF CreatePrivacyRule \
  --policy_id=<policy_id> \
  --url=<rule.url> \
  --category=<rule.category> \
  --index=<rule.index>

3.8 Web Anti-Tampering Rules

Note: The domain name specified by the --hostname parameter does not need to be pre-bound to the policy or exist in the WAF protected domain list. Rules can be created directly using the hostname value from JSON.

hcloud WAF CreateAntiTamperRule \
  --policy_id=<policy_id> \
  --hostname=<rule.hostname> \
  --url=<rule.url>

3.9 Sensitive Data Leakage Prevention Rules

hcloud WAF CreateAntileakageRule \
  --policy_id=<policy_id> \
  --url=<rule.url> \
  --category=<rule.category> \
  --contents.1=<rule.contents[0]>

3.10 Threat Intelligence Access Control Rules

Description: Threat intelligence rules are used for access control based on IP reputation databases (e.g., IDC datacenter IPs). The command is CreateIpReputationRule.

hcloud WAF CreateIpReputationRule \
  --policy_id=<policy_id> \
  --name=<rule.name> \
  --action.category=<rule.action.category> \
  --type=<rule.type> \
  --tags.1=<rule.tags[0]> \
  --tags.2=<rule.tags[1]>

Note: The --tags.N parameter format is --tags.1=value1 --tags.2=value2, with each tag as a separate parameter. --type currently only supports idc.

JSON Field Mapping:

JSON FieldCLI ParameterDescription
rule.name--nameRule name
rule.action.category--action.categoryAction type (block/log/pass)
rule.type--typeReputation type, currently only supports idc
rule.tags--tags.NTag list, e.g., ["Dr.Peng"] → --tags.1=Dr.Peng

3.11 Attack Punishment Rules

Description: Attack punishment rules automatically block visitors who trigger WAF blocking rules multiple times within a specified time period. The command is CreatePunishmentRule.

hcloud WAF CreatePunishmentRule \
  --policy_id=<policy_id> \
  --category=<rule.category> \
  --block_time=<rule.block_time> \
  --time_unit=<rule.time_unit> \
  --description=<rule.description>

Note:

  • --category specifies the punishment category (e.g., long_ip_block, short_ip_block). Each category can only have one rule.
  • --block_time specifies the punishment duration in the unit specified by --time_unit.
  • --time_unit can be SECOND, MINUTE, HOUR, DAY, or MONTH. Default is SECOND.
  • --description is optional but recommended for documentation purposes.

JSON Field Mapping:

JSON FieldCLI ParameterDescription
rule.category--categoryPunishment category (e.g., long_ip_block)
rule.block_time--block_timePunishment duration (integer)
rule.time_unit--time_unitTime unit (SECOND/MINUTE/HOUR/DAY/MONTH)
rule.description--descriptionRule description (optional)

Step 4: Verify Import Results

# Query policy details (includes rules_count field showing rule counts per module)
hcloud WAF ShowPolicy --policy_id=<policy_id>

# Query rules for each module (verify one by one)
# 1. Precise protection rules
hcloud WAF ListCustomRules --policy_id=<policy_id>

# 2. CC attack protection rules
hcloud WAF ListCcRules --policy_id=<policy_id>

# 3. Blacklist/whitelist rules
hcloud WAF ListWhiteblackipRule --policy_id=<policy_id>

# 4. Geo-access control rules
hcloud WAF ListGeoipRule --policy_id=<policy_id>

# 5. Global whitelist rules
hcloud WAF ListIgnoreRule --policy_id=<policy_id>

# 6. Anti-crawler rules
hcloud WAF ListAnticrawlerRules --policy_id=<policy_id>

# 7. Privacy masking rules
hcloud WAF ListPrivacyRule --policy_id=<policy_id>

# 8. Web anti-tampering rules
hcloud WAF ListAntitamperRule --policy_id=<policy_id>

# 9. Sensitive data leakage prevention rules
hcloud WAF ListAntileakageRules --policy_id=<policy_id>

# 10. Threat intelligence access control rules
hcloud WAF ListIpReputationRules --policy_id=<policy_id>

# 11. Attack punishment rules
hcloud WAF ListPunishmentRules --policy_id=<policy_id>

Verification Points:

  • Compare the rules_count field returned by ShowPolicy to confirm the number of rules per module matches the JSON
  • Query each module's rule list individually to confirm rule content (name, conditions, actions, etc.) is correct
  • Check that rule status is enabled (status=1)

Parameter Confirmation

Required JSON Fields

JSON FieldSourceDescription
metadata.regionhuawei-cloud-waf-policy-querySource region (informational only; hcloud uses configured default region)
metadata.policy_idhuawei-cloud-waf-policy-querySource policy ID (used in overwrite mode)
metadata.policy_namehuawei-cloud-waf-policy-queryPolicy name
basic_info.levelhuawei-cloud-waf-policy-queryProtection level (1-3)
basic_info.default_actionhuawei-cloud-waf-policy-queryDefault action (block/log)
basic_info.bind_hostshuawei-cloud-waf-policy-queryBound domain list
basic_info.full_detectionhuawei-cloud-waf-policy-queryPrecise protection detection mode
module_statushuawei-cloud-waf-policy-queryProtection module toggle status
rule_detailshuawei-cloud-waf-policy-queryRule details per module

Parameters Requiring User Confirmation

ParameterRequiredDescriptionSource
JSON file pathYesJSON file exported by huawei-cloud-waf-policy-queryUser provided
Import modeYesCreate New Policy / Overwrite Existing PolicyUser selection
New policy nameRequired for Create New Policy modeName of the new policy (must not duplicate existing policies)Specified by user in Step 1; do not use the source policy name from JSON
Target policy IDRequired for overwrite modeTarget policy ID to overwriteUser specified (obtained via ListPolicy)

Reference Documents

KooCLI Command Format Standard

hcloud <Service> <Operation> [--key=value ...]
FeatureDescriptionExample
Service nameExact KooCLI service name, first letter capitalizedWAF
Operation namePascalCase namingCreatePolicy, CreateCustomRule
Nested parameters--key.subkey=value--action.category=block
Indexed parameters--key.N=value--conditions.1.category=url
Array parameters--key.N.contents.M=value--conditions.1.contents.1=/admin

Hard Constraints (MANDATORY)

  1. JSON Source Validation: Importing JSON files not exported by huawei-cloud-waf-policy-query is strictly prohibited. If the JSON format does not match, the import must be refused and the user must be informed.
  2. Write Operation Confirmation: All write operations (CreatePolicy/UpdatePolicy/CreateXxxRule/BatchDeleteRules) must present the commands to be executed to the user and wait for confirmation before execution.
  3. Overwrite Mode Warning: Overwrite mode will delete all existing rules of the target policy. The user must be explicitly informed and confirmed before execution.
  4. No Credential Leakage: Exposing sensitive configuration information such as AK/SK, Project ID, etc. in output is strictly prohibited.

Notes

  • JSON files must be exported by huawei-cloud-waf-policy-query and contain four top-level fields: metadata, basic_info, module_status, rule_details
  • When creating a new policy, the policy name must not duplicate existing policies (name can only contain digits, letters, underscores, length <= 64). The user must be asked for the new policy name before creating the policy; do not use the source policy name from JSON directly
  • The --mode parameter for CC rules and global whitelist conflicts with KooCLI system parameters. You must use --cli-jsonInput to create them (see the "Parameter Conflict Resolution" section); the --mode command-line parameter cannot be used directly
  • The priority range for precise protection rules is 0-65535; lower values indicate higher priority
  • The white parameter for blacklist/whitelist: 0=block, 1=allow, 2=log only
  • In overwrite mode, rule deletion uses BatchDeleteRules; existing rule IDs must be queried first
  • Command names are case-sensitive and singular/plural-sensitive, e.g., ListWhiteblackipRule (singular), ShowWhiteBlackIpRule (note capitalization)
  • Nested parameters must be expanded with dots: --action.category=block; cannot be written as --action=block
  • CreatePolicy does not accept --level and --action.category parameters; defaults after creation are level=2, action=log; use UpdatePolicy if modifications are needed
  • When executing UpdatePolicy, the --options.* parameters must be dynamically set based on the module_status array in the JSON file. For each module in module_status, if enabled=true, set --options.<module>=true; if enabled=false, set --options.<module>=false. Exception: The ip_reputation module must NOT be included in --options.* parameters because it does not exist in the UpdatePolicy API's options parameter list (verified via hcloud WAF UpdatePolicy --help). Only modules that have corresponding --options.<module> parameters in UpdatePolicy should be processed
  • For precise protection rules, if logic_operation ends with _any or _all (e.g., contain_any, equal_any), --conditions.N.value_list_id must be used; --conditions.N.contents.M cannot be used, otherwise error WAF.00021017 will occur
  • For blacklist/whitelist rules, --addr only accepts a single IP/CIDR (e.g., 42.123.120.66 or 42.123.120.0/16); multiple IPs must use an IP address group (--ip_group_id), otherwise error WAF.00021009 will occur
  • value_list_id and ip_group.id in JSON should use a three-level fallback lookup strategy: first use ShowValueList --valuelistid=<ID> or ShowIpGroup --id=<ID> for exact ID query — if found, reuse directly; if an error is returned, use ListValueList --name=<name> or ListIpGroup --name=<name> for fuzzy name query; if still not found, create a new resource. Note parameter names: ShowValueList uses --valuelistid (not --value_list_id), ShowIpGroup uses --id (not --ip_group_id)
  • In --cli-jsonInput JSON files, the path location must contain both project_id and policy_id, otherwise the error "Missing required parameter: project_id" will occur. project_id can be obtained from the response of CreatePolicy or ShowPolicy
  • The --geoip parameter for geo-access control rules uses | (pipe) to separate multiple region codes (e.g., "BJ|Afghanistan"), not commas. The geoTagList array in JSON must be converted to a |-delimited string
  • The --hostname parameter for web anti-tampering rules (CreateAntiTamperRule) does not require the domain to be pre-bound to the policy or exist in the WAF protected domain list; rules can be created directly using the hostname value from JSON
  • If a name already exists when creating a new policy (WAF.00011016: Duplicate name), since the user was asked for the name in Step 1, this error is an exceptional case — ask the user for a new name (e.g., add _2, _copy suffix) and retry
  • Threat intelligence access control rules use the CreateIpReputationRule command; the --tags.N parameter format is --tags.1=value1 --tags.2=value2; --type currently only supports idc
  • CC rule mode must be determined based on condition categories: mode=0 (Standard) only supports url conditions; mode=1 (Advanced) supports all categories including ip, cookie, header, params. The source JSON does not contain mode field, so you must infer it: if any condition category is not url, use mode=1. Using wrong mode causes WAF.00021017: Illegal path error (misleading message — actually means incorrect rule configuration)
  • The --cli-jsonInput JSON file path must use object format {"project_id": "...", "policy_id": "..."}, NOT string format like "path": "v1/...". String format causes CLI parse error
  • CC rules: the source JSON does NOT contain a mode field. The mode must be inferred from condition categories: mode=0 (Standard) only supports url conditions; mode=1 (Advanced) supports all categories (ip, cookie, header, params, etc.). When in doubt, use mode=1. Using mode=0 with non-url conditions causes WAF.00021017: Illegal path (misleading error — actually means incorrect rule configuration)
  • Global whitelist (ignore) rules: The CreateIgnoreRule API does NOT have a name field (verified via SDK and --help). Do NOT include name in the temp JSON body. The required fields are conditions, domain, mode, rule.
  • --cli-jsonInput JSON files must use object format for path: {"path": {"project_id": "...", "policy_id": "..."}}. String format (e.g., "path": "v1/...") is NOT supported and causes 解析cli-jsonInput参数文件失败
  • Attack punishment rules use the CreatePunishmentRule command; each punishment category (e.g., long_ip_block, short_ip_block) can only have one rule per policy. The --block_time parameter value range depends on the --time_unit and category: for long_xxx categories, SECOND [301, 7776000], MINUTE [6, 129600], HOUR [1, 2160], DAY [1, 90], MONTH [1, 3]; for short_xxx categories, SECOND [1, 300]. Default --time_unit is SECOND. In overwrite mode, attack punishment rules must be deleted using the independent DeletePunishmentRule API (not BatchDeleteRules), similar to anti-crawler rules
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

skills/security/waf-aad/huawei-cloud-waf-policy-manage

默认分支

master

最新提交

f690d6e

Tree SHA

a8c0aba