mirror of
https://github.com/BishopFox/joro.git
synced 2026-11-07 17:07:23 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
376880c88d | ||
|
|
f7097f22d1 | ||
|
|
bbe68abe7c | ||
|
|
ee6083e311 |
No files matched your search
@@ -2,6 +2,8 @@ module github.com/BishopFox/joro
|
||||
|
||||
go 1.25.0
|
||||
|
||||
godebug multipathtcp=0
|
||||
|
||||
require github.com/hashicorp/go-uuid v1.0.3
|
||||
|
||||
require (
|
||||
|
||||
@@ -158,3 +158,18 @@ func (s *APIServer) handleClearRequests(w http.ResponseWriter, r *http.Request)
|
||||
s.clearDetectFindingsWithHistory()
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "cleared"})
|
||||
}
|
||||
|
||||
// handleDeleteRequestsByHost removes captured requests whose host matches a glob,
|
||||
// the "clear what is already captured" companion to adding a noise pattern. The
|
||||
// required "pattern" query param is the host glob (filepath.Match), the same value
|
||||
// stored as the noise pattern. Removing the requests also drops them from the site
|
||||
// map, which is derived from the same store.
|
||||
func (s *APIServer) handleDeleteRequestsByHost(w http.ResponseWriter, r *http.Request) {
|
||||
pattern := r.URL.Query().Get("pattern")
|
||||
if pattern == "" {
|
||||
writeError(w, http.StatusBadRequest, "pattern is required")
|
||||
return
|
||||
}
|
||||
removed := s.store.DeleteByHostPattern(pattern)
|
||||
writeJSON(w, http.StatusOK, map[string]int{"deleted": removed})
|
||||
}
|
||||
@@ -90,6 +90,7 @@ func registerRoutes(s *APIServer, mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /api/v1/requests/{id}", s.handleGetRequest)
|
||||
mux.HandleFunc("GET /api/v1/requests/{id}/params", s.handleGetRequestParams)
|
||||
mux.HandleFunc("DELETE /api/v1/requests", s.handleClearRequests)
|
||||
mux.HandleFunc("DELETE /api/v1/requests/by-host", s.handleDeleteRequestsByHost)
|
||||
|
||||
// Intercept
|
||||
mux.HandleFunc("GET /api/v1/intercept", s.handleGetInterceptQueue)
|
||||
|
||||
@@ -3,6 +3,7 @@ package proxy
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -666,6 +667,37 @@ func (s *Store) DeleteSitemapNode(origin, path string, matchPath bool) int {
|
||||
return removed
|
||||
}
|
||||
|
||||
// DeleteByHostPattern removes every captured request whose host matches the given
|
||||
// host glob, using the same filepath.Match predicate (case-insensitive) that the
|
||||
// NoiseFilter applies in IsNoisy and Scope applies in matchRule. It is the "clear
|
||||
// what is already captured" companion to adding a noise pattern, so the rows removed
|
||||
// are exactly the ones the pattern suppresses going forward. Host is derived like
|
||||
// Sitemap/DeleteSitemapNode (u.Host); requests whose URL cannot be parsed are kept.
|
||||
// Returns the number of requests removed.
|
||||
func (s *Store) DeleteByHostPattern(hostPattern string) int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
pat := strings.ToLower(hostPattern)
|
||||
kept := s.items[:0]
|
||||
removed := 0
|
||||
for _, r := range s.items {
|
||||
u, err := url.Parse(r.URL)
|
||||
if err != nil {
|
||||
kept = append(kept, r)
|
||||
continue
|
||||
}
|
||||
matched, merr := filepath.Match(pat, strings.ToLower(u.Host))
|
||||
if merr != nil || !matched {
|
||||
kept = append(kept, r)
|
||||
continue
|
||||
}
|
||||
removed++
|
||||
}
|
||||
s.items = kept
|
||||
return removed
|
||||
}
|
||||
|
||||
// Clear removes all stored requests.
|
||||
func (s *Store) Clear() {
|
||||
s.mu.Lock()
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
"github.com/BishopFox/joro/internal/xsshunter"
|
||||
)
|
||||
|
||||
var version = "v1.18.1"
|
||||
var version = "v1.18.3"
|
||||
var commit = "dev" // injected via -ldflags at build time
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1191,6 +1191,10 @@ export const api = {
|
||||
if (path !== undefined) qs.set('path', path)
|
||||
return req<{ deleted: number }>('DELETE', `/sitemap?${qs.toString()}`)
|
||||
},
|
||||
// Delete captured requests whose host matches a glob (the host-level companion to
|
||||
// adding a noise pattern). pattern is the host glob stored as the noise pattern.
|
||||
deleteRequestsByHost: (pattern: string) =>
|
||||
req<{ deleted: number }>('DELETE', `/requests/by-host?${new URLSearchParams({ pattern }).toString()}`),
|
||||
// Every parameter the node's requests carried. The node is 'origin', not 'host'
|
||||
// as deleteSitemapNode names it: this endpoint also takes the tree's filters,
|
||||
// where 'host' means a substring of the captured Host header. Filters must match
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { MenuItem } from '../components/ContextMenu'
|
||||
import { api } from './api'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
import { useRequestStore } from '../stores/requestStore'
|
||||
|
||||
// Host-level context-menu actions shared by the History table rows and the Site Map host
|
||||
// menu. Modeled on getSelectionMenuItems. Scope and noise both match a host glob with
|
||||
@@ -27,11 +28,18 @@ async function addScope(pattern: string, path: string, label: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// addNoise adds a noise pattern and then purges the already-captured requests it matches:
|
||||
// noise means traffic to never see, so leaving the old rows behind would be the surprise.
|
||||
// invalidate() reloads History and, via the total change, the Site Map (both derive from the
|
||||
// same store). The deletion predicate is the server's, matching the pattern just stored.
|
||||
async function addNoise(pattern: string, label: string) {
|
||||
try {
|
||||
const { enabled } = await api.getNoise()
|
||||
await api.addNoisePattern(pattern)
|
||||
toast(enabled ? `Added ${label} to noise filter` : `Added ${label} to noise filter — noise filtering is off`)
|
||||
const { deleted } = await api.deleteRequestsByHost(pattern)
|
||||
if (deleted > 0) useRequestStore.getState().invalidate()
|
||||
const cleared = deleted > 0 ? ` — cleared ${deleted} request${deleted === 1 ? '' : 's'}` : ''
|
||||
toast(enabled ? `Added ${label} to noise filter${cleared}` : `Added ${label} to noise filter — noise filtering is off${cleared}`)
|
||||
} catch {
|
||||
toast('Failed to add noise pattern', 'error')
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user