mirror of
https://github.com/lunchcat/sif.git
synced 2026-07-07 21:07:05 -07:00
Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d863fe8ff | |||
| 08874762ba | |||
| 662994caf1 | |||
| d5d3178783 | |||
| 32acf6b445 | |||
| cbcc4f43dc | |||
| afe55f36ee | |||
| 2ea412b04d | |||
| 60b2dd2804 | |||
| 73af6bc868 | |||
| 88fb01d70a | |||
| d98a1c4e84 | |||
| 8c4d6d81a7 | |||
| b6ad6f7a3e | |||
| 26ea7c0ee6 | |||
| 976a2d4390 | |||
| c62409bf0a | |||
| 2f288c4eb6 | |||
| 280e6ad8b0 | |||
| 1f808bbea3 | |||
| 2ee2283412 | |||
| 748f320e59 | |||
| ad9a98b132 | |||
| 6d6a57a0e0 | |||
| 53687e4bd4 | |||
| 4331929bd0 | |||
| c9497abfcb | |||
| 9290bbe6a0 | |||
| 0958a2c19c | |||
| 4e97da6863 | |||
| e5c88b754c | |||
| 2389901614 | |||
| 1f73a0dd8f | |||
| 4de9786e99 | |||
| e5510a4a16 | |||
| 8928ac7e99 | |||
| 137ba0c89a | |||
| 9ea0805090 | |||
| df6bfc91f0 | |||
| 255b67dff6 | |||
| 9c3a5fe1f0 | |||
| df274328ee | |||
| 487b440e52 | |||
| bf613c3aaf | |||
| 2922481be3 | |||
| e27476652f | |||
| 5ffea24182 | |||
| 67288577a6 | |||
| 7beb68e145 | |||
| d2f3a42c43 | |||
| c33896a45a | |||
| 652f4f0c7c | |||
| bf65820ff1 | |||
| 0d4c10e6ff | |||
| 0eba16bc4d | |||
| c067eafed0 | |||
| f20198dd26 | |||
| a86f117658 | |||
| a614b2ee8a | |||
| a8686c1e4a | |||
| c3f824e1e3 | |||
| 57acc6d37c | |||
| 2596ce1ea2 | |||
| abce1405ca | |||
| f212aed50a | |||
| 7e27e73554 | |||
| da645ee42f | |||
| 16c191fbaa | |||
| 697c7def57 | |||
| ff002f43f7 | |||
| 30482ecbce | |||
| ce07ac8b16 | |||
| 8b056231f7 | |||
| c517e2eb1f | |||
| 0dd533446a | |||
| 1c0ad454dc | |||
| 17aa3c00f0 | |||
| b6b0a5a782 | |||
| 24b3b43b57 | |||
| 1550202e7a | |||
| a8eb319efe | |||
| 43d5f7383b | |||
| 1447485af9 | |||
| 6e1d5cf488 | |||
| 1583e00478 | |||
| 40db023632 | |||
| 5868deef21 | |||
| b43e54bf60 | |||
| c75ebccb27 | |||
| 858555bc47 | |||
| 51ee65c5df | |||
| 035e9406d9 | |||
| ba5468725e | |||
| 49b081dc30 | |||
| 127eeff265 | |||
| 1b493e9572 | |||
| 74b044ce59 | |||
| 2a2bcf5b92 | |||
| 166e1b82c2 | |||
| be9c02e8ba | |||
| 018af224d6 | |||
| 97aeb4c8b0 | |||
| 2d38d3fea5 | |||
| 3df0064e4b | |||
| c20c37463a | |||
| 9190fa4741 |
@@ -1,44 +0,0 @@
|
||||
ci:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ".github/**"
|
||||
|
||||
deps:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- "flake.nix"
|
||||
- "flake.lock"
|
||||
|
||||
scan:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "internal/scan/**"
|
||||
|
||||
nuclei:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "internal/nuclei/**"
|
||||
|
||||
modules:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "internal/modules/**"
|
||||
- "internal/scan/builtin/**"
|
||||
- "internal/scan/js/**"
|
||||
- "modules/**"
|
||||
|
||||
docs:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "**/*.md"
|
||||
- "docs/**"
|
||||
|
||||
tests:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "**/*_test.go"
|
||||
|
||||
config:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "internal/config/**"
|
||||
- ".golangci.yml"
|
||||
- ".editorconfig"
|
||||
@@ -2,11 +2,6 @@ name: automatic rebase
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
rebase:
|
||||
name: Rebase
|
||||
@@ -14,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: automatic rebase
|
||||
|
||||
@@ -5,9 +5,6 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -17,7 +14,7 @@ jobs:
|
||||
name: check for large files
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: check for large files
|
||||
run: |
|
||||
large_files=$(find . -path ./.git -prune -o -type f -size +5M -print)
|
||||
|
||||
@@ -1,39 +1,28 @@
|
||||
name: code quality
|
||||
|
||||
name: qodana
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: "0 6 * * 1" # monday 06:00 UTC
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
qodana:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: set up go
|
||||
uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: initialize codeql
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: go
|
||||
- name: build
|
||||
run: go build ./...
|
||||
- name: perform codeql analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:go"
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
- name: 'Qodana Scan'
|
||||
uses: JetBrains/qodana-action@v2024.3
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
- name: dependency review
|
||||
uses: actions/dependency-review-action@v4
|
||||
continue-on-error: ${{ github.event_name == 'push' }}
|
||||
|
||||
@@ -6,9 +6,6 @@ on:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -17,7 +14,7 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: set up go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
@@ -33,7 +30,7 @@ jobs:
|
||||
matrix:
|
||||
go-version: ["1.23", "1.24"]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: set up go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
|
||||
@@ -8,20 +8,17 @@ on:
|
||||
schedule:
|
||||
- cron: "0 6 * * 1" # monday 06:00 UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
govulncheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: set up go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: install govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
- name: run govulncheck
|
||||
run: govulncheck ./...
|
||||
continue-on-error: true
|
||||
|
||||
@@ -8,14 +8,11 @@ on:
|
||||
paths:
|
||||
- '**.go'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-headers:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: check license headers
|
||||
run: |
|
||||
@@ -44,7 +41,7 @@ jobs:
|
||||
echo ': █▀ █ █▀▀ · Blazing-fast pentesting suite :'
|
||||
echo ': ▄█ █ █▀ · BSD 3-Clause License :'
|
||||
echo ': :'
|
||||
echo ': (c) 2022-2025 vmfunc, xyzeva, :'
|
||||
echo ': (c) 2022-2025 vmfunc (vmfunc), xyzeva, :'
|
||||
echo ': lunchcat alumni & contributors :'
|
||||
echo ': :'
|
||||
echo '·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·'
|
||||
|
||||
@@ -12,19 +12,13 @@ on:
|
||||
types:
|
||||
- created
|
||||
- edited
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
echo_issue_comment:
|
||||
runs-on: ubuntu-latest
|
||||
name: profanity check
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
- name: Profanity check step
|
||||
uses: tailaiw/mind-your-language-action@v1.0.3
|
||||
env:
|
||||
|
||||
@@ -5,18 +5,14 @@ on:
|
||||
paths:
|
||||
- "**/*.md"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
markdownlint:
|
||||
name: runner / markdownlint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: markdownlint
|
||||
uses: reviewdog/action-markdownlint@v0.26.2
|
||||
uses: reviewdog/action-markdownlint@v0.24.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reporter: github-pr-review
|
||||
|
||||
@@ -5,10 +5,6 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -18,7 +14,7 @@ jobs:
|
||||
name: runner / misspell
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: misspell
|
||||
uses: reviewdog/action-misspell@v1.26.0
|
||||
with:
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
name: pr bot
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
|
||||
size:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: label pr size
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const changes = files.reduce((sum, f) => sum + f.additions + f.deletions, 0);
|
||||
|
||||
let size;
|
||||
if (changes < 10) size = "size/xs";
|
||||
else if (changes < 50) size = "size/s";
|
||||
else if (changes < 200) size = "size/m";
|
||||
else if (changes < 500) size = "size/l";
|
||||
else size = "size/xl";
|
||||
|
||||
const sizeLabels = ["size/xs", "size/s", "size/m", "size/l", "size/xl"];
|
||||
const currentLabels = context.payload.pull_request.labels.map(l => l.name);
|
||||
const toRemove = currentLabels.filter(l => sizeLabels.includes(l) && l !== size);
|
||||
|
||||
for (const label of toRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
name: label,
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
labels: [size],
|
||||
});
|
||||
|
||||
ci-summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [label, size]
|
||||
if: always()
|
||||
steps:
|
||||
- uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
const { data: checks } = await github.rest.checks.listForRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: pr.head.sha,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const additions = files.reduce((sum, f) => sum + f.additions, 0);
|
||||
const deletions = files.reduce((sum, f) => sum + f.deletions, 0);
|
||||
const fileCount = files.length;
|
||||
|
||||
let body = `### pr summary\n\n`;
|
||||
body += `**${fileCount}** files changed (+${additions} -${deletions})\n\n`;
|
||||
|
||||
const goFiles = files.filter(f => f.filename.endsWith('.go')).length;
|
||||
const testFiles = files.filter(f => f.filename.endsWith('_test.go')).length;
|
||||
const ciFiles = files.filter(f => f.filename.startsWith('.github/')).length;
|
||||
const modFiles = files.filter(f => f.filename === 'go.mod' || f.filename === 'go.sum').length;
|
||||
|
||||
if (goFiles > 0 || testFiles > 0 || ciFiles > 0 || modFiles > 0) {
|
||||
body += `| category | files |\n|----------|-------|\n`;
|
||||
if (goFiles > 0) body += `| go source | ${goFiles} |\n`;
|
||||
if (testFiles > 0) body += `| tests | ${testFiles} |\n`;
|
||||
if (ciFiles > 0) body += `| ci/workflows | ${ciFiles} |\n`;
|
||||
if (modFiles > 0) body += `| deps | ${modFiles} |\n`;
|
||||
body += `\n`;
|
||||
}
|
||||
|
||||
// find existing bot comment
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
});
|
||||
|
||||
const marker = '<!-- sif-pr-bot -->';
|
||||
body = marker + '\n' + body;
|
||||
|
||||
const existing = comments.find(c =>
|
||||
c.user.type === 'Bot' && c.body.includes(marker)
|
||||
);
|
||||
|
||||
if (existing) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body,
|
||||
});
|
||||
}
|
||||
@@ -2,8 +2,7 @@ name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -19,30 +18,27 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: set up go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
|
||||
- name: extract version
|
||||
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||
|
||||
- name: build for windows
|
||||
run: |
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-windows-amd64.exe ./cmd/sif
|
||||
GOOS=windows GOARCH=386 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-windows-386.exe ./cmd/sif
|
||||
GOOS=windows GOARCH=amd64 go build -o sif-windows-amd64.exe ./cmd/sif
|
||||
GOOS=windows GOARCH=386 go build -o sif-windows-386.exe ./cmd/sif
|
||||
|
||||
- name: build for macOS
|
||||
run: |
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-macos-amd64 ./cmd/sif
|
||||
GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-macos-arm64 ./cmd/sif
|
||||
GOOS=darwin GOARCH=amd64 go build -o sif-macos-amd64 ./cmd/sif
|
||||
GOOS=darwin GOARCH=arm64 go build -o sif-macos-arm64 ./cmd/sif
|
||||
|
||||
- name: build for linux
|
||||
run: |
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-linux-amd64 ./cmd/sif
|
||||
GOOS=linux GOARCH=386 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-linux-386 ./cmd/sif
|
||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X main.version=${{ env.VERSION }}" -o sif-linux-arm64 ./cmd/sif
|
||||
GOOS=linux GOARCH=amd64 go build -o sif-linux-amd64 ./cmd/sif
|
||||
GOOS=linux GOARCH=386 go build -o sif-linux-386 ./cmd/sif
|
||||
GOOS=linux GOARCH=arm64 go build -o sif-linux-arm64 ./cmd/sif
|
||||
|
||||
- name: package releases with modules
|
||||
run: |
|
||||
@@ -61,6 +57,8 @@ jobs:
|
||||
|
||||
- name: build debian packages
|
||||
run: |
|
||||
VERSION="0.1.0-$(git rev-parse --short HEAD)"
|
||||
|
||||
declare -A arch_map=(
|
||||
["sif-linux-amd64"]="amd64"
|
||||
["sif-linux-386"]="i386"
|
||||
@@ -69,7 +67,7 @@ jobs:
|
||||
|
||||
for binary in sif-linux-amd64 sif-linux-386 sif-linux-arm64; do
|
||||
arch="${arch_map[$binary]}"
|
||||
pkg_dir="sif_${{ env.VERSION }}_${arch}"
|
||||
pkg_dir="sif_${VERSION}_${arch}"
|
||||
|
||||
mkdir -p "${pkg_dir}/DEBIAN"
|
||||
mkdir -p "${pkg_dir}/usr/bin"
|
||||
@@ -81,11 +79,11 @@ jobs:
|
||||
|
||||
cat > "${pkg_dir}/DEBIAN/control" << EOF
|
||||
Package: sif
|
||||
Version: ${{ env.VERSION }}
|
||||
Version: ${VERSION}
|
||||
Section: security
|
||||
Priority: optional
|
||||
Architecture: ${arch}
|
||||
Maintainer: vmfunc <celeste@linux.com>
|
||||
Maintainer: vmfunc <celeste@router.sex>
|
||||
Homepage: https://github.com/vmfunc/sif
|
||||
Description: Modular pentesting toolkit
|
||||
sif is a fast, concurrent, and extensible pentesting toolkit written in Go.
|
||||
@@ -115,71 +113,41 @@ jobs:
|
||||
artifact-name: sbom.spdx.json
|
||||
output-file: sbom.spdx.json
|
||||
|
||||
- name: generate changelog
|
||||
id: changelog
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const { data: releases } = await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 1,
|
||||
});
|
||||
- name: set release version
|
||||
run: echo "RELEASE_VERSION=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
|
||||
const prev = releases.length > 0 ? releases[0].tag_name : '';
|
||||
const range = prev ? `${prev}...${context.ref}` : '';
|
||||
|
||||
const { data: commits } = await github.rest.repos.compareCommitsWithBasehead({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
basehead: prev ? `${prev}...${{ github.ref_name }}` : `${{ github.sha }}~10...${{ github.sha }}`,
|
||||
}).catch(() => ({ data: { commits: [] } }));
|
||||
|
||||
let log = '';
|
||||
for (const c of commits.commits || []) {
|
||||
const msg = c.commit.message.split('\n')[0];
|
||||
const sha = c.sha.substring(0, 7);
|
||||
log += `- ${msg} (${sha})\n`;
|
||||
}
|
||||
|
||||
return log || 'initial release';
|
||||
|
||||
- name: create release
|
||||
- name: create release and upload assets
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: sif v${{ env.VERSION }}
|
||||
tag_name: automated-release-${{ env.RELEASE_VERSION }}
|
||||
name: Release ${{ env.RELEASE_VERSION }}
|
||||
body: |
|
||||
## what's changed
|
||||
Automated release v${{ env.RELEASE_VERSION }}
|
||||
|
||||
${{ steps.changelog.outputs.result }}
|
||||
## Assets
|
||||
|
||||
## install
|
||||
Each archive contains the sif binary and built-in modules.
|
||||
|
||||
**homebrew / linuxbrew**
|
||||
```bash
|
||||
# coming soon
|
||||
```
|
||||
- Windows (64-bit): `sif-windows-amd64.zip`
|
||||
- Windows (32-bit): `sif-windows-386.zip`
|
||||
- macOS (64-bit Intel): `sif-macos-amd64.tar.gz`
|
||||
- macOS (64-bit ARM): `sif-macos-arm64.tar.gz`
|
||||
- Linux (64-bit): `sif-linux-amd64.tar.gz`
|
||||
- Linux (32-bit): `sif-linux-386.tar.gz`
|
||||
- Linux (64-bit ARM): `sif-linux-arm64.tar.gz`
|
||||
- Debian/Ubuntu (64-bit): `sif_*_amd64.deb`
|
||||
- Debian/Ubuntu (32-bit): `sif_*_i386.deb`
|
||||
- Debian/Ubuntu (64-bit ARM): `sif_*_arm64.deb`
|
||||
|
||||
**debian / ubuntu**
|
||||
```bash
|
||||
sudo dpkg -i sif_${{ env.VERSION }}_amd64.deb
|
||||
```
|
||||
|
||||
**go install**
|
||||
```bash
|
||||
go install github.com/dropalldatabases/sif/cmd/sif@v${{ env.VERSION }}
|
||||
```
|
||||
|
||||
**binary download** - grab the right archive from below.
|
||||
|
||||
## verification
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
sha256sum -c checksums-sha256.txt
|
||||
```
|
||||
|
||||
For more details, check the [commit history](https://github.com/${{ github.repository }}/commits/main).
|
||||
draft: false
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
prerelease: false
|
||||
files: |
|
||||
sif-windows-amd64.zip
|
||||
sif-windows-386.zip
|
||||
@@ -197,7 +165,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: push to cloudsmith
|
||||
if: ${{ !contains(github.ref_name, '-') }}
|
||||
env:
|
||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
||||
run: |
|
||||
|
||||
@@ -7,9 +7,6 @@ on:
|
||||
branches: [main]
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -18,6 +15,6 @@ jobs:
|
||||
update-report-card:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: update go report card
|
||||
uses: creekorful/goreportcard-action@v1.0
|
||||
|
||||
@@ -7,14 +7,11 @@ on:
|
||||
branches: [main]
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: set up go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
|
||||
@@ -15,11 +15,11 @@ jobs:
|
||||
security-events: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: run scorecard
|
||||
uses: ossf/scorecard-action@v2.4.3
|
||||
uses: ossf/scorecard-action@v2.4.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
|
||||
@@ -5,18 +5,14 @@ on:
|
||||
paths:
|
||||
- "**/*.sh"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
name: runner / shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: shellcheck
|
||||
uses: reviewdog/action-shellcheck@v1.32.0
|
||||
uses: reviewdog/action-shellcheck@v1.27.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reporter: github-pr-review
|
||||
|
||||
@@ -6,16 +6,12 @@ on:
|
||||
- "**/*.yml"
|
||||
- "**/*.yaml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
yamllint:
|
||||
name: runner / yamllint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: yamllint
|
||||
uses: reviewdog/action-yamllint@v1.19.0
|
||||
with:
|
||||
|
||||
+7
-23
@@ -10,10 +10,12 @@ linters:
|
||||
- gocritic # opinionated lints
|
||||
- revive # replacement for golint
|
||||
- unconvert # unnecessary type conversions
|
||||
- prealloc # slice preallocation hints
|
||||
- bodyclose # http response body not closed
|
||||
- noctx # http requests without context
|
||||
- gosec # security issues
|
||||
- errorlint # error wrapping and comparison
|
||||
- gocognit # cognitive complexity
|
||||
- nilnil # return nil, nil
|
||||
- wastedassign # assignments to variables never read
|
||||
- usetesting # os.Setenv in tests instead of t.Setenv, etc.
|
||||
@@ -21,35 +23,23 @@ linters:
|
||||
linters-settings:
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- fieldalignment # too many structs to reorder, risks breaking serialization
|
||||
- shadow # common Go pattern, too noisy
|
||||
- unusedwrite # false positives on test data structs
|
||||
errcheck:
|
||||
check-blank: false
|
||||
exclude-functions:
|
||||
- github.com/dropalldatabases/sif/internal/logger.Write # log writes are best-effort
|
||||
revive:
|
||||
rules:
|
||||
- name: exported
|
||||
disabled: true # stuttering names (scan.ScanResult) require breaking API changes
|
||||
arguments: [checkPrivateReceivers]
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
disabled-checks:
|
||||
- commentedOutCode # too opinionated for a project with TODO comments
|
||||
- paramTypeCombine # style-only, not worth churn
|
||||
- unnamedResult # style-only
|
||||
- unnecessaryDefer # common pattern in tests
|
||||
- nestingReduce # inverting conditions in scan logic hurts readability
|
||||
gosec:
|
||||
excludes:
|
||||
- G104 # errcheck covers this
|
||||
- G107 # pentesting tool -- variable URLs are the whole point
|
||||
- G110 # nuclei template decompression, acceptable context
|
||||
- G304 # sif reads user-supplied wordlist paths -- intentional
|
||||
- G304 # sif reads user-supplied wordlist paths — intentional
|
||||
gocognit:
|
||||
min-complexity: 30
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
@@ -57,10 +47,4 @@ run:
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 50
|
||||
max-same-issues: 50
|
||||
exclude-rules:
|
||||
# test files get some slack
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
- noctx
|
||||
max-same-issues: 3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022-2025 vmfunc, xyzeva, lunchcat alumni,
|
||||
Copyright (c) 2022-2025 vmfunc (vmfunc), xyzeva, lunchcat alumni,
|
||||
and other sif contributors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -115,10 +115,6 @@ makepkg -si
|
||||
# shodan host intelligence (requires SHODAN_API_KEY env var)
|
||||
./sif -u https://example.com -shodan
|
||||
|
||||
# securitytrails domain discovery (requires SECURITYTRAILS_API_KEY env var)
|
||||
# discovers subdomains + associated domains, then scans all of them
|
||||
./sif -u https://example.com -securitytrails -headers
|
||||
|
||||
# sql recon + lfi scanning
|
||||
./sif -u https://example.com -sql -lfi
|
||||
|
||||
@@ -152,7 +148,6 @@ sif has a modular architecture. modules are defined in yaml and can be extended
|
||||
| `-whois` | whois lookups |
|
||||
| `-git` | exposed git repository detection |
|
||||
| `-shodan` | shodan lookup (requires SHODAN_API_KEY) |
|
||||
| `-securitytrails` | domain discovery + target expansion (requires SECURITYTRAILS_API_KEY) |
|
||||
| `-sql` | sql recon |
|
||||
| `-lfi` | local file inclusion |
|
||||
| `-framework` | framework detection with cve lookup |
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
# security policy
|
||||
|
||||
## reporting a vulnerability
|
||||
|
||||
if you find a security issue in sif, email celeste@linux.com directly.
|
||||
don't open a public issue.
|
||||
|
||||
expect a response within 48 hours. if it's confirmed, i'll push a fix
|
||||
and credit you in the release notes (unless you'd rather stay anonymous).
|
||||
|
||||
## scope
|
||||
|
||||
sif is a pentesting tool — "it can scan things" is not a vulnerability.
|
||||
actual bugs: command injection in user input handling, path traversal in
|
||||
template extraction, credential leaks, that kind of thing.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -5,8 +5,8 @@ go 1.24.2
|
||||
require (
|
||||
github.com/antchfx/htmlquery v1.3.5
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
|
||||
github.com/charmbracelet/log v0.4.2
|
||||
github.com/likexian/whois v1.15.7
|
||||
github.com/charmbracelet/log v0.2.4
|
||||
github.com/likexian/whois v1.15.1
|
||||
github.com/projectdiscovery/goflags v0.1.74
|
||||
github.com/projectdiscovery/nuclei/v3 v3.7.0
|
||||
github.com/projectdiscovery/utils v0.9.0
|
||||
@@ -132,7 +132,7 @@ require (
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.16.5 // indirect
|
||||
github.com/go-git/go-git/v5 v5.16.2 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.11 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
|
||||
@@ -259,8 +259,8 @@ github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V
|
||||
github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
|
||||
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
||||
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
||||
github.com/charmbracelet/log v0.2.4 h1:3pKtq5/Y5QMKtcZt7kDqD1p9w7lICzHYQACBFY4ocHA=
|
||||
github.com/charmbracelet/log v0.2.4/go.mod h1:nQGK8tvc4pS9cvVEH/pWJiZ50eUq1aoXUOjGpXvdD0k=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
@@ -390,8 +390,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
|
||||
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -699,10 +699,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/likexian/gokit v0.25.16 h1:wwBeUIN/OdoPp6t00xTnZE8Di/+s969Bl5N2Kw6bzP8=
|
||||
github.com/likexian/gokit v0.25.16/go.mod h1:Wqd4f+iifV0qxA1N3MqePJTUsmRy/lpst9/yXriDx/4=
|
||||
github.com/likexian/whois v1.15.7 h1:sajjDhi2bVD71AHJhjV7jLYxN92H4AWhTwxM8hmj7c0=
|
||||
github.com/likexian/whois v1.15.7/go.mod h1:kdPQtYb+7SQVftBEbCblDadUkycN7Mg1k1/Li/rwvmc=
|
||||
github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
|
||||
github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
|
||||
github.com/likexian/whois v1.15.1 h1:6vTMI8n9s1eJdmcO4R9h1x99aQWIZZX1CD3am68gApU=
|
||||
github.com/likexian/whois v1.15.1/go.mod h1:/nxmQ6YXvLz+qTxC/QFtEJNAt0zLuRxJrKiWpBJX8X0=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -42,7 +42,6 @@ type Settings struct {
|
||||
CloudStorage bool
|
||||
SubdomainTakeover bool
|
||||
Shodan bool
|
||||
SecurityTrails bool
|
||||
SQL bool
|
||||
LFI bool
|
||||
Framework bool
|
||||
@@ -93,7 +92,6 @@ func Parse() *Settings {
|
||||
flagSet.BoolVar(&settings.CloudStorage, "c3", false, "Enable C3 Misconfiguration Scan"),
|
||||
flagSet.BoolVar(&settings.SubdomainTakeover, "st", false, "Enable Subdomain Takeover Check"),
|
||||
flagSet.BoolVar(&settings.Shodan, "shodan", false, "Enable Shodan lookup (requires SHODAN_API_KEY env var)"),
|
||||
flagSet.BoolVar(&settings.SecurityTrails, "securitytrails", false, "Enable SecurityTrails domain discovery (requires SECURITYTRAILS_API_KEY env var)"),
|
||||
flagSet.BoolVar(&settings.SQL, "sql", false, "Enable SQL reconnaissance (admin panels, error disclosure)"),
|
||||
flagSet.BoolVar(&settings.LFI, "lfi", false, "Enable LFI (Local File Inclusion) reconnaissance"),
|
||||
flagSet.BoolVar(&settings.Framework, "framework", false, "Enable framework detection"),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -37,7 +37,7 @@ var defaultLogger = &Logger{
|
||||
// Init creates the log directory if it doesn't exist.
|
||||
func Init(dir string) error {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(dir, 0o755); err != nil {
|
||||
if err = os.Mkdir(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func (l *Logger) getWriter(path string) (*bufio.Writer, error) {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -228,9 +228,9 @@ func checkMatchers(matchers []Matcher, resp *http.Response, body string) bool {
|
||||
}
|
||||
|
||||
// Default to AND condition across matchers
|
||||
for i := range matchers {
|
||||
matched := checkMatcher(&matchers[i], resp, body)
|
||||
if matchers[i].Negative {
|
||||
for _, m := range matchers {
|
||||
matched := checkMatcher(m, resp, body)
|
||||
if m.Negative {
|
||||
matched = !matched
|
||||
}
|
||||
if !matched {
|
||||
@@ -242,7 +242,7 @@ func checkMatchers(matchers []Matcher, resp *http.Response, body string) bool {
|
||||
}
|
||||
|
||||
// checkMatcher evaluates a single matcher.
|
||||
func checkMatcher(m *Matcher, resp *http.Response, body string) bool {
|
||||
func checkMatcher(m Matcher, resp *http.Response, body string) bool {
|
||||
part := getPart(m.Part, resp, body)
|
||||
|
||||
switch m.Type {
|
||||
@@ -352,7 +352,8 @@ func runExtractors(extractors []Extractor, resp *http.Response, body string) map
|
||||
for _, e := range extractors {
|
||||
part := getPart(e.Part, resp, body)
|
||||
|
||||
if e.Type == "regex" {
|
||||
switch e.Type {
|
||||
case "regex":
|
||||
for _, pattern := range e.Regex {
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -16,7 +16,7 @@
|
||||
: SIF - Blazing-fast pentesting suite :
|
||||
: Blaze - BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
-------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -15,7 +15,6 @@ package templates
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -38,12 +37,7 @@ func Install(logger *log.Logger) error {
|
||||
|
||||
logger.Infof("nuclei-templates directory not found. Installing...")
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf(archive, ref), http.NoBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(fmt.Sprintf(archive, ref))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,7 +53,7 @@ func Install(logger *log.Logger) error {
|
||||
|
||||
for {
|
||||
header, err := data.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
if errors.Is(io.EOF, err) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
@@ -68,7 +62,7 @@ func Install(logger *log.Logger) error {
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if err := os.Mkdir(header.Name, 0o755); err != nil {
|
||||
if err := os.Mkdir(header.Name, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeReg:
|
||||
@@ -83,7 +77,7 @@ func Install(logger *log.Logger) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Rename(fmt.Sprintf("nuclei-templates-%s", ref), "nuclei-templates"); err != nil {
|
||||
if err = os.Rename(fmt.Sprintf("nuclei-templates-%s", ref), "nuclei-templates"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -138,12 +138,11 @@ func (p *Progress) render() {
|
||||
|
||||
bar := ""
|
||||
for i := 0; i < progressWidth; i++ {
|
||||
switch {
|
||||
case i < filled:
|
||||
if i < filled {
|
||||
bar += progressFilled
|
||||
case i == filled && current < total:
|
||||
} else if i == filled && current < total {
|
||||
bar += progressCurrent
|
||||
default:
|
||||
} else {
|
||||
bar += progressEmpty
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -21,5 +21,4 @@ func Register() {
|
||||
modules.Register(&FrameworksModule{})
|
||||
modules.Register(&NucleiModule{})
|
||||
modules.Register(&WhoisModule{})
|
||||
modules.Register(&SecurityTrailsModule{})
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
: :
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
*/
|
||||
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dropalldatabases/sif/internal/modules"
|
||||
"github.com/dropalldatabases/sif/internal/scan"
|
||||
)
|
||||
|
||||
type SecurityTrailsModule struct{}
|
||||
|
||||
func (m *SecurityTrailsModule) Info() modules.Info {
|
||||
return modules.Info{
|
||||
ID: "securitytrails-lookup",
|
||||
Name: "SecurityTrails Domain Discovery",
|
||||
Author: "sif",
|
||||
Severity: "info",
|
||||
Description: "Queries SecurityTrails API for subdomains and associated domains (requires SECURITYTRAILS_API_KEY)",
|
||||
Tags: []string{"recon", "osint", "dns", "subdomains"},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SecurityTrailsModule) Type() modules.ModuleType {
|
||||
return modules.TypeScript
|
||||
}
|
||||
|
||||
func (m *SecurityTrailsModule) Execute(ctx context.Context, target string, opts modules.Options) (*modules.Result, error) {
|
||||
stResult, err := scan.SecurityTrails(target, opts.Timeout, opts.LogDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &modules.Result{
|
||||
ModuleID: m.Info().ID,
|
||||
Target: target,
|
||||
Findings: []modules.Finding{},
|
||||
}
|
||||
|
||||
if stResult == nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
finding := modules.Finding{
|
||||
URL: target,
|
||||
Severity: "info",
|
||||
Evidence: fmt.Sprintf("discovered %d subdomains and %d associated domains",
|
||||
len(stResult.Subdomains), len(stResult.AssociatedDomains)),
|
||||
Extracted: map[string]string{
|
||||
"domain": stResult.Domain,
|
||||
"subdomain_count": fmt.Sprintf("%d", len(stResult.Subdomains)),
|
||||
"associated_count": fmt.Sprintf("%d", len(stResult.AssociatedDomains)),
|
||||
},
|
||||
}
|
||||
|
||||
if len(stResult.Subdomains) > 0 {
|
||||
finding.Extracted["subdomains"] = strings.Join(stResult.Subdomains, ", ")
|
||||
}
|
||||
|
||||
if len(stResult.AssociatedDomains) > 0 {
|
||||
finding.Extracted["associated_domains"] = strings.Join(stResult.AssociatedDomains, ", ")
|
||||
}
|
||||
|
||||
result.Findings = append(result.Findings, finding)
|
||||
return result, nil
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -55,7 +54,7 @@ func CloudStorage(url string, timeout time.Duration, logdir string) ([]CloudStor
|
||||
var results []CloudStorageResult
|
||||
|
||||
for _, bucket := range potentialBuckets {
|
||||
isPublic, err := checkS3Bucket(context.TODO(), bucket, client)
|
||||
isPublic, err := checkS3Bucket(bucket, client)
|
||||
if err != nil {
|
||||
cloudlog.Errorf("Error checking S3 bucket %s: %v", bucket, err)
|
||||
continue
|
||||
@@ -70,7 +69,7 @@ func CloudStorage(url string, timeout time.Duration, logdir string) ([]CloudStor
|
||||
if isPublic {
|
||||
cloudlog.Warnf("Public S3 bucket found: %s", styles.Highlight.Render(bucket))
|
||||
if logdir != "" {
|
||||
_ = logger.Write(sanitizedURL, logdir, fmt.Sprintf("Public S3 bucket found: %s\n", bucket))
|
||||
logger.Write(sanitizedURL, logdir, fmt.Sprintf("Public S3 bucket found: %s\n", bucket))
|
||||
}
|
||||
} else {
|
||||
cloudlog.Infof("S3 bucket is not public/found: %s", bucket)
|
||||
@@ -86,23 +85,22 @@ func extractPotentialBuckets(url string) []string {
|
||||
parts := strings.Split(url, ".")
|
||||
var buckets []string
|
||||
for i, part := range parts {
|
||||
buckets = append(buckets, part, part+"-s3", "s3-"+part)
|
||||
buckets = append(buckets, part)
|
||||
buckets = append(buckets, part+"-s3")
|
||||
buckets = append(buckets, "s3-"+part)
|
||||
|
||||
if i < len(parts)-1 {
|
||||
domainExtension := part + "-" + parts[i+1]
|
||||
buckets = append(buckets, domainExtension, parts[i+1]+"-"+part)
|
||||
buckets = append(buckets, domainExtension)
|
||||
buckets = append(buckets, parts[i+1]+"-"+part)
|
||||
}
|
||||
}
|
||||
return buckets
|
||||
}
|
||||
|
||||
func checkS3Bucket(ctx context.Context, bucket string, client *http.Client) (bool, error) {
|
||||
func checkS3Bucket(bucket string, client *http.Client) (bool, error) {
|
||||
url := fmt.Sprintf("https://%s.s3.amazonaws.com", bucket)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
+4
-15
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -49,13 +48,7 @@ func CMS(url string, timeout time.Duration, logdir string) (*CMSResult, error) {
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
@@ -99,7 +92,7 @@ func CMS(url string, timeout time.Duration, logdir string) (*CMSResult, error) {
|
||||
spin.Stop()
|
||||
log.Info("No CMS detected")
|
||||
log.Complete(0, "detected")
|
||||
return nil, nil //nolint:nilnil // no CMS found is not an error
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func detectWordPress(url string, client *http.Client, bodyString string) bool {
|
||||
@@ -125,11 +118,7 @@ func detectWordPress(url string, client *http.Client, bodyString string) bool {
|
||||
}
|
||||
|
||||
for _, file := range wpFiles {
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url+file, http.NoBody)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(url + file)
|
||||
if err == nil {
|
||||
found := resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusFound
|
||||
resp.Body.Close()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -14,7 +14,6 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -63,12 +62,7 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
list = directoryURL + bigFile
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, list, http.NoBody)
|
||||
if err != nil {
|
||||
log.Error("Error creating directory list request: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(list)
|
||||
if err != nil {
|
||||
log.Error("Error downloading directory list: %s", err)
|
||||
return nil, err
|
||||
@@ -105,12 +99,7 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
progress.Increment(directory)
|
||||
|
||||
charmlog.Debugf("%s", directory)
|
||||
dirReq, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url+"/"+directory, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error creating request for %s: %s", directory, err)
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(dirReq)
|
||||
resp, err := client.Get(url + "/" + directory)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error %s: %s", directory, err)
|
||||
continue
|
||||
@@ -122,7 +111,7 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
progress.Resume()
|
||||
|
||||
if logdir != "" {
|
||||
_ = logger.Write(sanitizedURL, logdir, fmt.Sprintf("%s [%s]\n", strconv.Itoa(resp.StatusCode), directory))
|
||||
logger.Write(sanitizedURL, logdir, fmt.Sprintf("%s [%s]\n", strconv.Itoa(resp.StatusCode), directory))
|
||||
}
|
||||
|
||||
result := DirectoryResult{
|
||||
@@ -133,7 +122,6 @@ func Dirlist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
results = append(results, result)
|
||||
mu.Unlock()
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
}(thread)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -14,7 +14,6 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -48,12 +47,7 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
list = dnsURL + dnsBigFile
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, list, http.NoBody)
|
||||
if err != nil {
|
||||
log.Error("Error creating request: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(list)
|
||||
if err != nil {
|
||||
log.Error("Error downloading DNS list: %s", err)
|
||||
return nil, err
|
||||
@@ -101,19 +95,13 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
charmlog.Debugf("Looking up: %s", domain)
|
||||
|
||||
// Check HTTP
|
||||
httpReq, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, "http://"+domain+"."+sanitizedURL, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error %s: %s", domain, err)
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(httpReq)
|
||||
resp, err := client.Get("http://" + domain + "." + sanitizedURL)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error %s: %s", domain, err)
|
||||
} else {
|
||||
mu.Lock()
|
||||
urls = append(urls, resp.Request.URL.String())
|
||||
mu.Unlock()
|
||||
resp.Body.Close()
|
||||
|
||||
progress.Pause()
|
||||
log.Success("found: %s.%s [http]", output.Highlight.Render(domain), sanitizedURL)
|
||||
@@ -125,26 +113,20 @@ func Dnslist(size string, url string, timeout time.Duration, threads int, logdir
|
||||
}
|
||||
|
||||
// Check HTTPS
|
||||
httpsReq, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, "https://"+domain+"."+sanitizedURL, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error %s: %s", domain, err)
|
||||
continue
|
||||
}
|
||||
resp, err = client.Do(httpsReq)
|
||||
resp, err = client.Get("https://" + domain + "." + sanitizedURL)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error %s: %s", domain, err)
|
||||
} else {
|
||||
mu.Lock()
|
||||
urls = append(urls, resp.Request.URL.String())
|
||||
mu.Unlock()
|
||||
resp.Body.Close()
|
||||
|
||||
progress.Pause()
|
||||
log.Success("found: %s.%s [https]", output.Highlight.Render(domain), sanitizedURL)
|
||||
progress.Resume()
|
||||
|
||||
if logdir != "" {
|
||||
_ = logger.Write(sanitizedURL, logdir, fmt.Sprintf("[https] %s.%s\n", domain, sanitizedURL))
|
||||
logger.Write(sanitizedURL, logdir, fmt.Sprintf("[https] %s.%s\n", domain, sanitizedURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-12
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -17,7 +17,6 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -70,14 +69,7 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) ([]Dork
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, dorkURL+dorkFile, http.NoBody)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
output.Error("Error creating dork list request: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(dorkURL + dorkFile)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
output.Error("Error downloading dork list: %s", err)
|
||||
@@ -106,7 +98,7 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) ([]Dork
|
||||
continue
|
||||
}
|
||||
|
||||
results, err := googlesearch.Search(context.TODO(), fmt.Sprintf("%s %s", dork, sanitizedURL))
|
||||
results, err := googlesearch.Search(nil, fmt.Sprintf("%s %s", dork, sanitizedURL))
|
||||
if err != nil {
|
||||
log.Debugf("error searching for dork %s: %v", dork, err)
|
||||
continue
|
||||
@@ -116,7 +108,7 @@ func Dork(url string, timeout time.Duration, threads int, logdir string) ([]Dork
|
||||
output.Success("%s dork results found for dork %s", output.Status.Render(strconv.Itoa(len(results))), output.Highlight.Render(dork))
|
||||
spin.Start()
|
||||
if logdir != "" {
|
||||
_ = logger.Write(sanitizedURL, logdir, strconv.Itoa(len(results))+" dork results found for dork ["+dork+"]\n")
|
||||
logger.Write(sanitizedURL, logdir, strconv.Itoa(len(results))+" dork results found for dork ["+dork+"]\n")
|
||||
}
|
||||
|
||||
result := DorkResult{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package frameworks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -48,12 +47,7 @@ func DetectFramework(url string, timeout time.Duration, logdir string) (*Framewo
|
||||
|
||||
client := &http.Client{Timeout: timeout}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
}
|
||||
resp, err := client.Do(req) //nolint:bodyclose // closed via defer below
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
@@ -72,7 +66,7 @@ func DetectFramework(url string, timeout time.Duration, logdir string) (*Framewo
|
||||
if len(detectors) == 0 {
|
||||
spin.Stop()
|
||||
log.Warn("No framework detectors registered")
|
||||
return nil, nil //nolint:nilnil // no detectors registered is not an error
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Run all detectors concurrently
|
||||
@@ -111,7 +105,7 @@ func DetectFramework(url string, timeout time.Duration, logdir string) (*Framewo
|
||||
if best.confidence <= detectionThreshold {
|
||||
log.Info("No framework detected with sufficient confidence")
|
||||
log.Complete(0, "detected")
|
||||
return nil, nil //nolint:nilnil // no framework detected is not an error
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get version match details
|
||||
@@ -130,7 +124,7 @@ func DetectFramework(url string, timeout time.Duration, logdir string) (*Framewo
|
||||
logEntry += fmt.Sprintf(" CVEs: %v\n", cves)
|
||||
logEntry += fmt.Sprintf(" Recommendations: %v\n", suggestions)
|
||||
}
|
||||
_ = logger.Write(url, logdir, logEntry)
|
||||
logger.Write(url, logdir, logEntry)
|
||||
}
|
||||
|
||||
log.Success("Detected %s framework (version: %s, confidence: %.2f)",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
: :
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
*/
|
||||
|
||||
package frameworks
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzIsValidVersionString(f *testing.F) {
|
||||
f.Add("1.0")
|
||||
f.Add("1.0.0")
|
||||
f.Add("10.2.3.4")
|
||||
f.Add("999.999.999")
|
||||
f.Add("")
|
||||
f.Add("abc")
|
||||
f.Add("1.")
|
||||
f.Add(".1")
|
||||
f.Add("1.2.3.4.5")
|
||||
f.Add("aaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
|
||||
f.Fuzz(func(t *testing.T, v string) {
|
||||
// should never panic
|
||||
isValidVersionString(v)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzExtractVersionOptimized(f *testing.F) {
|
||||
f.Add("<meta name=\"generator\" content=\"WordPress 6.4.2\">", "WordPress")
|
||||
f.Add("Laravel v10.0.1", "Laravel")
|
||||
f.Add("<html>nothing</html>", "Django")
|
||||
f.Add("", "unknown")
|
||||
f.Add("X-Powered-By: Express/4.18.2", "Express")
|
||||
|
||||
f.Fuzz(func(t *testing.T, body string, framework string) {
|
||||
// should never panic
|
||||
ExtractVersionOptimized(body, framework)
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -202,7 +202,7 @@ func ExtractVersionOptimized(body string, framework string) VersionMatch {
|
||||
|
||||
// isValidVersionString checks if a version string looks like a valid semver
|
||||
func isValidVersionString(v string) bool {
|
||||
if v == "" || len(v) > 20 {
|
||||
if len(v) == 0 || len(v) > 20 {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
: :
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzDetectLFIFromResponse(f *testing.F) {
|
||||
f.Add("root:x:0:0:root:/root:/bin/bash")
|
||||
f.Add("<html><body>Hello World</body></html>")
|
||||
f.Add("[boot loader]\ntimeout=30")
|
||||
f.Add("DOCUMENT_ROOT=/var/www/html")
|
||||
f.Add("<?php echo 'hello'; ?>")
|
||||
f.Add("127.0.0.1 localhost")
|
||||
f.Add("")
|
||||
f.Add("PD9waHAgZWNobyAnaGVsbG8nOyA/Pg==")
|
||||
|
||||
f.Fuzz(func(t *testing.T, body string) {
|
||||
// should never panic
|
||||
DetectLFIFromResponse(body)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzIsAdminPanel(f *testing.F) {
|
||||
f.Add("<html>phpMyAdmin</html>", "phpMyAdmin")
|
||||
f.Add("<html>adminer</html>", "Adminer")
|
||||
f.Add("<html>pgadmin</html>", "pgAdmin")
|
||||
f.Add("<html>nothing here</html>", "phpMyAdmin")
|
||||
f.Add("", "unknown")
|
||||
f.Add("<html>database query mysql</html>", "generic")
|
||||
|
||||
f.Fuzz(func(t *testing.T, body string, panelType string) {
|
||||
// should never panic
|
||||
isAdminPanel(body, panelType)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzDatabaseErrorPatterns(f *testing.F) {
|
||||
f.Add("you have an error in your sql syntax")
|
||||
f.Add("Warning: mysql_fetch_array()")
|
||||
f.Add("postgresql error at character 42")
|
||||
f.Add("ORA-12345: some oracle error")
|
||||
f.Add("sqlite3_prepare_v2 failed")
|
||||
f.Add("document bson error in mongodb")
|
||||
f.Add("<html>normal page</html>")
|
||||
f.Add("")
|
||||
|
||||
f.Fuzz(func(t *testing.T, body string) {
|
||||
// should never panic on any input
|
||||
for _, pattern := range databaseErrorPatterns {
|
||||
pattern.pattern.MatchString(body)
|
||||
}
|
||||
})
|
||||
}
|
||||
+3
-17
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -14,7 +14,6 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -48,13 +47,7 @@ func Git(url string, timeout time.Duration, threads int, logdir string) ([]strin
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, gitURL+gitFile, http.NoBody)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
log.Error("Error creating git list request: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(gitURL + gitFile)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
log.Error("Error downloading git list: %s", err)
|
||||
@@ -87,15 +80,9 @@ func Git(url string, timeout time.Duration, threads int, logdir string) ([]strin
|
||||
}
|
||||
|
||||
charmlog.Debugf("%s", repourl)
|
||||
gitReq, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url+"/"+repourl, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error creating request for %s: %s", repourl, err)
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(gitReq)
|
||||
resp, err := client.Get(url + "/" + repourl)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error %s: %s", repourl, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode == 200 && !strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") {
|
||||
@@ -108,7 +95,6 @@ func Git(url string, timeout time.Duration, threads int, logdir string) ([]strin
|
||||
|
||||
foundUrls = append(foundUrls, resp.Request.URL.String())
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
}(thread)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -44,11 +43,7 @@ func Headers(url string, timeout time.Duration, logdir string) ([]HeaderResult,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -24,7 +24,6 @@ package frameworks
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@@ -42,13 +41,7 @@ func GetPagesRouterScripts(scriptUrl string) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, scriptUrl, http.NoBody)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(scriptUrl)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -14,7 +14,6 @@ package js
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
@@ -48,12 +47,7 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, err
|
||||
@@ -115,12 +109,7 @@ func JavascriptScan(url string, timeout time.Duration, threads int, logdir strin
|
||||
supabaseResults := make([]supabaseScanResult, 0, len(scripts))
|
||||
for _, script := range scripts {
|
||||
charmlog.Debugf("Scanning %s", script)
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, script, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Warnf("Failed to create request: %s", err)
|
||||
continue
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(script)
|
||||
if err != nil {
|
||||
charmlog.Warnf("Failed to fetch script: %s", err)
|
||||
continue
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -16,7 +16,6 @@ package js
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -71,7 +70,7 @@ type supabaseOpenAPIResponse struct {
|
||||
|
||||
// getSupabaseArrayResponse fetches a Supabase endpoint that returns an array.
|
||||
func getSupabaseArrayResponse(projectId, path, apikey string, auth *string) (*supabaseArrayResponse, error) {
|
||||
body, resp, err := doSupabaseRequest(projectId, path, apikey, auth) //nolint:bodyclose // closed in doSupabaseRequest
|
||||
body, resp, err := doSupabaseRequest(projectId, path, apikey, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -96,7 +95,7 @@ func getSupabaseArrayResponse(projectId, path, apikey string, auth *string) (*su
|
||||
|
||||
// getSupabaseOpenAPI fetches the OpenAPI spec from Supabase.
|
||||
func getSupabaseOpenAPI(projectId, apikey string, auth *string) (*supabaseOpenAPIResponse, error) {
|
||||
body, _, err := doSupabaseRequest(projectId, "/rest/v1/", apikey, auth) //nolint:bodyclose // closed in doSupabaseRequest
|
||||
body, _, err := doSupabaseRequest(projectId, "/rest/v1/", apikey, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -112,7 +111,7 @@ func getSupabaseOpenAPI(projectId, apikey string, auth *string) (*supabaseOpenAP
|
||||
func doSupabaseRequest(projectId, path, apikey string, auth *string) ([]byte, *http.Response, error) {
|
||||
client := http.Client{}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, "https://"+projectId+".supabase.co"+path, http.NoBody)
|
||||
req, err := http.NewRequest("GET", "https://"+projectId+".supabase.co"+path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -171,7 +170,7 @@ func ScanSupabase(jsContent string, jsUrl string) ([]supabaseScanResult, error)
|
||||
|
||||
supabaselog.Debugf("JWT body: %s", decoded)
|
||||
var supabaseJwt *supabaseJwtBody
|
||||
err = json.Unmarshal(decoded, &supabaseJwt)
|
||||
err = json.Unmarshal([]byte(decoded), &supabaseJwt)
|
||||
if err != nil {
|
||||
supabaselog.Debugf("Failed to json parse JWT %s: %s", jwt, err)
|
||||
continue
|
||||
@@ -184,7 +183,7 @@ func ScanSupabase(jsContent string, jsUrl string) ([]supabaseScanResult, error)
|
||||
supabaselog.Infof("Found valid supabase project %s with role %s", *supabaseJwt.ProjectId, *supabaseJwt.Role)
|
||||
client := http.Client{}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodPost, "https://"+*supabaseJwt.ProjectId+".supabase.co/auth/v1/signup", bytes.NewBufferString(`{"email":"automated`+strconv.Itoa(int(time.Now().Unix()))+`@sif.sh","password":"automatedacct"}`))
|
||||
req, err := http.NewRequest("POST", "https://"+*supabaseJwt.ProjectId+".supabase.co/auth/v1/signup", bytes.NewBufferString(`{"email":"automated`+strconv.Itoa(int(time.Now().Unix()))+`@sif.sh","password":"automatedacct"}`))
|
||||
if err != nil {
|
||||
supabaselog.Errorf("Error while creating HTTP req for creating user: %s", err)
|
||||
continue
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -215,12 +214,7 @@ func LFI(targetURL string, timeout time.Duration, threads int, logdir string) (*
|
||||
parsedURL.Path,
|
||||
testParams.Encode())
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, testURL, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error creating request for %s: %v", testURL, err)
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(testURL)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error testing %s: %v", testURL, err)
|
||||
continue
|
||||
@@ -300,7 +294,7 @@ func LFI(targetURL string, timeout time.Duration, threads int, logdir string) (*
|
||||
} else {
|
||||
log.Info("No LFI vulnerabilities detected")
|
||||
log.Complete(0, "found")
|
||||
return nil, nil //nolint:nilnil // no LFI found is not an error
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -37,7 +37,7 @@ func Nuclei(url string, timeout time.Duration, threads int, logdir string) ([]ou
|
||||
Prefix: "nuclei",
|
||||
}).With("url", url)
|
||||
|
||||
_ = templates.Install(nucleilog)
|
||||
templates.Install(nucleilog)
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -14,7 +14,6 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -45,12 +44,7 @@ func Ports(scope string, url string, timeout time.Duration, threads int, logdir
|
||||
var ports []int
|
||||
switch scope {
|
||||
case "common":
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, commonPorts, http.NoBody)
|
||||
if err != nil {
|
||||
log.Error("Error creating request: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := http.Get(commonPorts)
|
||||
if err != nil {
|
||||
log.Error("Error downloading ports list: %s", err)
|
||||
return nil, err
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -31,11 +31,10 @@ type ScanResult interface {
|
||||
|
||||
// ResultType implementations for pointer result types.
|
||||
|
||||
func (r *ShodanResult) ResultType() string { return "shodan" }
|
||||
func (r *SQLResult) ResultType() string { return "sql" }
|
||||
func (r *LFIResult) ResultType() string { return "lfi" }
|
||||
func (r *CMSResult) ResultType() string { return "cms" }
|
||||
func (r *SecurityTrailsResult) ResultType() string { return "securitytrails" }
|
||||
func (r *ShodanResult) ResultType() string { return "shodan" }
|
||||
func (r *SQLResult) ResultType() string { return "sql" }
|
||||
func (r *LFIResult) ResultType() string { return "lfi" }
|
||||
func (r *CMSResult) ResultType() string { return "cms" }
|
||||
|
||||
// ResultType implementations for slice result types.
|
||||
|
||||
@@ -51,7 +50,6 @@ var (
|
||||
_ ScanResult = (*SQLResult)(nil)
|
||||
_ ScanResult = (*LFIResult)(nil)
|
||||
_ ScanResult = (*CMSResult)(nil)
|
||||
_ ScanResult = (*SecurityTrailsResult)(nil)
|
||||
_ ScanResult = HeaderResults(nil)
|
||||
_ ScanResult = DirectoryResults(nil)
|
||||
_ ScanResult = CloudStorageResults(nil)
|
||||
|
||||
+3
-14
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -19,7 +19,6 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,12 +31,7 @@ import (
|
||||
)
|
||||
|
||||
func fetchRobotsTXT(url string, client *http.Client) *http.Response {
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
log.Debugf("Error creating request for robots.txt: %s", err)
|
||||
return nil
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
log.Debugf("Error fetching robots.txt: %s", err)
|
||||
return nil
|
||||
@@ -116,12 +110,7 @@ func Scan(url string, timeout time.Duration, threads int, logdir string) {
|
||||
|
||||
_, sanitizedRobot, _ := strings.Cut(robot, ": ")
|
||||
log.Debugf("%s", robot)
|
||||
robotReq, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url+"/"+sanitizedRobot, http.NoBody)
|
||||
if err != nil {
|
||||
log.Debugf("Error creating request for %s: %s", sanitizedRobot, err)
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(robotReq)
|
||||
resp, err := client.Get(url + "/" + sanitizedRobot)
|
||||
if err != nil {
|
||||
log.Debugf("Error %s: %s", sanitizedRobot, err)
|
||||
continue
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
: :
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dropalldatabases/sif/internal/logger"
|
||||
"github.com/dropalldatabases/sif/internal/output"
|
||||
)
|
||||
|
||||
const securityTrailsBaseURL = "https://api.securitytrails.com/v1"
|
||||
|
||||
// SecurityTrailsResult holds discovered domains from SecurityTrails API
|
||||
type SecurityTrailsResult struct {
|
||||
Domain string `json:"domain"`
|
||||
Subdomains []string `json:"subdomains,omitempty"`
|
||||
AssociatedDomains []string `json:"associated_domains,omitempty"`
|
||||
}
|
||||
|
||||
// stSubdomainsResponse is the raw response from the subdomains endpoint -
|
||||
// returns prefix labels, not FQDNs
|
||||
type stSubdomainsResponse struct {
|
||||
Subdomains []string `json:"subdomains"`
|
||||
}
|
||||
|
||||
type stAssociatedResponse struct {
|
||||
Records []stAssociatedRecord `json:"records"`
|
||||
}
|
||||
|
||||
type stAssociatedRecord struct {
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
|
||||
// SecurityTrails queries the SecurityTrails API for subdomains and associated domains.
|
||||
// API key should be provided via the SECURITYTRAILS_API_KEY environment variable.
|
||||
func SecurityTrails(targetURL string, timeout time.Duration, logdir string) (*SecurityTrailsResult, error) {
|
||||
output.ScanStart("SecurityTrails lookup")
|
||||
|
||||
spin := output.NewSpinner("querying SecurityTrails API")
|
||||
spin.Start()
|
||||
|
||||
apiKey := os.Getenv("SECURITYTRAILS_API_KEY")
|
||||
if apiKey == "" {
|
||||
spin.Stop()
|
||||
output.Warn("SECURITYTRAILS_API_KEY environment variable not set, skipping SecurityTrails lookup")
|
||||
return nil, fmt.Errorf("SECURITYTRAILS_API_KEY environment variable not set")
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(targetURL)
|
||||
if err != nil {
|
||||
spin.Stop()
|
||||
return nil, fmt.Errorf("failed to parse URL: %w", err)
|
||||
}
|
||||
hostname := parsedURL.Hostname()
|
||||
|
||||
client := &http.Client{Timeout: timeout}
|
||||
|
||||
result := &SecurityTrailsResult{
|
||||
Domain: hostname,
|
||||
}
|
||||
|
||||
// fetch subdomains
|
||||
spin.Update("fetching subdomains for " + hostname)
|
||||
subs, err := querySTSubdomains(client, hostname, apiKey)
|
||||
if err != nil {
|
||||
// non-fatal - still try associated domains
|
||||
output.Warn("SecurityTrails subdomains failed: %v", err)
|
||||
} else {
|
||||
result.Subdomains = subs
|
||||
}
|
||||
|
||||
// fetch associated domains
|
||||
spin.Update("fetching associated domains for " + hostname)
|
||||
assoc, err := querySTAssociated(client, hostname, apiKey)
|
||||
if err != nil {
|
||||
output.Warn("SecurityTrails associated domains failed: %v", err)
|
||||
} else {
|
||||
result.AssociatedDomains = assoc
|
||||
}
|
||||
|
||||
spin.Stop()
|
||||
|
||||
if logdir != "" {
|
||||
sanitizedURL := strings.Split(targetURL, "://")[1]
|
||||
if err := logger.WriteHeader(sanitizedURL, logdir, "SecurityTrails lookup"); err != nil {
|
||||
output.Error("error writing log header: %v", err)
|
||||
}
|
||||
logSecurityTrailsResults(sanitizedURL, logdir, result)
|
||||
}
|
||||
|
||||
printSecurityTrailsResults(result)
|
||||
|
||||
total := len(result.Subdomains) + len(result.AssociatedDomains)
|
||||
output.ScanComplete("SecurityTrails lookup", total, "domains discovered")
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// DiscoveredURLs returns all discovered domains as https:// URLs.
|
||||
// used by the orchestration layer for target expansion.
|
||||
func (r *SecurityTrailsResult) DiscoveredURLs() []string {
|
||||
seen := make(map[string]struct{})
|
||||
var urls []string
|
||||
|
||||
for _, sub := range r.Subdomains {
|
||||
fqdn := sub + "." + r.Domain
|
||||
if _, ok := seen[fqdn]; !ok {
|
||||
seen[fqdn] = struct{}{}
|
||||
urls = append(urls, "https://"+fqdn)
|
||||
}
|
||||
}
|
||||
|
||||
for _, assoc := range r.AssociatedDomains {
|
||||
if _, ok := seen[assoc]; !ok {
|
||||
seen[assoc] = struct{}{}
|
||||
urls = append(urls, "https://"+assoc)
|
||||
}
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
func querySTSubdomains(client *http.Client, hostname, apiKey string) ([]string, error) {
|
||||
reqURL := fmt.Sprintf("%s/domain/%s/subdomains", securityTrailsBaseURL, hostname)
|
||||
body, err := doSTRequest(client, reqURL, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp stSubdomainsResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
return nil, fmt.Errorf("parse subdomains response: %w", err)
|
||||
}
|
||||
|
||||
return resp.Subdomains, nil
|
||||
}
|
||||
|
||||
func querySTAssociated(client *http.Client, hostname, apiKey string) ([]string, error) {
|
||||
reqURL := fmt.Sprintf("%s/domain/%s/associated", securityTrailsBaseURL, hostname)
|
||||
body, err := doSTRequest(client, reqURL, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp stAssociatedResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
return nil, fmt.Errorf("parse associated response: %w", err)
|
||||
}
|
||||
|
||||
domains := make([]string, 0, len(resp.Records))
|
||||
for _, rec := range resp.Records {
|
||||
if rec.Hostname != "" {
|
||||
domains = append(domains, rec.Hostname)
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
// doSTRequest makes an authenticated GET to the SecurityTrails API
|
||||
func doSTRequest(client *http.Client, reqURL, apiKey string) ([]byte, error) {
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, reqURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
req.Header.Set("APIKEY", apiKey)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SecurityTrails request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized {
|
||||
return nil, fmt.Errorf("invalid SecurityTrails API key (status %d)", resp.StatusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
return nil, fmt.Errorf("SecurityTrails rate limit exceeded")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
|
||||
return nil, fmt.Errorf("SecurityTrails API error (status %d): %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 5*1024*1024))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func printSecurityTrailsResults(result *SecurityTrailsResult) {
|
||||
output.Info("Domain: %s", output.Highlight.Render(result.Domain))
|
||||
|
||||
if len(result.Subdomains) > 0 {
|
||||
output.Info("Subdomains found: %d", len(result.Subdomains))
|
||||
for _, sub := range result.Subdomains {
|
||||
output.Success(" %s.%s", sub, result.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.AssociatedDomains) > 0 {
|
||||
output.Info("Associated domains found: %d", len(result.AssociatedDomains))
|
||||
for _, assoc := range result.AssociatedDomains {
|
||||
output.Success(" %s", assoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logSecurityTrailsResults(sanitizedURL, logdir string, result *SecurityTrailsResult) {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(fmt.Sprintf("Domain: %s\n", result.Domain))
|
||||
|
||||
if len(result.Subdomains) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("\nSubdomains (%d):\n", len(result.Subdomains)))
|
||||
for _, sub := range result.Subdomains {
|
||||
sb.WriteString(fmt.Sprintf(" %s.%s\n", sub, result.Domain))
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.AssociatedDomains) > 0 {
|
||||
sb.WriteString(fmt.Sprintf("\nAssociated Domains (%d):\n", len(result.AssociatedDomains)))
|
||||
for _, assoc := range result.AssociatedDomains {
|
||||
sb.WriteString(fmt.Sprintf(" %s\n", assoc))
|
||||
}
|
||||
}
|
||||
|
||||
logger.Write(sanitizedURL, logdir, sb.String())
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSecurityTrailsResult_DiscoveredURLs(t *testing.T) {
|
||||
result := &SecurityTrailsResult{
|
||||
Domain: "example.com",
|
||||
Subdomains: []string{"www", "api", "mail"},
|
||||
AssociatedDomains: []string{"example.org", "example.net"},
|
||||
}
|
||||
|
||||
urls := result.DiscoveredURLs()
|
||||
|
||||
if len(urls) != 5 {
|
||||
t.Errorf("expected 5 URLs, got %d: %v", len(urls), urls)
|
||||
}
|
||||
|
||||
expected := map[string]bool{
|
||||
"https://www.example.com": false,
|
||||
"https://api.example.com": false,
|
||||
"https://mail.example.com": false,
|
||||
"https://example.org": false,
|
||||
"https://example.net": false,
|
||||
}
|
||||
|
||||
for _, u := range urls {
|
||||
if _, ok := expected[u]; !ok {
|
||||
t.Errorf("unexpected URL: %s", u)
|
||||
}
|
||||
expected[u] = true
|
||||
}
|
||||
|
||||
for u, seen := range expected {
|
||||
if !seen {
|
||||
t.Errorf("missing expected URL: %s", u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityTrailsResult_DiscoveredURLs_Dedup(t *testing.T) {
|
||||
result := &SecurityTrailsResult{
|
||||
Domain: "example.com",
|
||||
Subdomains: []string{"www"},
|
||||
AssociatedDomains: []string{"www.example.com"},
|
||||
}
|
||||
|
||||
urls := result.DiscoveredURLs()
|
||||
if len(urls) != 1 {
|
||||
t.Errorf("expected 1 URL (deduped), got %d: %v", len(urls), urls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityTrailsResult_DiscoveredURLs_Empty(t *testing.T) {
|
||||
result := &SecurityTrailsResult{
|
||||
Domain: "example.com",
|
||||
}
|
||||
|
||||
urls := result.DiscoveredURLs()
|
||||
if len(urls) != 0 {
|
||||
t.Errorf("expected 0 URLs, got %d: %v", len(urls), urls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoSTRequest_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("APIKEY") != "test-key" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"test": true}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
body, err := doSTRequest(client, server.URL, "test-key")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(body) == 0 {
|
||||
t.Error("expected non-empty body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoSTRequest_Unauthorized(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
_, err := doSTRequest(client, server.URL, "bad-key")
|
||||
if err == nil {
|
||||
t.Error("expected error for forbidden response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoSTRequest_RateLimit(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
_, err := doSTRequest(client, server.URL, "test-key")
|
||||
if err == nil {
|
||||
t.Error("expected error for rate limit response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuerySTSubdomains(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("APIKEY") != "test-key" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
resp := stSubdomainsResponse{
|
||||
Subdomains: []string{"www", "api", "mail", "dev"},
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
|
||||
// query the mock server directly via doSTRequest + unmarshal
|
||||
body, err := doSTRequest(client, server.URL, "test-key")
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
|
||||
var resp stSubdomainsResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
|
||||
if len(resp.Subdomains) != 4 {
|
||||
t.Errorf("expected 4 subdomains, got %d", len(resp.Subdomains))
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuerySTAssociated(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("APIKEY") != "test-key" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
resp := stAssociatedResponse{
|
||||
Records: []stAssociatedRecord{
|
||||
{Hostname: "related.com"},
|
||||
{Hostname: "sibling.net"},
|
||||
{Hostname: ""},
|
||||
},
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
|
||||
body, err := doSTRequest(client, server.URL, "test-key")
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
|
||||
var resp stAssociatedResponse
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
|
||||
// should have 3 records total (including empty one)
|
||||
if len(resp.Records) != 3 {
|
||||
t.Errorf("expected 3 records, got %d", len(resp.Records))
|
||||
}
|
||||
|
||||
// filter empty hostnames like the real code does
|
||||
var domains []string
|
||||
for _, rec := range resp.Records {
|
||||
if rec.Hostname != "" {
|
||||
domains = append(domains, rec.Hostname)
|
||||
}
|
||||
}
|
||||
if len(domains) != 2 {
|
||||
t.Errorf("expected 2 non-empty domains, got %d", len(domains))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityTrailsResult_ResultType(t *testing.T) {
|
||||
result := &SecurityTrailsResult{}
|
||||
if result.ResultType() != "securitytrails" {
|
||||
t.Errorf("expected ResultType 'securitytrails', got '%s'", result.ResultType())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityTrailsIntegration(t *testing.T) {
|
||||
t.Skip("integration test - requires valid SECURITYTRAILS_API_KEY")
|
||||
|
||||
_, err := SecurityTrails("https://example.com", 10*time.Second, "")
|
||||
if err != nil {
|
||||
t.Logf("SecurityTrails lookup failed: %v", err)
|
||||
}
|
||||
}
|
||||
+9
-14
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -183,11 +182,7 @@ func queryShodanHost(ip string, apiKey string, timeout time.Duration) (*ShodanRe
|
||||
client := &http.Client{Timeout: timeout}
|
||||
|
||||
reqURL := fmt.Sprintf("%s/shodan/host/%s?key=%s", shodanBaseURL, ip, apiKey)
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, reqURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Shodan request: %w", err)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(reqURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query Shodan: %w", err)
|
||||
}
|
||||
@@ -237,14 +232,14 @@ func queryShodanHost(ip string, apiKey string, timeout time.Duration) (*ShodanRe
|
||||
Services: make([]ShodanService, 0, len(shodanResp.Data)),
|
||||
}
|
||||
|
||||
for i := range shodanResp.Data {
|
||||
for _, data := range shodanResp.Data {
|
||||
service := ShodanService{
|
||||
Port: shodanResp.Data[i].Port,
|
||||
Protocol: shodanResp.Data[i].Transport,
|
||||
Product: shodanResp.Data[i].Product,
|
||||
Version: shodanResp.Data[i].Version,
|
||||
Banner: truncateBanner(shodanResp.Data[i].Data, 200),
|
||||
Module: shodanResp.Data[i].Shodan.Module,
|
||||
Port: data.Port,
|
||||
Protocol: data.Transport,
|
||||
Product: data.Product,
|
||||
Version: data.Version,
|
||||
Banner: truncateBanner(data.Data, 200),
|
||||
Module: data.Shodan.Module,
|
||||
}
|
||||
result.Services = append(result.Services, service)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
+4
-14
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,7 +13,6 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@@ -165,12 +164,7 @@ func SQL(targetURL string, timeout time.Duration, threads int, logdir string) (*
|
||||
adminPath := sqlAdminPaths[idx]
|
||||
checkURL := strings.TrimSuffix(targetURL, "/") + adminPath.path
|
||||
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, checkURL, http.NoBody)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error creating request for %s: %v", checkURL, err)
|
||||
continue
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(checkURL)
|
||||
if err != nil {
|
||||
charmlog.Debugf("Error checking %s: %v", checkURL, err)
|
||||
continue
|
||||
@@ -249,7 +243,7 @@ func SQL(targetURL string, timeout time.Duration, threads int, logdir string) (*
|
||||
if totalFindings == 0 {
|
||||
log.Info("No SQL exposures found")
|
||||
log.Complete(0, "found")
|
||||
return nil, nil //nolint:nilnil // no SQLi found is not an error
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Complete(totalFindings, "found")
|
||||
@@ -291,11 +285,7 @@ func isAdminPanel(body string, panelType string) bool {
|
||||
}
|
||||
|
||||
func checkDatabaseErrors(client *http.Client, checkURL, sanitizedURL string, result *SQLResult, logdir string, mu *sync.Mutex, seen map[string]bool) {
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, checkURL, http.NoBody)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get(checkURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -13,8 +13,10 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/dropalldatabases/sif/internal/logger"
|
||||
"github.com/dropalldatabases/sif/internal/styles"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -22,10 +24,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/dropalldatabases/sif/internal/logger"
|
||||
"github.com/dropalldatabases/sif/internal/styles"
|
||||
)
|
||||
|
||||
// SubdomainTakeoverResult represents the outcome of a subdomain takeover vulnerability check.
|
||||
@@ -116,11 +114,7 @@ func SubdomainTakeover(url string, dnsResults []string, timeout time.Duration, t
|
||||
}
|
||||
|
||||
func checkSubdomainTakeover(subdomain string, client *http.Client) (bool, string) {
|
||||
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, "http://"+subdomain, http.NoBody)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Get("http://" + subdomain)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no such host") {
|
||||
// Check if CNAME exists
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
@@ -86,10 +86,9 @@ func New(settings *config.Settings) (*App, error) {
|
||||
return app, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(settings.URLs) > 0:
|
||||
if len(settings.URLs) > 0 {
|
||||
app.targets = settings.URLs
|
||||
case settings.File != "":
|
||||
} else if settings.File != "" {
|
||||
if _, err := os.Stat(settings.File); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -105,7 +104,7 @@ func New(settings *config.Settings) (*App, error) {
|
||||
for scanner.Scan() {
|
||||
app.targets = append(app.targets, scanner.Text())
|
||||
}
|
||||
default:
|
||||
} else {
|
||||
return nil, fmt.Errorf("target(s) must be supplied with -u or -f\n\nSee 'sif -h' for more information")
|
||||
}
|
||||
|
||||
@@ -169,15 +168,6 @@ func (app *App) Run() error {
|
||||
defer logger.Close()
|
||||
}
|
||||
|
||||
// target expansion - securitytrails discovers new domains before scanning
|
||||
if app.settings.SecurityTrails {
|
||||
expanded := app.expandTargets()
|
||||
if len(expanded) > 0 {
|
||||
output.Info("SecurityTrails discovered %d additional targets", len(expanded))
|
||||
app.targets = append(app.targets, expanded...)
|
||||
}
|
||||
}
|
||||
|
||||
scansRun := make([]string, 0, 16)
|
||||
|
||||
for _, url := range app.targets {
|
||||
@@ -372,14 +362,13 @@ func (app *App) Run() error {
|
||||
|
||||
// Determine which modules to run
|
||||
var toRun []modules.Module
|
||||
switch {
|
||||
case app.settings.AllModules:
|
||||
if app.settings.AllModules {
|
||||
toRun = modules.All()
|
||||
case app.settings.ModuleTags != "":
|
||||
} else if app.settings.ModuleTags != "" {
|
||||
for _, tag := range strings.Split(app.settings.ModuleTags, ",") {
|
||||
toRun = append(toRun, modules.ByTag(strings.TrimSpace(tag))...)
|
||||
}
|
||||
case app.settings.Modules != "":
|
||||
} else if app.settings.Modules != "" {
|
||||
for _, id := range strings.Split(app.settings.Modules, ",") {
|
||||
if m, ok := modules.Get(strings.TrimSpace(id)); ok {
|
||||
toRun = append(toRun, m)
|
||||
@@ -422,8 +411,7 @@ func (app *App) Run() error {
|
||||
|
||||
marshalled, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Errorf("failed to marshal result: %s", err)
|
||||
continue
|
||||
log.Fatalf("failed to marshal result: %s", err)
|
||||
}
|
||||
fmt.Println(string(marshalled))
|
||||
}
|
||||
@@ -435,38 +423,3 @@ func (app *App) Run() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// expandTargets queries SecurityTrails for each original target and returns
|
||||
// newly discovered domains (subdomains + associated) for target expansion
|
||||
func (app *App) expandTargets() []string {
|
||||
seen := make(map[string]struct{})
|
||||
for _, t := range app.targets {
|
||||
seen[t] = struct{}{}
|
||||
}
|
||||
|
||||
// snapshot original targets - don't expand discovered ones
|
||||
originals := make([]string, len(app.targets))
|
||||
copy(originals, app.targets)
|
||||
|
||||
var expanded []string
|
||||
|
||||
for _, url := range originals {
|
||||
result, err := scan.SecurityTrails(url, app.settings.Timeout, app.settings.LogDir)
|
||||
if err != nil {
|
||||
log.Errorf("SecurityTrails error for %s: %v", url, err)
|
||||
continue
|
||||
}
|
||||
if result == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, d := range result.DiscoveredURLs() {
|
||||
if _, exists := seen[d]; !exists {
|
||||
seen[d] = struct{}{}
|
||||
expanded = append(expanded, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expanded
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2025 vmfunc, xyzeva, :
|
||||
: (c) 2022-2025 vmfunc (vmfunc), xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
|
||||
Reference in New Issue
Block a user