
Alibabacloud Waf Checkresponse Intercept Query
- 147 installs
- 208 repo stars
- Updated August 4, 2026
- aliyun/alibabacloud-aiops-skills
Query Alibaba Cloud WAF CheckResponse intercept events to diagnose blocked requests, false positives, and attack patterns during live traffic incidents.
About
Operational Alibaba Cloud WAF skill for querying CheckResponse intercept records so teams can see why requests were blocked, validate rule behavior, and resolve production security incidents faster.
- Investigate WAF blocks without console digging
- Correlate intercept reason with client responses
- Speed up false-positive triage on Alibaba Cloud
- Support post-incident security review
- Tune rules using real intercept evidence
Alibabacloud Waf Checkresponse Intercept Query by the numbers
- 147 all-time installs (skills.sh)
- Ranked #896 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aliyun/alibabacloud-aiops-skills --skill alibabacloud-waf-checkresponse-intercept-queryAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 147 |
|---|---|
| repo stars | ★ 208 |
| Last updated | August 4, 2026 |
| Repository | aliyun/alibabacloud-aiops-skills ↗ |
What it does
Query Alibaba Cloud WAF CheckResponse intercept events to diagnose blocked requests, false positives, and attack patterns during live traffic incidents.
Files
WAF CheckResponse Intercept Query
Prerequisites
Pre-check: Aliyun CLI >= 3.3.3 required
Run aliyun version to verify >= 3.3.3. If not installed or version too low,run curl -fsSL https://aliyuncli.alicdn.com/setup.sh | bash to update,or see references/cli-installation-guide.md for installation instructions.Pre-check: Aliyun CLI plugin update required
[MUST] run aliyun configure set --auto-plugin-install true to enable automatic plugin installation.[MUST] run aliyun plugin update to ensure that any existing plugins are always up-to-date.At the start of the Core Workflow (before any CLI invocation): [MUST] Enable AI-Mode — AI-mode is required for Agent Skill execution. Run the following commands before any CLI invocation:
aliyun configure ai-mode enable
aliyun configure ai-mode set-user-agent --user-agent "AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query"[MUST] Disable AI-Mode at EVERY exit point — Before delivering the final response for ANY reason, always disable AI-mode first. This applies to ALL exit paths: workflow success, workflow failure, error/exception, user cancellation, session end, or any other scenario where no further CLI commands will be executed. AI-mode is only used for Agent Skill invocation scenarios and MUST NOT remain enabled after the skill stops running.
aliyun configure ai-mode disable[MUST] CLI User-Agent — Every aliyun CLI command invocation must include: --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query
Before execution, you must collect the following information from the user:
| Parameter | Description | Required |
|---|---|---|
| Request ID | The traceid obtained from the HTML body of WAF's block (intercept) response, or the Request ID shown on the 405 block page displayed in the browser | Yes |
Optional: WAF Instance ID, SLS Project name, SLS Logstore name (will be auto-discovered if not provided)
Notes:
- Request ID (traceid) is obtained from the HTML body of WAF's block response, or from the 405 block page displayed in the browser
- Uses Alibaba Cloud default credential chain for authentication (ECS RAM Role, ~/.alibabacloud/config, etc.)
Region Information
| RegionId Value | Region | Description |
|---|---|---|
cn-hangzhou | Chinese Mainland | WAF instances within mainland China |
ap-southeast-1 | Outside Chinese Mainland | WAF instances in overseas and Hong Kong/Macao/Taiwan regions |
Query Workflow
Step 1: Information Collection
Confirm the Request ID (traceid) with the user. If the user has not provided one, guide them to obtain it from: 1. The 405 block page displayed in the browser, which shows the Request ID directly 2. The HTML body of WAF's block (intercept) response, which contains the traceid
Step 2: Auto-Discover WAF Instances and Verify Log Service
If the user has not provided WAF Instance ID and SLS configuration, perform auto-discovery:
Step 2a: Discover WAF Instances
# Query WAF instances in both regions in parallel
aliyun waf-openapi DescribeInstance --region cn-hangzhou --RegionId cn-hangzhou --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query
aliyun waf-openapi DescribeInstance --region ap-southeast-1 --RegionId ap-southeast-1 --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryStep 2b: Check Log Service Status (Mandatory Before Querying Logs)
Before retrieving SLS configuration, you MUST first verify that the WAF instance has log service enabled by calling DescribeSlsLogStoreStatus:
aliyun waf-openapi DescribeSlsLogStoreStatus --region <region-id> --InstanceId '<instance-id>' --RegionId '<region-id>' --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query- If the response indicates log service is already enabled (
SlsLogStoreStatusis true/enabled), skip the enable operation and proceed directly to Step 2c (idempotent: no redundant writes). - If log service is not enabled, inform the user that WAF log service must be activated before log queries can proceed. With user consent, call
ModifyUserWafLogStatusto enable it:
aliyun waf-openapi ModifyUserWafLogStatus \
--region <region-id> \
--InstanceId '<instance-id>' \
--Status 1 \
--RegionId '<region-id>' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryConstraint: This skill only supports enabling log service (Status=1). Disabling log service is not permitted. Never call this API withStatus=0.
After enabling, wait a moment and re-verify with DescribeSlsLogStoreStatus to confirm activation.
Step 2c: Retrieve SLS Configuration (Mandatory After Confirming Log Service is Enabled)
Once DescribeSlsLogStoreStatus confirms that log service is enabled, you must immediately call DescribeSlsLogStore to obtain the WAF log Project and Logstore information:
aliyun waf-openapi DescribeSlsLogStore --region <region-id> --InstanceId '<instance-id>' --RegionId '<region-id>' --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryKey fields in the DescribeSlsLogStore response:
| Field | Description |
|---|---|
ProjectName | SLS Project name associated with the WAF instance |
LogStoreName | SLS Logstore name for WAF logs |
Ttl | Log retention period (in days) |
Cross-region note: The SLS log storage region may differ from the WAF instance region (e.g., WAF in ap-southeast-1 but SLS logs stored in ap-southeast-5). When querying SLS in Step 3, always use the region where the SLS Project is located, not the WAF instance region.
Step 3: Query SLS Logs
Use the ProjectName, LogStoreName and SLS region obtained from Step 2 to query block logs (prefer using the Python script):
# Query using script (recommended, supports automatic time range expansion)
python3 scripts/get_waf_logs.py \
--project <project-name> \
--logstore <logstore-name> \
--request-id <request-id> \
--region <sls-region>Or use CLI directly:
TO_TIME=$(python3 -c "import time; print(int(time.time()))")
FROM_TIME=$((TO_TIME - 86400))
aliyun sls get-logs \
--project <project-name> \
--logstore <logstore-name> \
--from $FROM_TIME \
--to $TO_TIME \
--query "<request-id>" \
--region <sls-region> \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryImportant: The --region here must be the SLS log storage region, which may differ from the WAF instance region. Check the DescribeSlsLogStore response from Step 2 to determine the correct SLS region.
Step 4: Query Rule Details
Extract rule_id and final_plugin from the logs to query the rule configuration:
Important: The DescribeDefenseRule API requires the DefenseScene parameter. Common defense scenes include:
custom_acl- Custom access control rulescustom_cc- Custom rate limiting rules (CC rules)waf_group- WAF protection rulesantiscan- Anti-scan rulesdlp- Data leakage preventiontamperproof- Anti-tampering
You can determine the defense scene from final_plugin field in the logs:
| final_plugin | DefenseScene |
|---|---|
| customrule | custom_acl or custom_cc |
| waf | waf_group |
| scanner_behavior | antiscan |
| dlp | dlp |
# Query rule details with DefenseScene
aliyun waf-openapi DescribeDefenseRule \
--region <region-id> \
--InstanceId '<instance-id>' \
--TemplateId <template-id> \
--RuleId <rule-id> \
--DefenseScene '<defense-scene>' \
--RegionId '<region-id>' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryNote: If you don't know the TemplateId, first use DescribeDefenseTemplates to list templates:
aliyun waf-openapi DescribeDefenseTemplates \
--region <region-id> \
--InstanceId '<instance-id>' \
--DefenseScene '<defense-scene>' \
--RegionId '<region-id>' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryStep 5: Output Analysis Report
Output using the following template:
## WAF Block Analysis Report
### Request Information
- Request ID: {request_id}
- Block Time: {time}
- Client IP: {real_client_ip (masked, e.g. 192.***.***.***)}
- Request URL: {host}{request_path}?{masked_query_params}
### Block Details
- Rule ID: {rule_id}
- Rule Name: {rule_name}
- Action: {action}
### Recommendations
{Provide recommendations based on rule type, refer to references/common-block-reasons.md}Troubleshooting
No Logs Found
1. Re-check global log service status (should have been verified in Step 2b, but re-confirm):
aliyun waf-openapi DescribeSlsLogStoreStatus --region <region-id> --InstanceId '<instance-id>' --RegionId '<region-id>' --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryIf not enabled, prompt the user and enable with ModifyUserWafLogStatus (see Step 2b). Only enabling (Status=1) is allowed.
2. Check protection object log switch:
aliyun waf-openapi DescribeResourceLogStatus --region <region-id> --InstanceId '<instance-id>' --RegionId '<region-id>' --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query3. Enable protection object log collection (check-then-act: only if DescribeResourceLogStatus shows log collection is disabled for the target resource; skip if already enabled):
aliyun waf-openapi ModifyResourceLogStatus \
--region <region-id> \
--InstanceId '<instance-id>' \
--Resource '<resource-name>' \
--Status true \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-querySee references/common-block-reasons.md for protection object naming conventions.
Permission Denied Errors
If you encounter permission errors, check the following:
1. Verify CLI profile configuration:
aliyun configure list2. Check RAM policy permissions: Required permissions:
waf-openapi:DescribeInstancewaf-openapi:DescribeSlsLogStoreStatuswaf-openapi:DescribeSlsLogStorewaf-openapi:ModifyUserWafLogStatus(optional, for enabling log service)waf-openapi:DescribeDefenseRule(for rule details)sls:GetLogs(for log queries)
3. Try specifying a different profile:
aliyun waf-openapi DescribeInstance --profile <profile-name> --region <region-id> --user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryRequest ID Not Found
If the Request ID is not found in the logs:
1. Verify Request ID format: Should be 32 characters without hyphens 2. Check time range: The script automatically expands search up to 90 days 3. Verify the correct region: Try both cn-hangzhou and ap-southeast-1 4. Check log retention (TTL): Default is 180 days, use --ttl parameter if different
Multi-Instance Scenarios
If both Chinese Mainland and non-Chinese Mainland instances exist, determine based on query results:
- Logs found in only one region -> use that region directly
- Logs found in both regions -> ask the user for clarification
- No logs found in either region -> ask the user for the expected region, check protection object log switch
Note: Follow the same discovery commands as in Step 2, then query logs across all discovered SLS projects until the Request ID is found.
Rule Operation Constraints
Warning: Rule Disabling Policy
When the user requests to disable a rule: 1. Check current rule status first — call DescribeDefenseRule to query the rule's current status. If the rule is already in the target state (e.g., already disabled), skip the write operation and inform the user (idempotent check-then-act pattern) 2. Only perform disable operations (ModifyDefenseRuleStatus with RuleStatus=0) 3. Never delete rules 4. Never modify rule content 5. Must confirm with user before executing
# Disable a rule (only after confirming it is currently enabled)
aliyun waf-openapi ModifyDefenseRuleStatus \
--region <region-id> \
--InstanceId '<instance-id>' \
--RuleId <rule-id> \
--RuleStatus 0 \
--RegionId '<region-id>' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-querySee references/rule-operations.md for detailed instructions.
References
- RAM Policy Requirements
- Rule Configuration Details
- Rule Operation Policy
- Common Block Reasons
- WAF OpenAPI
Common WAF Block Reasons and Recommendations
Block Reason Reference Table
| Rule Type | Common Causes | Recommendations |
|---|---|---|
| Custom Access Control (ACL) | URL/parameters matched blacklist rules | Check if request URL and parameters match business expectations |
| CC Protection | Request frequency exceeded threshold | Reduce request frequency, or request CC threshold adjustment |
| IP Blacklist/Whitelist | Client IP is on the blacklist | Verify if IP was blocked by mistake, contact admin to remove |
| Region Blocking | Source region is restricted | Verify if the access region is compliant |
| Bot Management | Identified as malicious crawler | Verify if it is a legitimate crawler, request whitelist addition |
| Data Risk Control | Triggered risk control policy | Check if request behavior is normal |
Protection Object Naming Conventions
Protection objects are named differently based on the access method:
| Access Method | Protection Object Name Example | Description |
|---|---|---|
| CNAME Access | hhd.aliyundemo.com-waf | Domain name + -waf suffix |
| ALB Cloud Product Access | alb-ofywk004eo08ou0hqe-alb | ALB instance ID + -alb suffix |
| MSE Route-Level Access | testzhukuoroute-gw-f3d2135cd0674b2199fab5a4186596e2-mse | Route name + -mse suffix |
| ECS Instance Port-Level Access | i-2ze9eanh176rq8p1o0l7-80-ecs | ECS instance ID + port + -ecs suffix |
| Domain + ALB Instance-Level Access | abc.test.com-alb-4zej9hs2bz41kq2g52-alb | Domain name + ALB instance ID + -alb suffix |
RAM Policy Requirements
This skill requires the following RAM permissions to operate correctly.
Minimum Required Permissions
WAF OpenAPI Permissions
| Action | Resource | Description |
|---|---|---|
waf:DescribeInstance | * | Query WAF instance information |
waf:DescribeSlsLogStore | * | Get SLS log storage configuration |
waf:DescribeSlsLogStoreStatus | * | Check global log service status |
waf:DescribeResourceLogStatus | * | Check protection object log switch |
waf:DescribeDefenseTemplates | * | List defense templates |
waf:DescribeDefenseRule | * | Query defense rule details |
waf:DescribeDefenseRules | * | List defense rules |
waf:DescribeBaseSystemRules | * | Query built-in system rule details |
SLS Permissions
| Action | Resource | Description |
|---|---|---|
log:GetLogStoreLogs | acs:log:*:*:project/<waf-sls-project>/logstore/<waf-logstore> | Query WAF block logs from SLS |
Optional Permissions (Rule Operations)
These permissions are only needed when the user requests to disable a WAF rule:
| Action | Resource | Description |
|---|---|---|
waf:ModifyDefenseRuleStatus | * | Disable/enable a defense rule (RuleStatus=0/1 only) |
Optional Permissions (Log Service Management)
These permissions are only needed when enabling log service or log collection for a protection object:
| Action | Resource | Description |
|---|---|---|
waf:ModifyUserWafLogStatus | * | Enable WAF log service for an instance (enable only, disable is not permitted) |
waf:ModifyResourceLogStatus | * | Enable/disable log collection for a protection object |
Sample RAM Policy (JSON)
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"waf:DescribeInstance",
"waf:DescribeSlsLogStore",
"waf:DescribeSlsLogStoreStatus",
"waf:DescribeResourceLogStatus",
"waf:DescribeDefenseTemplates",
"waf:DescribeDefenseRule",
"waf:DescribeDefenseRules",
"waf:DescribeBaseSystemRules"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"log:GetLogStoreLogs"
],
"Resource": "acs:log:*:*:project/*/logstore/*"
}
]
}Notes
- The WAF resources use
*because WAF instance IDs are dynamically discovered during execution. - The SLS resource can be narrowed to specific projects/logstores if known in advance.
- Rule modification permissions (
ModifyDefenseRuleStatus) are intentionally excluded from the base policy. Only grant when rule disable operations are needed. - This skill never calls
DeleteDefenseRuleorModifyDefenseRule— those actions are explicitly prohibited.
WAF Rule Configuration Details
Config Field Description
| Parameter | Type | Description |
|---|---|---|
action | string | Action to execute (block - block request, monitor - observe only) |
ccStatus | int | CC rule indicator: 1 - custom rate limiting rule, 0 - custom access control rule |
effect | string | Only valid when ccStatus=1, scope of effect after blacklisting |
conditions | array | List of matching conditions |
Important Notes - ccStatus and effect
ccStatus Parameter
1- The rule is a custom rate limiting rule (CC rule)0- The rule is a custom access control rule (ACL rule)
effect Parameter (only valid when ccStatus=1)
service- After blacklisting, takes effect on the entire protection object (i.e.,matched_hostin SLS logs)rule- After blacklisting, takes effect only within the scope of this rule (must satisfy rule matching conditions)
Note: When ccStatus=0, the effect parameter is meaningless and can be ignored.
Example Configuration Interpretation
{
"action": "block",
"ccStatus": 0, // ACL rule, not a CC rule
"effect": "service", // Meaningless because ccStatus=0
"conditions": [{"key": "URL", "opValue": "contain", "values": "/test"}]
}Common Rule ID Prefixes
| Prefix | Rule Type |
|---|---|
| 101xxx | Custom Access Control (ACL) |
| 102xxx | CC Protection Rules |
| 103xxx | IP Blacklist/Whitelist |
| 104xxx | Region Blocking |
| 105xxx | Bot Management |
| 106xxx | Data Risk Control |
SLS Log Key Fields
| Field | Description |
|---|---|
request_traceid | Request ID |
final_rule_id | Block rule ID |
final_plugin | Block plugin type (e.g., acl, cc, etc.) |
final_action | Action executed (block - blocked, monitor - observed) |
status | HTTP response status code |
real_client_ip | Real client IP |
host | Request domain |
request_uri | Request URI |
WAF Rule Operation Policy
Warning: Rule Disabling Policy (Important!)
When the user requests to disable a rule, the following constraints must be followed:
1. Only Perform Disable Operations
Only call ModifyDefenseRule or ModifyDefenseTemplate to set the rule status to Status=0
2. Never Delete Rules
Even if the disable operation fails, you must not call DeleteDefenseRule to delete the rule
3. Never Modify Rule Content
Do not modify rule matching conditions, actions, or other configurations
4. Failure Handling
- If the disable operation fails, inform the user of the failure reason
- Do not attempt to delete the rule or use other workarounds
- Wait for the user's new instructions before performing any other operations
5. Idempotent Check-Then-Act (Required)
Before executing any write operation, always query the current state first and skip the operation if the resource is already in the target state:
# Step 1: Check current rule status
aliyun waf-openapi DescribeDefenseRule \
--region <region-id> \
--InstanceId '<instance-id>' \
--TemplateId <template-id> \
--RuleId <rule-id> \
--DefenseScene '<defense-scene>' \
--RegionId '<region-id>' \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query
# Step 2: Only proceed if the rule is NOT already in the target state
# If the rule is already disabled (Status=0), skip the disable call
# If the rule is already enabled (Status=1), skip the enable callRationale: This check-then-act pattern ensures idempotent behavior — repeated execution produces no additional side effects. It prevents unnecessary API calls and provides clear feedback to the user about the current state.
6. Pre-Operation Confirmation
Confirm operation: Disable rule {rule_name} (ID: {rule_id})
- Operation type: Disable (Status=0)
- Will not delete the rule
- Will not modify rule content
- Can be re-enabled at any time
Continue? Reply "yes" to confirm---
Example Commands
Recommended: Use ModifyDefenseRuleStatus (Simple and Direct)
Disable a rule:
aliyun waf-openapi ModifyDefenseRuleStatus \
--region ap-southeast-1 \
--InstanceId 'waf_v2_public_cn-xxx' \
--RuleId 20400384 \
--RuleStatus 0 \
--RegionId ap-southeast-1 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryEnable a rule:
aliyun waf-openapi ModifyDefenseRuleStatus \
--region ap-southeast-1 \
--InstanceId 'waf_v2_public_cn-xxx' \
--RuleId 20400384 \
--RuleStatus 1 \
--RegionId ap-southeast-1 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryAlternative: Use ModifyDefenseRule (Requires Full Configuration)
aliyun waf-openapi ModifyDefenseRule \
--region ap-southeast-1 \
--InstanceId waf_v2_public_cn-xxx \
--Rules '{"id": 20400384, "Status": 0, "Config": "..."}' \
--RegionId ap-southeast-1 \
--user-agent AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-queryNote:ModifyDefenseRulerequires passing the complete rule configuration with complex parameters. It is recommended to useModifyDefenseRuleStatusfirst.
Wrong: Never Delete (Even on Failure)
aliyun waf-openapi DeleteDefenseRule ... # ForbiddenWrong: Never Modify Configuration
aliyun waf-openapi ModifyDefenseRule \
--Rules '{"id": 20400384, "Config": {"action": "monitor"}}' # Forbidden---
Operation Flowchart
User requests to disable/enable a rule
|
Confirm rule information (RuleId, InstanceId, Region)
|
Check current rule status via DescribeDefenseRule <-- Idempotent check
|
+---------------------+
| Already in target |
| state? |
+------+--------------+
Yes | No
| |
Inform user Confirm operation with user
(no action (disable only, no deletion)
needed) |
Execute ModifyDefenseRuleStatus
|
+-------------+
| Success? |
+------+------+
Yes | No
| |
| Report failure reason
| Wait for user's new instructions
| (Do not attempt to delete)
v
Operation complete#!/usr/bin/env python3
"""
WAF SLS Log Query Script
Generates timestamps and calls aliyun sls get-logs to query WAF block logs
"""
import subprocess
import sys
import json
import time
import argparse
import re
# User-Agent header for all Alibaba Cloud API calls
ALIYUN_USER_AGENT = "AlibabaCloud-Agent-Skills/alibabacloud-waf-checkresponse-intercept-query"
# ---------------------------------------------------------------------------
# Sensitive data masking helpers
# ---------------------------------------------------------------------------
# Fields that require masking in log output
_SENSITIVE_LOG_FIELDS = {
'real_client_ip', 'remote_addr', 'client_ip', 'src_ip',
'http_user_agent', 'user_agent',
'cookie', 'http_cookie', 'set_cookie',
'authorization', 'token', 'secret',
}
def _mask_ip(ip_str):
"""Mask an IP address, preserving only the first octet (IPv4) or prefix (IPv6).
Examples:
'192.168.1.100' -> '192.***.***.***'
'2001:db8::1' -> '2001:****:****:****'
"""
if not ip_str or not isinstance(ip_str, str):
return ip_str
ip_str = ip_str.strip()
if ':' in ip_str and '.' not in ip_str: # IPv6
parts = ip_str.split(':')
if len(parts) >= 2:
return parts[0] + ':****:****:****'
return ip_str
# IPv4 (may also contain port like 1.2.3.4:8080)
host = ip_str.split(':')[0] if ':' in ip_str else ip_str
octets = host.split('.')
if len(octets) == 4:
return f"{octets[0]}.***.***.***"
return ip_str
def _mask_uri(uri_str):
"""Mask query parameters in a URI while preserving the path.
Examples:
'/api/v1/user?token=abc123&name=test' -> '/api/v1/user?token=***&name=***'
'/static/page' -> '/static/page'
"""
if not uri_str or not isinstance(uri_str, str):
return uri_str
if '?' not in uri_str:
return uri_str
path, query = uri_str.split('?', 1)
masked_params = []
for param in query.split('&'):
if '=' in param:
key, _ = param.split('=', 1)
masked_params.append(f"{key}=***")
else:
masked_params.append(param)
return f"{path}?{'&'.join(masked_params)}"
def _mask_user_agent(ua_str):
"""Truncate User-Agent to first 32 chars to reduce PII exposure."""
if not ua_str or not isinstance(ua_str, str):
return ua_str
if len(ua_str) <= 32:
return ua_str
return ua_str[:32] + '...'
def _mask_field_value(field_key, value):
"""Apply appropriate masking based on the field key."""
field_lower = field_key.lower()
if field_lower in ('real_client_ip', 'remote_addr', 'client_ip', 'src_ip'):
return _mask_ip(str(value))
if field_lower in ('request_uri', 'uri', 'querystring', 'query_string'):
return _mask_uri(str(value))
if field_lower in ('http_user_agent', 'user_agent'):
return _mask_user_agent(str(value))
if field_lower in ('cookie', 'http_cookie', 'set_cookie',
'authorization', 'token', 'secret'):
return '******'
return value
def _is_sensitive_field(field_key):
"""Check if a field contains potentially sensitive data."""
fl = field_key.lower()
return (fl in _SENSITIVE_LOG_FIELDS or
'cookie' in fl or 'token' in fl or 'secret' in fl or
'password' in fl or 'auth' in fl or 'credential' in fl)
def get_current_timestamp():
"""Get current Unix timestamp (seconds)"""
return int(time.time())
def query_sls_logs(project, logstore, request_id, region, ttl=90):
"""
Query SLS logs with automatic time range expansion
Args:
project: SLS Project name
logstore: SLS Logstore name
request_id: Request ID to query
region: SLS region
ttl: Log retention period (days), default 90
Returns:
Query results (list of dicts)
"""
to_time = get_current_timestamp()
max_from_time = to_time - ttl * 86400 # Maximum lookback time
# Initial time range: last 24 hours
from_time = to_time - 86400
# Progressively expand time range
time_ranges = [
(to_time - 86400, "last 24 hours"),
(to_time - 86400 * 3, "last 3 days"),
(to_time - 86400 * 7, "last 7 days"),
(to_time - 86400 * 30, "last 30 days"),
(max_from_time, f"last {ttl} days (maximum range)"),
]
for from_ts, range_desc in time_ranges:
# Ensure not exceeding maximum lookback time
if from_ts < max_from_time:
from_ts = max_from_time
range_desc = f"last {ttl} days (maximum range)"
print(f"\nQuerying logs for {range_desc}...")
print(f"Time range: {from_ts} ({time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(from_ts))}) -> {to_time} ({time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(to_time))})")
# Build aliyun sls command
cmd = [
"aliyun", "sls", "get-logs",
"--project", project,
"--logstore", logstore,
"--from", str(from_ts),
"--to", str(to_time),
"--query", request_id,
"--reverse", "true",
"--region", region,
"--user-agent", ALIYUN_USER_AGENT
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
if result.returncode == 0:
try:
logs = json.loads(result.stdout)
if logs and len(logs) > 0:
print(f"Found {len(logs)} log record(s)")
return logs
else:
print(f"No logs found in this time range")
except json.JSONDecodeError:
print(f"Failed to parse response")
print(f"Raw output: {result.stdout[:200]}")
else:
print(f"Query failed: {result.stderr[:200]}")
except subprocess.TimeoutExpired:
print(f"Query timed out")
except Exception as e:
print(f"Query error: {e}")
# Stop querying if maximum range is reached
if from_ts <= max_from_time:
break
print(f"\nRequest ID not found in any time range: {request_id}")
return []
def parse_log_entry(log):
"""Parse a single log entry and extract key information (with masking)"""
key_fields = {
'request_traceid': 'Request ID',
'final_rule_id': 'Rule ID',
'final_plugin': 'Block Plugin',
'final_action': 'Action',
'status': 'HTTP Status',
'real_client_ip': 'Client IP',
'host': 'Domain',
'request_uri': 'Request URI',
'request_method': 'Request Method',
'http_user_agent': 'User-Agent',
'time': 'Time',
}
parsed = {}
for key, label in key_fields.items():
if key in log:
parsed[label] = _mask_field_value(key, log[key])
return parsed
def query_rule_detail(instance_id, rule_id, region):
"""
Query rule details using the DescribeDefenseRule API
Args:
instance_id: WAF instance ID
rule_id: Rule ID
region: WAF region
Returns:
Rule detail dict, or None on failure
"""
cmd = [
"aliyun", "waf-openapi", "DescribeDefenseRule",
"--region", region,
"--InstanceId", instance_id,
"--RuleId", str(rule_id),
"--RegionId", region,
"--user-agent", ALIYUN_USER_AGENT
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
try:
data = json.loads(result.stdout)
return data.get('Rule', {})
except json.JSONDecodeError:
return None
else:
return None
except Exception:
return None
def parse_rule_config(rule):
"""
Parse rule configuration content
Reference: https://help.aliyun.com/zh/waf/web-application-firewall-3-0/developer-reference/api-waf-openapi-2021-10-01-createdefenserule
Important notes:
- ccStatus: 1 means custom rate limiting rule (CC rule), 0 means custom access control rule (ACL rule)
- effect: Only valid when ccStatus=1, indicates the scope of effect after blacklisting
- service: Entire protection object (matched_host)
- rule: Only within this rule's scope (must satisfy matching conditions)
"""
if not rule:
return None
config = {}
# Basic information
config['rule_id'] = rule.get('RuleId')
config['rule_name'] = rule.get('RuleName')
config['status'] = 'Enabled' if rule.get('Status') == 1 else 'Disabled'
config['defense_origin'] = rule.get('DefenseOrigin', 'N/A')
config['defense_scene'] = rule.get('DefenseScene', 'N/A')
config['gmt_modified'] = rule.get('GmtModified')
# Parse Config field (JSON string)
try:
rule_config = json.loads(rule.get('Config', '{}'))
# Action configuration
config['action'] = rule_config.get('action', 'N/A')
config['name'] = rule_config.get('name', 'N/A')
# CC protection configuration
cc_status = rule_config.get('ccStatus', 0)
config['cc_status'] = cc_status
config['is_cc_rule'] = (cc_status == 1)
# Rule type description
if cc_status == 1:
config['rule_type'] = 'Custom Rate Limiting Rule (CC Rule)'
# effect parameter is only valid for CC rules
effect = rule_config.get('effect', 'N/A')
config['effect'] = effect
if effect == 'service':
config['effect_desc'] = 'After blacklisting, takes effect on the entire protection object'
elif effect == 'rule':
config['effect_desc'] = 'After blacklisting, takes effect only within the rule scope'
else:
config['effect_desc'] = 'Unknown'
else:
config['rule_type'] = 'Custom Access Control Rule (ACL Rule)'
# effect parameter is meaningless for ACL rules
config['effect'] = None
config['effect_desc'] = 'N/A (only valid for CC rules)'
# Matching conditions
conditions = []
for cond in rule_config.get('conditions', []):
conditions.append({
'key': cond.get('key', 'N/A'),
'op_code': cond.get('opCode', 'N/A'),
'op_value': cond.get('opValue', 'N/A'),
'values': cond.get('values', 'N/A')
})
config['conditions'] = conditions
# Rate limiting configuration (CC rules)
if 'ratelimit' in rule_config:
config['rate_limit'] = rule_config['ratelimit']
# Time configuration
if 'timeConfig' in rule_config:
config['time_config'] = rule_config['timeConfig']
# Canary configuration
if 'grayStatus' in rule_config:
config['gray_status'] = rule_config['grayStatus']
if 'grayConfig' in rule_config:
config['gray_config'] = rule_config['grayConfig']
except json.JSONDecodeError:
config['config_raw'] = rule.get('Config', 'N/A')
return config
def print_log_analysis(logs, instance_id=None, region=None):
"""Print log analysis results including rule details"""
if not logs:
return
print("\n" + "="*60)
print("WAF Block Analysis Report")
print("="*60)
for idx, log in enumerate(logs, 1):
parsed = parse_log_entry(log)
print(f"\n[Log Record {idx}]")
print("-"*60)
# Request information
print("\nRequest Information:")
for key in ['Request ID', 'Time', 'Client IP', 'Request Method', 'Domain', 'Request URI', 'User-Agent']:
if key in parsed:
print(f" {key}: {parsed[key]}")
# Block details
print("\nBlock Details:")
for key in ['Rule ID', 'Block Plugin', 'Action', 'HTTP Status']:
if key in parsed:
print(f" {key}: {parsed[key]}")
# Query and display rule details
rule_id = log.get('final_rule_id')
if rule_id and instance_id and region:
print("\nRule Details:")
rule = query_rule_detail(instance_id, rule_id, region)
if rule:
config = parse_rule_config(rule)
if config:
print(f" Rule Name: {config.get('rule_name', 'N/A')}")
print(f" Rule Status: {config.get('status', 'N/A')}")
print(f" Defense Origin: {config.get('defense_origin', 'N/A')}")
print(f" Defense Scene: {config.get('defense_scene', 'N/A')}")
print(f" Rule Type: {config.get('rule_type', 'N/A')}")
# CC rule specific information
if config.get('is_cc_rule'):
print(f" Effect Scope: {config.get('effect', 'N/A')}")
print(f" Effect Description: {config.get('effect_desc', 'N/A')}")
# Display rate limiting configuration
if 'rate_limit' in config:
print(f" Rate Limit Config: {config['rate_limit']}")
# Display matching conditions
conditions = config.get('conditions', [])
if conditions:
print(f"\n Matching Conditions:")
for i, cond in enumerate(conditions, 1):
print(f" Condition {i}:")
print(f" Field: {cond.get('key', 'N/A')}")
print(f" Operator: {cond.get('op_value', cond.get('op_code', 'N/A'))}")
print(f" Value: {cond.get('values', 'N/A')}")
else:
print(f" Failed to parse rule configuration")
else:
print(f" Unable to retrieve rule details (may require permissions)")
# Raw log (optional)
if len(logs) == 1:
print("\nFull Log Fields:")
for key, value in sorted(log.items()):
if key not in ['request_traceid', 'final_rule_id', 'final_plugin', 'final_action',
'status', 'real_client_ip', 'host', 'request_uri', 'request_method',
'http_user_agent', 'time', '__source__', '__time__', '__topic__']:
# Mask sensitive fields in raw log output
if _is_sensitive_field(key):
display_value = _mask_field_value(key, value)
else:
display_value = value if len(str(value)) < 50 else str(value)[:50] + "..."
print(f" {key}: {display_value}")
print("\n" + "="*60)
# ---------------------------------------------------------------------------
# Input validation helpers
# ---------------------------------------------------------------------------
# Allowed Alibaba Cloud region IDs (non-exhaustive but covers all public regions)
_VALID_REGIONS = {
# China mainland
'cn-hangzhou', 'cn-shanghai', 'cn-beijing', 'cn-shenzhen', 'cn-zhangjiakou',
'cn-huhehaote', 'cn-wulanchabu', 'cn-chengdu', 'cn-qingdao', 'cn-guangzhou',
'cn-nanjing', 'cn-fuzhou', 'cn-heyuan',
# International
'ap-southeast-1', 'ap-southeast-2', 'ap-southeast-3', 'ap-southeast-5',
'ap-southeast-6', 'ap-southeast-7', 'ap-south-1', 'ap-northeast-1',
'ap-northeast-2', 'us-east-1', 'us-west-1', 'eu-west-1', 'eu-central-1',
'me-east-1', 'me-central-1',
# China Finance / Gov
'cn-hangzhou-finance', 'cn-shanghai-finance-1', 'cn-shenzhen-finance-1',
'cn-beijing-finance-1', 'cn-north-2-gov-1',
}
# Pattern: alphanumeric, hyphens, underscores (SLS project / logstore names)
_SLS_NAME_RE = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$')
# Pattern: request trace ID — hex, alphanumeric, hyphens (e.g. UUIDs, trace IDs)
_REQUEST_ID_RE = re.compile(r'^[a-zA-Z0-9-]{1,128}$')
# Pattern: WAF instance ID (e.g. waf_v3cdnrecognition-cn-xxx, waf-cn-xxx)
_INSTANCE_ID_RE = re.compile(r'^[a-zA-Z0-9_-]{1,128}$')
def _validate_sls_name(value, label):
"""Validate SLS project / logstore name format."""
if not _SLS_NAME_RE.match(value):
raise argparse.ArgumentTypeError(
f"Invalid {label}: '{value}'. "
f"Must start with alphanumeric and contain only [a-zA-Z0-9_-], max 128 chars."
)
return value
def _validate_request_id(value):
"""Validate request ID format (alphanumeric + hyphens)."""
if not _REQUEST_ID_RE.match(value):
raise argparse.ArgumentTypeError(
f"Invalid request ID: '{value}'. "
f"Must contain only [a-zA-Z0-9-], max 128 chars."
)
return value
def _validate_region(value):
"""Validate region is a known Alibaba Cloud region ID."""
if value not in _VALID_REGIONS:
raise argparse.ArgumentTypeError(
f"Invalid region: '{value}'. "
f"Must be a valid Alibaba Cloud region ID (e.g. cn-hangzhou, ap-southeast-1)."
)
return value
def _validate_instance_id(value):
"""Validate WAF instance ID format."""
if not _INSTANCE_ID_RE.match(value):
raise argparse.ArgumentTypeError(
f"Invalid instance ID: '{value}'. "
f"Must contain only [a-zA-Z0-9_-], max 128 chars."
)
return value
def _validate_ttl(value):
"""Validate TTL is a positive integer within a reasonable range."""
try:
ivalue = int(value)
except (ValueError, TypeError):
raise argparse.ArgumentTypeError(f"Invalid TTL: '{value}'. Must be a positive integer.")
if ivalue < 1 or ivalue > 3650:
raise argparse.ArgumentTypeError(
f"TTL out of range: {ivalue}. Must be between 1 and 3650 days."
)
return ivalue
def main():
parser = argparse.ArgumentParser(description='Query WAF SLS block logs')
parser.add_argument('--project', required=True,
type=lambda v: _validate_sls_name(v, 'project'),
help='SLS Project name')
parser.add_argument('--logstore', required=True,
type=lambda v: _validate_sls_name(v, 'logstore'),
help='SLS Logstore name')
parser.add_argument('--request-id', required=True,
type=_validate_request_id,
help='Request ID to query')
parser.add_argument('--region', default='ap-southeast-5',
type=_validate_region,
help='SLS region (default: ap-southeast-5)')
parser.add_argument('--ttl', type=_validate_ttl, default=90,
help='Log retention period in days (default: 90, max: 3650)')
parser.add_argument('--json', action='store_true', help='Output raw logs in JSON format')
parser.add_argument('--instance-id',
type=_validate_instance_id,
help='WAF instance ID (for querying rule details)')
parser.add_argument('--waf-region',
type=_validate_region,
help='WAF region (for querying rule details, defaults to --region)')
args = parser.parse_args()
# WAF region defaults to SLS region
waf_region = args.waf_region if args.waf_region else args.region
print("="*60)
print("WAF SLS Log Query")
print("="*60)
print(f"Project: {args.project}")
print(f"Logstore: {args.logstore}")
print(f"Request ID: {args.request_id}")
print(f"Region: {args.region}")
print(f"Current timestamp: {get_current_timestamp()} ({time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(get_current_timestamp()))})")
# Query logs
logs = query_sls_logs(args.project, args.logstore, args.request_id, args.region, args.ttl)
if logs:
if args.json:
# JSON format output — mask sensitive fields before emitting
sanitized_logs = []
for log in logs:
sanitized = {}
for k, v in log.items():
if _is_sensitive_field(k):
sanitized[k] = _mask_field_value(k, v)
elif k.lower() in ('request_uri', 'uri', 'querystring', 'query_string'):
sanitized[k] = _mask_uri(str(v))
else:
sanitized[k] = v
sanitized_logs.append(sanitized)
print("\n" + json.dumps(sanitized_logs, indent=2, ensure_ascii=False))
else:
# Analysis format output (with rule details)
print_log_analysis(logs, args.instance_id, waf_region)
return 0
else:
print("\nSuggestions:")
print(" 1. Verify the Request ID is correct")
print(" 2. Confirm that the log service is enabled")
print(" 3. Wait 3-5 minutes and retry (log sync delay)")
return 1
if __name__ == '__main__':
sys.exit(main())