|
| 1 | +/** Provides classes and predicates to reason about cleartext logging of sensitive data vulnerabilities. */ |
| 2 | + |
| 3 | +import swift |
| 4 | +private import codeql.swift.dataflow.DataFlow |
| 5 | +private import codeql.swift.dataflow.ExternalFlow |
| 6 | +private import codeql.swift.security.SensitiveExprs |
| 7 | + |
| 8 | +/** A data flow sink for cleartext logging of sensitive data vulnerabilities. */ |
| 9 | +abstract class CleartextLoggingSink extends DataFlow::Node { } |
| 10 | + |
| 11 | +/** A sanitizer for cleartext logging of sensitive data vulnerabilities. */ |
| 12 | +abstract class CleartextLoggingSanitizer extends DataFlow::Node { } |
| 13 | + |
| 14 | +/** |
| 15 | + * A unit class for adding additional taint steps. |
| 16 | + * |
| 17 | + * Extend this class to add additional taint steps that should apply to paths related to |
| 18 | + * cleartext logging of sensitive data vulnerabilities. |
| 19 | + */ |
| 20 | +class CleartextLoggingAdditionalTaintStep extends Unit { |
| 21 | + /** |
| 22 | + * Holds if the step from `n1` to `n2` should be considered a taint |
| 23 | + * step for flows related to cleartext logging of sensitive data vulnerabilities. |
| 24 | + */ |
| 25 | + abstract predicate step(DataFlow::Node n1, DataFlow::Node n2); |
| 26 | +} |
| 27 | + |
| 28 | +private class DefaultCleartextLoggingSink extends CleartextLoggingSink { |
| 29 | + DefaultCleartextLoggingSink() { sinkNode(this, "logging") } |
| 30 | +} |
| 31 | + |
| 32 | +/** |
| 33 | + * A sanitizer for `OSLogMessage`s configured with the appropriate privacy option. |
| 34 | + * Numeric and boolean arguments aren't redacted unless the `private` or `sensitive` options are used. |
| 35 | + * Arguments of other types are always redacted unless the `public` option is used. |
| 36 | + */ |
| 37 | +private class OsLogPrivacyCleartextLoggingSanitizer extends CleartextLoggingSanitizer { |
| 38 | + OsLogPrivacyCleartextLoggingSanitizer() { |
| 39 | + exists(CallExpr c, AutoClosureExpr e | |
| 40 | + c.getStaticTarget().getName().matches("appendInterpolation(_:%privacy:%)") and |
| 41 | + c.getArgument(0).getExpr() = e and |
| 42 | + this.asExpr() = e |
| 43 | + | |
| 44 | + e.getExpr().getType() instanceof OsLogNonRedactedType and |
| 45 | + c.getArgumentWithLabel("privacy").getExpr().(OsLogPrivacyRef).isSafe() |
| 46 | + or |
| 47 | + not e.getExpr().getType() instanceof OsLogNonRedactedType and |
| 48 | + not c.getArgumentWithLabel("privacy").getExpr().(OsLogPrivacyRef).isPublic() |
| 49 | + ) |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +/** A type that isn't redacted by default in an `OSLogMessage`. */ |
| 54 | +private class OsLogNonRedactedType extends Type { |
| 55 | + OsLogNonRedactedType() { |
| 56 | + this.getName() = [["", "U"] + "Int" + ["", "8", "16", "32", "64"], "Double", "Float", "Bool"] |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +/** A reference to a field of `OsLogPrivacy`. */ |
| 61 | +private class OsLogPrivacyRef extends MemberRefExpr { |
| 62 | + string optionName; |
| 63 | + |
| 64 | + OsLogPrivacyRef() { |
| 65 | + exists(FieldDecl f | this.getMember() = f | |
| 66 | + f.getEnclosingDecl().(NominalTypeDecl).getName() = "OSLogPrivacy" and |
| 67 | + optionName = f.getName() |
| 68 | + ) |
| 69 | + } |
| 70 | + |
| 71 | + /** Holds if this is a safe privacy option (private or sensitive). */ |
| 72 | + predicate isSafe() { optionName = ["private", "sensitive"] } |
| 73 | + |
| 74 | + /** Holds if this is a public (that is, unsafe) privacy option. */ |
| 75 | + predicate isPublic() { optionName = "public" } |
| 76 | +} |
| 77 | + |
| 78 | +private class LoggingSinks extends SinkModelCsv { |
| 79 | + override predicate row(string row) { |
| 80 | + row = |
| 81 | + [ |
| 82 | + ";;false;print(_:separator:terminator:);;;Argument[0].ArrayElement;logging", |
| 83 | + ";;false;print(_:separator:terminator:);;;Argument[1..2];logging", |
| 84 | + ";;false;print(_:separator:terminator:toStream:);;;Argument[0].ArrayElement;logging", |
| 85 | + ";;false;print(_:separator:terminator:toStream:);;;Argument[1..2];logging", |
| 86 | + ";;false;NSLog(_:_:);;;Argument[0];logging", |
| 87 | + ";;false;NSLog(_:_:);;;Argument[1].ArrayElement;logging", |
| 88 | + ";;false;NSLogv(_:_:);;;Argument[0];logging", |
| 89 | + ";;false;NSLogv(_:_:);;;Argument[1].ArrayElement;logging", |
| 90 | + ";;false;vfprintf(_:_:_:);;;Agument[1..2];logging", |
| 91 | + ";Logger;true;log(_:);;;Argument[0];logging", |
| 92 | + ";Logger;true;log(level:_:);;;Argument[1];logging", |
| 93 | + ";Logger;true;trace(_:);;;Argument[1];logging", |
| 94 | + ";Logger;true;debug(_:);;;Argument[1];logging", |
| 95 | + ";Logger;true;info(_:);;;Argument[1];logging", |
| 96 | + ";Logger;true;notice(_:);;;Argument[1];logging", |
| 97 | + ";Logger;true;warning(_:);;;Argument[1];logging", |
| 98 | + ";Logger;true;error(_:);;;Argument[1];logging", |
| 99 | + ";Logger;true;critical(_:);;;Argument[1];logging", |
| 100 | + ";Logger;true;fault(_:);;;Argument[1];logging", |
| 101 | + ] |
| 102 | + } |
| 103 | +} |
0 commit comments