circle-ir 3.182.0 → 3.186.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10655,6 +10655,49 @@ var DEFAULT_SOURCES = [
10655
10655
  // Server-side interceptor receives `Metadata headers`; `headers.get(KEY)`
10656
10656
  // returns caller-supplied header values.
10657
10657
  { method: "get", class: "Metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["java"] },
10658
+ // --- WebSocket transport channels (cognium-dev #213 second slice) ---
10659
+ //
10660
+ // Server-side WebSocket handlers receive attacker-authored frames on
10661
+ // every call to a receive-shaped method. Untrusted the moment they
10662
+ // return, regardless of any application-level auth on the socket.
10663
+ //
10664
+ // Python — FastAPI / Starlette (`from fastapi import WebSocket`):
10665
+ // data = await websocket.receive_text()
10666
+ // data = await websocket.receive_bytes()
10667
+ // data = await websocket.receive_json() # parsed dict/list
10668
+ // data = await websocket.receive() # {'type', 'text'|'bytes'}
10669
+ { method: "receive_text", class: "WebSocket", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
10670
+ { method: "receive_bytes", class: "WebSocket", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
10671
+ { method: "receive_json", class: "WebSocket", type: "http_body", severity: "high", return_tainted: true, languages: ["python"] },
10672
+ //
10673
+ // `receive` intentionally class-scoped to WebSocket (Starlette pattern).
10674
+ // Broadening to unqualified would collide with queue/signal `.receive()`.
10675
+ { method: "receive", class: "WebSocket", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
10676
+ // Django Channels `AsyncJsonWebsocketConsumer` / `WebsocketConsumer`
10677
+ // expose the same receive_* names on `self`; the class filter matches
10678
+ // `WebSocket` only, so add unqualified fallbacks for the receive_json /
10679
+ // receive_text convention (broad — no class filter possible without
10680
+ // hardcoding Django's consumer names).
10681
+ { method: "receive_json", type: "http_body", severity: "high", return_tainted: true, languages: ["python"] },
10682
+ { method: "receive_text", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
10683
+ { method: "receive_bytes", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
10684
+ // Go — gorilla/websocket (`github.com/gorilla/websocket`):
10685
+ // messageType, message, err := conn.ReadMessage()
10686
+ // _, r, err := conn.NextReader()
10687
+ { method: "ReadMessage", class: "Conn", type: "network_input", severity: "high", return_tainted: true, languages: ["go"] },
10688
+ { method: "NextReader", class: "Conn", type: "network_input", severity: "high", return_tainted: true, languages: ["go"] },
10689
+ // nhooyr.io/websocket exposes a `Read` method on `*websocket.Conn`;
10690
+ // signature is `func (c *Conn) Read(ctx) (MessageType, []byte, error)`.
10691
+ { method: "Read", class: "Conn", type: "network_input", severity: "high", return_tainted: true, languages: ["go"] },
10692
+ // Java — Jakarta / Java WebSocket API (`javax.websocket` / `jakarta.websocket`):
10693
+ // session.getBasicRemote().sendText(input); // OUT (not a source)
10694
+ // @OnMessage public void onMessage(String msg) — `msg` is a callback
10695
+ // param, not a return; handled via param_tainted when annotation is set.
10696
+ { annotation: "OnMessage", type: "network_input", severity: "high", param_tainted: true, languages: ["java"] },
10697
+ // Spring `@MessageMapping` STOMP-over-WebSocket handlers receive
10698
+ // untrusted payloads on every dispatched frame.
10699
+ { annotation: "MessageMapping", type: "http_body", severity: "high", param_tainted: true, languages: ["java"] },
10700
+ { annotation: "SubscribeMapping", type: "http_body", severity: "high", param_tainted: true, languages: ["java"] },
10658
10701
  // --- Cache reads (second-order taint — Redis / Memcached / Django cache) ---
10659
10702
  // The cache round-trip is a canonical second-order sink: whatever was
10660
10703
  // written previously (potentially attacker-controlled) resurfaces on read.
@@ -12683,6 +12726,91 @@ var DEFAULT_SANITIZERS = [
12683
12726
  // Command injection - shell escaping
12684
12727
  { method: "quote", class: "shell", removes: ["command_injection"] },
12685
12728
  { method: "escape", class: "shell-escape", removes: ["command_injection"] },
12729
+ // JS/Node — additional XSS/HTML encoders (cognium-dev #213 sixth slice).
12730
+ //
12731
+ // he.encode(x) / he.escape(x) — `he` npm package
12732
+ // sanitizeHtml(x) — `sanitize-html` npm
12733
+ // xss(x) / filterXSS(x) — `xss` npm (Yahoo)
12734
+ // escapeHtml(x) / escapeHTML(x) — common bare helpers
12735
+ // entities.encode(x) / entities.escape(x) — `entities` npm
12736
+ // xssFilters.inHTMLData(x) / inHTMLComment(x) / uriInHTMLData(x)
12737
+ // — `xss-filters` npm (Yahoo)
12738
+ //
12739
+ // `external_taint_escape` is included alongside `xss` — the CWE-668
12740
+ // fallback fires on any call receiving tainted data that the engine
12741
+ // does not otherwise recognize. Since these functions are explicitly
12742
+ // registered as sanitizers (they PURIFY input), they should also
12743
+ // suppress the fallback so a `res.send(sanitizeHtml(x))` flow does
12744
+ // not report an external-taint-escape at the sanitizer call itself.
12745
+ { method: "encode", class: "he", removes: ["xss", "external_taint_escape"] },
12746
+ { method: "escape", class: "he", removes: ["xss", "external_taint_escape"] },
12747
+ { method: "sanitizeHtml", removes: ["xss", "external_taint_escape"] },
12748
+ { method: "xss", removes: ["xss", "external_taint_escape"] },
12749
+ { method: "filterXSS", removes: ["xss", "external_taint_escape"] },
12750
+ { method: "escapeHtml", removes: ["xss", "external_taint_escape"] },
12751
+ { method: "escapeHTML", removes: ["xss", "external_taint_escape"] },
12752
+ { method: "encode", class: "entities", removes: ["xss", "external_taint_escape"] },
12753
+ { method: "escape", class: "entities", removes: ["xss", "external_taint_escape"] },
12754
+ // xss-filters: context-specific helpers, all XSS-safe by construction.
12755
+ { method: "inHTMLData", class: "xssFilters", removes: ["xss", "external_taint_escape"] },
12756
+ { method: "inHTMLComment", class: "xssFilters", removes: ["xss", "external_taint_escape"] },
12757
+ { method: "uriInHTMLData", class: "xssFilters", removes: ["xss", "external_taint_escape"] },
12758
+ // JS/Node — SQL escaping (in addition to the mysql class above).
12759
+ //
12760
+ // SqlString.escape / .escapeId — `sqlstring` npm (raw
12761
+ // escape used by mysql /
12762
+ // node-mysql2 drivers)
12763
+ // pgFormat(sql, ...args) / format(bare) — `pg-format` npm
12764
+ // SQL.raw / SQL(bare) — `sql-template-strings`
12765
+ //
12766
+ // Bare `format` is intentionally NOT registered — it collides with
12767
+ // string.format-style helpers unrelated to SQL. Callers should use the
12768
+ // qualified form.
12769
+ { method: "escape", class: "SqlString", removes: ["sql_injection", "external_taint_escape"] },
12770
+ { method: "escapeId", class: "SqlString", removes: ["sql_injection", "external_taint_escape"] },
12771
+ { method: "format", class: "SqlString", removes: ["sql_injection", "external_taint_escape"] },
12772
+ { method: "format", class: "pgFormat", removes: ["sql_injection", "external_taint_escape"] },
12773
+ { method: "ident", class: "pgFormat", removes: ["sql_injection", "external_taint_escape"] },
12774
+ { method: "literal", class: "pgFormat", removes: ["sql_injection", "external_taint_escape"] },
12775
+ // JS/Node — crypto.randomUUID() and Node's crypto.randomBytes(...)toString()
12776
+ // produce cryptographically-secure typed strings that cannot carry
12777
+ // attacker-injected payload. Registered as type coercion.
12778
+ { method: "randomUUID", class: "crypto", removes: ["sql_injection", "nosql_injection", "command_injection", "path_traversal", "code_injection", "xss"] },
12779
+ // Bare `randomUUID()` alias — `import { randomUUID } from 'crypto'`.
12780
+ { method: "randomUUID", removes: ["sql_injection", "nosql_injection", "command_injection", "path_traversal", "code_injection", "xss"] },
12781
+ // JS/Node — URL construction. `new URL(x, base)` (constructor-call
12782
+ // matched by method_name === 'URL') and `URLSearchParams.toString()`
12783
+ // safely encode components for URL context.
12784
+ { method: "toString", class: "URLSearchParams", removes: ["ssrf", "open_redirect", "xss"] },
12785
+ // Go — additional sanitizers (cognium-dev #213 sixth slice).
12786
+ //
12787
+ // regexp.QuoteMeta(x) — mirror of Python re.escape; strips regex
12788
+ // metacharacters so downstream compile/match
12789
+ // cannot execute attacker-crafted patterns.
12790
+ // bluemonday.Sanitize — dominant Go HTML sanitizer library
12791
+ // (`microcosm-cc/bluemonday`). Called on a
12792
+ // Policy value returned by UGCPolicy() /
12793
+ // StrictPolicy() / etc. Match by method name
12794
+ // alone — Policy is the receiver type.
12795
+ // net.ParseIP(x) — validates the input is a well-formed IP;
12796
+ // returns nil for bad input (developer must
12797
+ // check, but the sanitizer only applies when
12798
+ // the parsed IP flows onward).
12799
+ // sql.Named(name, val) — parameterized query binding.
12800
+ { method: "QuoteMeta", class: "regexp", removes: ["redos", "code_injection", "external_taint_escape"] },
12801
+ { method: "Sanitize", class: "bluemonday", removes: ["xss", "external_taint_escape"] },
12802
+ { method: "Sanitize", class: "Policy", removes: ["xss", "external_taint_escape"] },
12803
+ // Bare `Sanitize` — the receiver is a Policy variable (`p := bluemonday.UGCPolicy(); p.Sanitize(x)`).
12804
+ // `Policy` class-matching only fires when the IR resolves the receiver type;
12805
+ // since Go DFG rarely propagates the type of a locally-assigned variable
12806
+ // from a package factory call, we also register the bare method name.
12807
+ // Narrow FP risk: `Sanitize` bare is uncommon outside bluemonday context.
12808
+ { method: "Sanitize", removes: ["xss", "external_taint_escape"] },
12809
+ { method: "SanitizeBytes", class: "bluemonday", removes: ["xss", "external_taint_escape"] },
12810
+ { method: "SanitizeBytes", class: "Policy", removes: ["xss", "external_taint_escape"] },
12811
+ { method: "SanitizeBytes", removes: ["xss", "external_taint_escape"] },
12812
+ { method: "ParseIP", class: "net", removes: ["ssrf", "command_injection", "path_traversal", "external_taint_escape"] },
12813
+ { method: "Named", class: "sql", removes: ["sql_injection", "external_taint_escape"] },
12686
12814
  // =========================================================================
12687
12815
  // Python Sanitizers
12688
12816
  // =========================================================================
@@ -12727,6 +12855,60 @@ var DEFAULT_SANITIZERS = [
12727
12855
  // Python Type coercion
12728
12856
  { method: "int", removes: ["sql_injection", "command_injection", "xss"] },
12729
12857
  { method: "float", removes: ["sql_injection", "command_injection"] },
12858
+ // Python URL encoding (cognium-dev #213 fifth slice).
12859
+ //
12860
+ // urllib.parse.quote(x) — RFC-3986 percent-encode; safe for URL path
12861
+ // urllib.parse.quote_plus(x) — same + space→+; safe for query string
12862
+ // urllib.parse.urlencode(d) — encode a dict of pairs
12863
+ //
12864
+ // Bounded to URL-context sinks. Class-scoped to the specific
12865
+ // `urllib.parse` receiver to avoid colliding with unrelated bare
12866
+ // `quote(...)` calls in other libraries.
12867
+ { method: "quote", class: "urllib.parse", removes: ["ssrf", "open_redirect", "xss", "path_traversal"] },
12868
+ { method: "quote_plus", class: "urllib.parse", removes: ["ssrf", "open_redirect", "xss", "path_traversal"] },
12869
+ { method: "urlencode", class: "urllib.parse", removes: ["ssrf", "open_redirect", "xss"] },
12870
+ // Bare aliases — `from urllib.parse import quote` then unqualified.
12871
+ { method: "quote_plus", removes: ["ssrf", "open_redirect", "xss", "path_traversal"] },
12872
+ { method: "urlencode", removes: ["ssrf", "open_redirect", "xss"] },
12873
+ // `quote` bare is intentionally NOT registered — it collides with
12874
+ // shlex.quote (bare-imported) which is a command_injection sanitizer,
12875
+ // not a URL sanitizer. The class-scoped variant above catches the
12876
+ // qualified `urllib.parse.quote(...)` shape; unqualified callers who
12877
+ // want URL-context credit should use `quote_plus` (which does not
12878
+ // collide with any command_injection sanitizer).
12879
+ // Python XSS — additional common sanitizers.
12880
+ //
12881
+ // bleach.clean(...) — already covered above (line 2898)
12882
+ // bleach.linkify(...) — turns URLs into anchor tags; sanitizes as well
12883
+ // django.utils.html.escape / strip_tags — Django's XSS escape helpers
12884
+ // jinja2.escape — Jinja2's Markup escape (aliased from markupsafe)
12885
+ // flask.escape — Flask re-export of markupsafe.escape
12886
+ // saxutils.escape — stdlib xml.sax.saxutils.escape for XML docs
12887
+ { method: "linkify", class: "bleach", removes: ["xss"] },
12888
+ // Bare `linkify(...)` alias — `from bleach import linkify`.
12889
+ { method: "linkify", removes: ["xss"] },
12890
+ { method: "escape", class: "django.utils.html", removes: ["xss"] },
12891
+ { method: "strip_tags", class: "django.utils.html", removes: ["xss"] },
12892
+ { method: "escape", class: "jinja2", removes: ["xss"] },
12893
+ { method: "escape", class: "flask", removes: ["xss"] },
12894
+ { method: "escape", class: "saxutils", removes: ["xss"] },
12895
+ { method: "escape", class: "xml.sax.saxutils", removes: ["xss"] },
12896
+ { method: "quoteattr", class: "saxutils", removes: ["xss"] },
12897
+ { method: "quoteattr", class: "xml.sax.saxutils", removes: ["xss"] },
12898
+ // Python ReDoS — `re.escape(user)` when building a regex from user input
12899
+ // strips regex metacharacters. Downstream `re.compile / re.match` cannot
12900
+ // interpret user-supplied alternations or quantifiers. Also covers the
12901
+ // `code_injection` categorization that `re.compile` currently emits
12902
+ // (a re.escape-wrapped pattern cannot execute anything, so both are safe).
12903
+ { method: "escape", class: "re", removes: ["redos", "code_injection"] },
12904
+ // Python SQLAlchemy — `text(...).bindparams(...)` binds params safely.
12905
+ // The `bindparams` call is the sanitizer; the parent `text` wraps the
12906
+ // template. Also add `expression.literal` for explicit SQL literals.
12907
+ { method: "bindparams", removes: ["sql_injection"] },
12908
+ // psycopg2 sql-composition helpers
12909
+ { method: "Identifier", class: "sql", removes: ["sql_injection"] },
12910
+ { method: "Literal", class: "sql", removes: ["sql_injection"] },
12911
+ { method: "Placeholder", class: "sql", removes: ["sql_injection"] },
12730
12912
  // =========================================================================
12731
12913
  // Rust Sanitizers
12732
12914
  // =========================================================================
@@ -14555,7 +14737,14 @@ function matchesSanitizerPattern(call, pattern) {
14555
14737
  return false;
14556
14738
  }
14557
14739
  if (pattern.class) {
14558
- if (!call.receiver || !receiverMightBeClass(call.receiver, pattern.class)) {
14740
+ if (!call.receiver) {
14741
+ const target = call.resolution?.target;
14742
+ const expectedTail = `${pattern.class}.${pattern.method}`;
14743
+ if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {
14744
+ } else {
14745
+ return false;
14746
+ }
14747
+ } else if (!receiverMightBeClass(call.receiver, pattern.class)) {
14559
14748
  return false;
14560
14749
  }
14561
14750
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.182.0",
3
+ "version": "3.186.0",
4
4
  "description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",