Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions commands/host/ai-prompts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ echo_yellow() { echo -e "${YELLOW}$1${NC}" >&2; }
# Menu options
OPTIONS=(
"1. BackstopJS Init"
"2. Access Log Forensics"
)

# AI Agent options
Expand Down Expand Up @@ -103,6 +104,62 @@ backstop_init() {
esac
}

# Access Log Forensics function
access_log_forensics() {
# Pick a log file - prefer fzf over *.log files in current dir, fallback to manual input
local log_file
local log_files
log_files=$(find . -maxdepth 1 -name "*_access.log" 2>/dev/null)

if [[ -n "$log_files" ]]; then
log_file=$(echo "$log_files" | sed 's|^\./||' | fzf --reverse --height=50% --header="Select access log file")
fi

if [[ -z "$log_file" ]]; then
echo_yellow "No .log files found in current directory. Enter path manually:"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this allow us to paste an Upsun shared log file location as per their docs - https://developer.upsun.com/docs/observability/logs/access-logs#sharing-activity-logs. I'm guessing most log files we will be interested in will be production ones living on Upsun/Aquia.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Activity logs are not access logs. You'll need the access.log file. ddev ucc has a helper for this.
Haven't worked on Acquia regarding this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rare case, but I committed the helper along other improvements, it's here:
c668f5f#diff-dd1fb0a2215b0ac89cfced16af666f6513ce4a61685aa23896041431585c2a1a

read -r log_file
fi

if [[ -z "$log_file" || ! -f "$log_file" ]]; then
echo_red "File not found: $log_file"
exit 1
fi

# Select AI agent
local agent_selection
agent_selection=$(select_agent)
if [[ -z "$agent_selection" ]]; then
echo_red "No agent selected... Exiting"
exit 0
fi

local agent_label
agent_label=$(echo "$agent_selection" | sed 's/^[0-9]*\. //')

echo_green "Running Access Log Forensics with ${agent_label} on ${log_file}..."
echo ""

local prompt_content
prompt_content=$(sed -e "s|__ACCESS_LOG_FILE__|$log_file|g" \
"${DDEV_APPROOT}/.ddev/scripts/prompts/access-log-forensics.md")

case "$agent_label" in
"Gemini")
gemini "$prompt_content"
;;
"Copilot")
copilot "$prompt_content"
;;
"Claude")
claude "$prompt_content"
;;
*)
echo_red "Unknown agent: $agent_label"
exit 1
;;
esac
}

# Main function
main() {
# Banner
Expand Down Expand Up @@ -136,6 +193,9 @@ main() {
"BackstopJS Init")
backstop_init
;;
"Access Log Forensics")
access_log_forensics
;;
*)
echo_red "Unknown action: $action_label"
exit 1
Expand Down
33 changes: 33 additions & 0 deletions scripts/prompts/access-log-forensics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
I have an Apache access log at __ACCESS_LOG_FILE__
Run the following triage in order, stopping to flag anomalies before proceeding:
**STEP 1 — Baseline (run all at once)**
- Date range of the log
- Total request count
- HTTP status code distribution (flag if 4xx+5xx > 10% of total)
- Requests per hour (flag any hour > 3x the median)

**STEP 2 — Top offenders**
- Top 30 source IPs with request counts
- Group IPs by /24 subnet and flag any subnet with > 500 requests
- Top 20 user agents (flag empty UAs, known scanners, rotating browser strings
doing uniform behaviour)
- Top 20 requested URLs (flag if one URL accounts for > 20% of traffic)

**STEP 3 — Attack signatures (only on IPs/subnets flagged above)**
- What URLs are they hitting? Uniform = likely DDoS/flood. Varied = crawler/scraper.
- What referrers are they sending? Flag non-existent domains.
- What is their request rate per hour? Coordinated spikes = botnet.
- Run whois on the top flagged /24 to identify the owning organisation and country.

**STEP 4 — Verdict**
Produce a short report:
- Is an attack ongoing right now? (check last 30 mins of log)
- Was there a past attack? When did it peak, when did it stop?
- What is the blast radius? (did it cause 500/502s for legitimate users?)
- What is the source? (ASN, country, likely actor type)
- Immediate action: list of CIDR ranges to block
- Abuse contact if available from whois

Keep responses concise. Lead with the verdict, support with numbers.

<!-- #ddev-generated -->
Loading