srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ipc')
-rw-r--r--internal/ipc/ipc.go46
-rw-r--r--internal/ipc/server.go25
2 files changed, 70 insertions, 1 deletions
diff --git a/internal/ipc/ipc.go b/internal/ipc/ipc.go
index dc7a1df..f5d5452 100644
--- a/internal/ipc/ipc.go
+++ b/internal/ipc/ipc.go
@@ -77,6 +77,18 @@ type Request struct {
ClientCert *clientcert.Cert `json:"client_cert,omitempty"`
ClientCertID int64 `json:"client_cert_id,omitempty"`
+ // For "tag_entry": ID identifies the history entry (same field
+ // "get"/"set_flagged"/"delete_entry" use). Plugin names who's
+ // tagging it - informational only, not an identity or auth
+ // mechanism, since anything that can reach the socket can claim any
+ // name. Tag is the short marker itself (e.g. "jwt", "authz-bypass").
+ // Data is an opaque, plugin-defined JSON blob a panel view renders
+ // later without needing this plugin still connected - empty is
+ // fine for a plugin that only needs the tag itself, no extra detail.
+ TagPlugin string `json:"tag_plugin,omitempty"`
+ Tag string `json:"tag,omitempty"`
+ TagData string `json:"tag_data,omitempty"`
+
// For "set_flagged" and "delete_entry": ID identifies the history
// entry. "clear_history" needs no fields at all.
Flagged bool `json:"flagged,omitempty"`
@@ -121,6 +133,9 @@ type Response struct {
// per-entry insert failure is skipped, not fatal to the batch).
Imported int `json:"imported,omitempty"`
+ // For "tag_entry": the new tag row's assigned ID.
+ TagID int64 `json:"tag_id,omitempty"`
+
// For "intrude_result": one completed attack request.
IntrudeResult *IntrudeResultMsg `json:"intrude_result,omitempty"`
@@ -164,6 +179,12 @@ type EntryDetail struct {
// a false *Exact.
RequestTruncated bool `json:"request_truncated,omitempty"`
ResponseTruncated bool `json:"response_truncated,omitempty"`
+ // Tags is every plugin-contributed marker on this entry - see the
+ // "tag_entry" request. Summary.Tags (from List/Search) is just the
+ // comma-joined names for a compact list-view badge; this is the
+ // full record, including each tag's plugin and opaque Data blob, for
+ // a panel view to render.
+ Tags []store.EntryTag `json:"tags,omitempty"`
}
// Client talks to a mitmuxd instance for request/response queries
@@ -199,6 +220,31 @@ func (c *Client) Close() error {
// SetFlagged sets the flagged marker on a history entry - a simple
// "mark this, revisit later" bit, filterable via flagged:true/false in
// Search.
+// TagEntry marks history entry id with tag, attributed to plugin (any
+// non-empty name a plugin chooses to identify itself by - informational
+// only), with an optional opaque data blob a panel view can render
+// later. This is the core plugin-integration primitive: any process
+// that can reach the control socket - the reference Go client here, or
+// a plugin in any other language following the same JSON wire protocol
+// (see PLAN.md) - can tag entries it finds interesting without mitmux
+// needing to know anything about it in advance. Returns the new tag's
+// assigned ID.
+func (c *Client) TagEntry(entryID int64, plugin, tag, data string) (int64, error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if err := c.enc.Encode(Request{Type: "tag_entry", ID: entryID, TagPlugin: plugin, Tag: tag, TagData: data}); err != nil {
+ return 0, err
+ }
+ var resp Response
+ if err := c.dec.Decode(&resp); err != nil {
+ return 0, err
+ }
+ if resp.Type == "error" {
+ return 0, errors.New(resp.Error)
+ }
+ return resp.TagID, nil
+}
+
func (c *Client) SetFlagged(id int64, flagged bool) error {
c.mu.Lock()
defer c.mu.Unlock()
diff --git a/internal/ipc/server.go b/internal/ipc/server.go
index 378c1a9..98c2c22 100644
--- a/internal/ipc/server.go
+++ b/internal/ipc/server.go
@@ -141,7 +141,13 @@ func (s *Server) handleConn(conn net.Conn) {
enc.Encode(Response{Type: "error", Error: err.Error()})
continue
}
- enc.Encode(Response{Type: "get", Detail: detailFromEntry(e)})
+ detail := detailFromEntry(e)
+ if tags, err := s.db.ListEntryTags(req.ID); err != nil {
+ log.Printf("list entry tags for #%d: %v", req.ID, err)
+ } else {
+ detail.Tags = tags
+ }
+ enc.Encode(Response{Type: "get", Detail: detail})
case "ws_messages":
msgs, err := s.db.ListWSMessages(req.ID)
@@ -220,6 +226,23 @@ func (s *Server) handleConn(conn net.Conn) {
}
enc.Encode(Response{Type: "intrude_done"})
+ case "tag_entry":
+ if req.Tag == "" {
+ enc.Encode(Response{Type: "error", Error: "tag_entry: missing tag"})
+ continue
+ }
+ id, err := s.db.AddEntryTag(store.EntryTag{
+ EntryID: req.ID,
+ Plugin: req.TagPlugin,
+ Tag: req.Tag,
+ Data: req.TagData,
+ })
+ if err != nil {
+ enc.Encode(Response{Type: "error", Error: err.Error()})
+ continue
+ }
+ enc.Encode(Response{Type: "tag_entry", TagID: id})
+
case "set_flagged":
if err := s.db.SetFlagged(req.ID, req.Flagged); err != nil {
enc.Encode(Response{Type: "error", Error: err.Error()})