| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -171,6 +171,8 @@ Python built-in support | |||
| 171 | 171 | Twisted, Web framework | |
| 172 | 172 | Flask-Admin, Web framework | |
| 173 | 173 | starlette, Asynchronous Server Gateway Interface (ASGI) | |
| 174 | + python-ldap, Lightweight Directory Access Protocol (LDAP) | ||
| 175 | + ldap3, Lightweight Directory Access Protocol (LDAP) | ||
| 174 | 176 | requests, HTTP client | |
| 175 | 177 | dill, Serialization | |
| 176 | 178 | PyYAML, Serialization | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -449,6 +449,44 @@ module RegexExecution { | |||
| 449 | 449 | } | |
| 450 | 450 | } | |
| 451 | 451 | ||
| 452 | + /** Provides classes for modeling LDAP-related APIs. */ | ||
| 453 | + module LDAP { | ||
| 454 | + /** | ||
| 455 | + * A data-flow node that executes an LDAP query. | ||
| 456 | + * | ||
| 457 | + * Extend this class to refine existing API models. If you want to model new APIs, | ||
| 458 | + * extend `LDAPQuery::Range` instead. | ||
| 459 | + */ | ||
| 460 | + class LdapExecution extends DataFlow::Node { | ||
| 461 | + LdapExecution::Range range; | ||
| 462 | + | ||
| 463 | + LdapExecution() { this = range } | ||
| 464 | + | ||
| 465 | + /** Gets the argument containing the filter string. */ | ||
| 466 | + DataFlow::Node getFilter() { result = range.getFilter() } | ||
| 467 | + | ||
| 468 | + /** Gets the argument containing the base DN. */ | ||
| 469 | + DataFlow::Node getBaseDn() { result = range.getBaseDn() } | ||
| 470 | + } | ||
| 471 | + | ||
| 472 | + /** Provides classes for modeling new LDAP query execution-related APIs. */ | ||
| 473 | + module LdapExecution { | ||
| 474 | + /** | ||
| 475 | + * A data-flow node that executes an LDAP query. | ||
| 476 | + * | ||
| 477 | + * Extend this class to model new APIs. If you want to refine existing API models, | ||
| 478 | + * extend `LDAPQuery` instead. | ||
| 479 | + */ | ||
| 480 | + abstract class Range extends DataFlow::Node { | ||
| 481 | + /** Gets the argument containing the filter string. */ | ||
| 482 | + abstract DataFlow::Node getFilter(); | ||
| 483 | + | ||
| 484 | + /** Gets the argument containing the base DN. */ | ||
| 485 | + abstract DataFlow::Node getBaseDn(); | ||
| 486 | + } | ||
| 487 | + } | ||
| 488 | + } | ||
| 489 | + | ||
| 452 | 490 | /** | |
| 453 | 491 | * A data-flow node that escapes meta-characters, which could be used to prevent | |
| 454 | 492 | * injection attacks. | |
@@ -506,8 +544,20 @@ module Escaping { | |||
| 506 | 544 | /** Gets the escape-kind for escaping a string so it can safely be included in HTML. */ | |
| 507 | 545 | string getHtmlKind() { result = "html" } | |
| 508 | 546 | ||
| 509 | - /** Gets the escape-kind for escaping a string so it can safely be included in HTML. */ | ||
| 547 | + /** Gets the escape-kind for escaping a string so it can safely be included in a regular expression. */ | ||
| 510 | 548 | string getRegexKind() { result = "regex" } | |
| 549 | + | ||
| 550 | + /** | ||
| 551 | + * Gets the escape-kind for escaping a string so it can safely be used as a | ||
| 552 | + * distinguished name (DN) in an LDAP search. | ||
| 553 | + */ | ||
| 554 | + string getLdapDnKind() { result = "ldap_dn" } | ||
| 555 | + | ||
| 556 | + /** | ||
| 557 | + * Gets the escape-kind for escaping a string so it can safely be used as a | ||
| 558 | + * filter in an LDAP search. | ||
| 559 | + */ | ||
| 560 | + string getLdapFilterKind() { result = "ldap_filter" } | ||
| 511 | 561 | // TODO: If adding an XML kind, update the modeling of the `MarkupSafe` PyPI package. | |
| 512 | 562 | // | |
| 513 | 563 | // Technically it claims to escape for both HTML and XML, but for now we don't have | |
@@ -532,6 +582,21 @@ class RegexEscaping extends Escaping { | |||
| 532 | 582 | RegexEscaping() { range.getKind() = Escaping::getRegexKind() } | |
| 533 | 583 | } | |
| 534 | 584 | ||
| 585 | + /** | ||
| 586 | + * An escape of a string so it can be safely used as a distinguished name (DN) | ||
| 587 | + * in an LDAP search. | ||
| 588 | + */ | ||
| 589 | + class LdapDnEscaping extends Escaping { | ||
| 590 | + LdapDnEscaping() { range.getKind() = Escaping::getLdapDnKind() } | ||
| 591 | + } | ||
| 592 | + | ||
| 593 | + /** | ||
| 594 | + * An escape of a string so it can be safely used as a filter in an LDAP search. | ||
| 595 | + */ | ||
| 596 | + class LdapFilterEscaping extends Escaping { | ||
| 597 | + LdapFilterEscaping() { range.getKind() = Escaping::getLdapFilterKind() } | ||
| 598 | + } | ||
| 599 | + | ||
| 535 | 600 | /** Provides classes for modeling HTTP-related APIs. */ | |
| 536 | 601 | module HTTP { | |
| 537 | 602 | /** Gets an HTTP verb, in upper case */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,8 @@ private import semmle.python.frameworks.FlaskSqlAlchemy | |||
| 22 | 22 | private import semmle.python.frameworks.Idna | |
| 23 | 23 | private import semmle.python.frameworks.Invoke | |
| 24 | 24 | private import semmle.python.frameworks.Jmespath | |
| 25 | + private import semmle.python.frameworks.Ldap | ||
| 26 | + private import semmle.python.frameworks.Ldap3 | ||
| 25 | 27 | private import semmle.python.frameworks.MarkupSafe | |
| 26 | 28 | private import semmle.python.frameworks.Multidict | |
| 27 | 29 | private import semmle.python.frameworks.Mysql | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,75 @@ | |||
| 1 | + /** | ||
| 2 | + * Provides classes modeling security-relevant aspects of the `python-ldap` PyPI package (imported as `ldap`). | ||
| 3 | + * See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html | ||
| 4 | + */ | ||
| 5 | + | ||
| 6 | + private import python | ||
| 7 | + private import semmle.python.dataflow.new.DataFlow | ||
| 8 | + private import semmle.python.Concepts | ||
| 9 | + private import semmle.python.ApiGraphs | ||
| 10 | + | ||
| 11 | + /** | ||
| 12 | + * Provides models for the `python-ldap` PyPI package (imported as `ldap`). | ||
| 13 | + * | ||
| 14 | + * See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html | ||
| 15 | + */ | ||
| 16 | + private module Ldap { | ||
| 17 | + /** | ||
| 18 | + * The execution of an `ldap` query. | ||
| 19 | + * | ||
| 20 | + * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions | ||
| 21 | + */ | ||
| 22 | + private class LdapQueryExecution extends DataFlow::CallCfgNode, LDAP::LdapExecution::Range { | ||
| 23 | + LdapQueryExecution() { | ||
| 24 | + this = | ||
| 25 | + API::moduleImport("ldap") | ||
| 26 | + .getMember("initialize") | ||
| 27 | + .getReturn() | ||
| 28 | + .getMember(["search", "search_s", "search_st", "search_ext", "search_ext_s"]) | ||
| 29 | + .getACall() | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + override DataFlow::Node getFilter() { | ||
| 33 | + result in [this.getArg(2), this.getArgByName("filterstr")] | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + override DataFlow::Node getBaseDn() { result in [this.getArg(0), this.getArgByName("base")] } | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * A call to `ldap.dn.escape_dn_chars`. | ||
| 41 | + * | ||
| 42 | + * See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17 | ||
| 43 | + */ | ||
| 44 | + private class LdapEscapeDnCall extends DataFlow::CallCfgNode, Escaping::Range { | ||
| 45 | + LdapEscapeDnCall() { | ||
| 46 | + this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("s")] } | ||
| 50 | + | ||
| 51 | + override DataFlow::Node getOutput() { result = this } | ||
| 52 | + | ||
| 53 | + override string getKind() { result = Escaping::getLdapDnKind() } | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * A call to `ldap.filter.escape_filter_chars`. | ||
| 58 | + * | ||
| 59 | + * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-filter.html#ldap.filter.escape_filter_chars | ||
| 60 | + */ | ||
| 61 | + private class LdapEscapeFilterCall extends DataFlow::CallCfgNode, Escaping::Range { | ||
| 62 | + LdapEscapeFilterCall() { | ||
| 63 | + this = | ||
| 64 | + API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + override DataFlow::Node getAnInput() { | ||
| 68 | + result in [this.getArg(0), this.getArgByName("assertion_value")] | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + override DataFlow::Node getOutput() { result = this } | ||
| 72 | + | ||
| 73 | + override string getKind() { result = Escaping::getLdapFilterKind() } | ||
| 74 | + } | ||
| 75 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,80 @@ | |||
| 1 | + /** | ||
| 2 | + * Provides classes modeling security-relevant aspects of the `ldap3` PyPI package | ||
| 3 | + * See https://pypi.org/project/ldap3/ | ||
| 4 | + */ | ||
| 5 | + | ||
| 6 | + private import python | ||
| 7 | + private import semmle.python.dataflow.new.DataFlow | ||
| 8 | + private import semmle.python.Concepts | ||
| 9 | + private import semmle.python.ApiGraphs | ||
| 10 | + | ||
| 11 | + /** | ||
| 12 | + * Provides models for the `ldap3` PyPI package | ||
| 13 | + * | ||
| 14 | + * See https://pypi.org/project/ldap3/ | ||
| 15 | + */ | ||
| 16 | + private module Ldap3 { | ||
| 17 | + /** The execution of an `ldap` query. */ | ||
| 18 | + private class LdapQueryExecution extends DataFlow::CallCfgNode, LDAP::LdapExecution::Range { | ||
| 19 | + LdapQueryExecution() { | ||
| 20 | + this = | ||
| 21 | + API::moduleImport("ldap3") | ||
| 22 | + .getMember("Connection") | ||
| 23 | + .getReturn() | ||
| 24 | + .getMember("search") | ||
| 25 | + .getACall() | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + override DataFlow::Node getFilter() { | ||
| 29 | + result in [this.getArg(1), this.getArgByName("search_filter")] | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + override DataFlow::Node getBaseDn() { | ||
| 33 | + result in [this.getArg(0), this.getArgByName("search_base")] | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * A call to `ldap3.utils.dn.escape_rdn`. | ||
| 39 | + * | ||
| 40 | + * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390 | ||
| 41 | + */ | ||
| 42 | + private class LdapEscapeDnCall extends DataFlow::CallCfgNode, Escaping::Range { | ||
| 43 | + LdapEscapeDnCall() { | ||
| 44 | + this = | ||
| 45 | + API::moduleImport("ldap3") | ||
| 46 | + .getMember("utils") | ||
| 47 | + .getMember("dn") | ||
| 48 | + .getMember("escape_rdn") | ||
| 49 | + .getACall() | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("rdn")] } | ||
| 53 | + | ||
| 54 | + override DataFlow::Node getOutput() { result = this } | ||
| 55 | + | ||
| 56 | + override string getKind() { result = Escaping::getLdapDnKind() } | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * A call to `ldap3.utils.conv.escape_filter_chars`. | ||
| 61 | + * | ||
| 62 | + * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/conv.py#L91 | ||
| 63 | + */ | ||
| 64 | + private class LdapEscapeFilterCall extends DataFlow::CallCfgNode, Escaping::Range { | ||
| 65 | + LdapEscapeFilterCall() { | ||
| 66 | + this = | ||
| 67 | + API::moduleImport("ldap3") | ||
| 68 | + .getMember("utils") | ||
| 69 | + .getMember("conv") | ||
| 70 | + .getMember("escape_filter_chars") | ||
| 71 | + .getACall() | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("text")] } | ||
| 75 | + | ||
| 76 | + override DataFlow::Node getOutput() { result = this } | ||
| 77 | + | ||
| 78 | + override string getKind() { result = Escaping::getLdapFilterKind() } | ||
| 79 | + } | ||
| 80 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,60 @@ | |||
| 1 | + /** | ||
| 2 | + * Provides taint-tracking configurations for detecting LDAP injection vulnerabilities | ||
| 3 | + * | ||
| 4 | + * Note, for performance reasons: only import this file if | ||
| 5 | + * `LdapInjection::Configuration` is needed, otherwise | ||
| 6 | + * `LdapInjectionCustomizations` should be imported instead. | ||
| 7 | + */ | ||
| 8 | + | ||
| 9 | + import python | ||
| 10 | + import semmle.python.Concepts | ||
| 11 | + import semmle.python.dataflow.new.DataFlow | ||
| 12 | + import semmle.python.dataflow.new.TaintTracking | ||
| 13 | + import semmle.python.dataflow.new.RemoteFlowSources | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * Provides aint-tracking configurations for detecting LDAP injection vulnerabilities.class | ||
| 17 | + * | ||
| 18 | + * Two configurations are provided. One is for detecting LDAP injection | ||
| 19 | + * via the distinguished name (DN). The other is for detecting LDAP injection | ||
| 20 | + * via the filter. These require different escapings. | ||
| 21 | + */ | ||
| 22 | + module LdapInjection { | ||
| 23 | + import LdapInjectionCustomizations::LdapInjection | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * A taint-tracking configuration for detecting LDAP injection vulnerabilities | ||
| 27 | + * via the distinguished name (DN) parameter of an LDAP search. | ||
| 28 | + */ | ||
| 29 | + class DnConfiguration extends TaintTracking::Configuration { | ||
| 30 | + DnConfiguration() { this = "LdapDnInjection" } | ||
| 31 | + | ||
| 32 | + override predicate isSource(DataFlow::Node source) { source instanceof Source } | ||
| 33 | + | ||
| 34 | + override predicate isSink(DataFlow::Node sink) { sink instanceof DnSink } | ||
| 35 | + | ||
| 36 | + override predicate isSanitizer(DataFlow::Node node) { node instanceof DnSanitizer } | ||
| 37 | + | ||
| 38 | + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { | ||
| 39 | + guard instanceof DnSanitizerGuard | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * A taint-tracking configuration for detecting LDAP injection vulnerabilities | ||
| 45 | + * via the filter parameter of an LDAP search. | ||
| 46 | + */ | ||
| 47 | + class FilterConfiguration extends TaintTracking::Configuration { | ||
| 48 | + FilterConfiguration() { this = "LdapFilterInjection" } | ||
| 49 | + | ||
| 50 | + override predicate isSource(DataFlow::Node source) { source instanceof Source } | ||
| 51 | + | ||
| 52 | + override predicate isSink(DataFlow::Node sink) { sink instanceof FilterSink } | ||
| 53 | + | ||
| 54 | + override predicate isSanitizer(DataFlow::Node node) { node instanceof FilterSanitizer } | ||
| 55 | + | ||
| 56 | + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { | ||
| 57 | + guard instanceof FilterSanitizerGuard | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments